How to update QtGui.QSlider's position while video goes on? - pyqt4

I am currently using PyQt4 to develop a video player GUI, and I use QtGui.QSlider to control the progress of video stream. And I am wondering how can I change the slider's value when video is playing?
import sys, os
import xml.etree.ElementTree as ET
from xml.dom import minidom
from PyQt4 import QtCore, QtGui, uic
from PyQt4.phonon import Phonon
class vidplayer(QtGui.QWidget):
def __init__(self,url,xml_url,parent = None):
self.url = url
super(vidplayer,self).__init__()
self.initUI()
self.getElm_from_XML(xml_url);
def printXMLInfo(self):
itemlist = self.doc.getElementsByTagName('object')
for item in itemlist:
print ("frame-span:"+str(item.attributes['framespan'].value)+
" Event tag: "+ str(item.attributes['name'].value));
print(len(itemlist))
def getElm_from_XML(self,xml_url):
self.doc = minidom.parse(xml_url)
self.clipList = self.doc.getElementsByTagName('object');
print("Reading XML done...\n Have read %s elements.\n" %(len(self.clipList)))
def initUI(self):
## create widgets
# phonon video player and media
self.vp = Phonon.VideoPlayer()
media = Phonon.MediaSource(self.url)
# layout components (boxes)
self.vbox_play = QtGui.QVBoxLayout()
self.hbox_ctrl_vid = QtGui.QHBoxLayout()
self.hbox_ctrl_dec = QtGui.QHBoxLayout()
self.hbox = QtGui.QHBoxLayout()
self.vbox_control = QtGui.QVBoxLayout()
# bottons to control
self.btn_go_prev = QtGui.QPushButton("|<")
self.btn_go_next = QtGui.QPushButton(">|")
self.btn_play = QtGui.QPushButton("Play(Pause)")
self.btn_accept = QtGui.QPushButton("Accept")
self.btn_reject = QtGui.QPushButton("Reject")
# slider to interact with videoplayer
self.sld = QtGui.QSlider(QtCore.Qt.Horizontal,self)
#self.sld = Phonon.SeekSlider(self)
## layout components setup
self.vbox_control.addStretch(1)
self.hbox_ctrl_vid.addStretch(1)
self.hbox_ctrl_dec.addStretch(1)
self.vbox_play.addStretch(1)
self.hbox.addStretch(1)
self.vbox_control.setDirection(QtGui.QBoxLayout.BottomToTop)
self.vbox_play.setDirection(QtGui.QBoxLayout.BottomToTop)
## widgets inits
self.vp.load(media)
self.vp.play()
self.sld.setFocusPolicy(QtCore.Qt.NoFocus)
self.sld.setRange(1,1000)
## widgets assignment
self.hbox_ctrl_vid.addWidget(self.btn_go_prev)
self.hbox_ctrl_vid.addWidget(self.btn_play)
self.hbox_ctrl_vid.addWidget(self.btn_go_next)
self.hbox_ctrl_dec.addWidget(self.btn_accept)
self.hbox_ctrl_dec.addWidget(self.btn_reject)
self.vbox_play.addWidget(self.vp)
self.vbox_play.addWidget(self.sld)
self.vbox_control.addLayout(self.hbox_ctrl_dec)
self.vbox_control.addLayout(self.hbox_ctrl_vid)
self.hbox.addLayout(self.vbox_play)
self.hbox.addLayout(self.vbox_control)
## main setup and display
self.setLayout(self.hbox)
self.setGeometry(300,300,600,400)
self.setWindowTitle('CCNY_SRI TRECVid iSED UI')
self.setWindowIcon(QtGui.QIcon('./icon.png'))
self.show()
## connection set up
self.sld.valueChanged[int].connect(self.sld_changeValue)
self.vp.finished.connect(self.onReachingFinish)
self.btn_play.clicked.connect(self.onClicked_play)
self.btn_go_next.clicked.connect(self.onClicked_nextClip)
self.btn_go_prev.clicked.connect(self.onClicked_prevClip)
###################### callable functions ##################
def sld_changeValue(self,value):
totalT = self.vp.totalTime()
print totalT
newT = totalT*value/1000
self.vp.seek(newT)
def onClicked_play(self):
# BUG: sth wrong with boundary
if self.vp.isPaused():
self.vp.play()
print("resume play")
elif self.vp.isPlaying():
self.vp.pause()
print("pause at",self.sld.value())
elif self.sld.value()<1000:
self.vp.play()
def onClicked_nextClip(self):
print("go next")
def onClicked_prevClip(self):
print("go prev")
def onClicked_acc(self):
print("accepted")
def onClicked_rej(self):
print("rejected")
def onReachingFinish(self):
self.vp.pause()
self.vp.stop()
def main():
app = QtGui.QApplication(sys.argv)
window = vidplayer(sys.argv[1],sys.argv[2])
sys.exit(app.exec_())
if __name__ == '__main__':
main()

There is a dedicated class for that in Phonon: Phonon.SeekSlider:
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
# creating player resource
self.player = Player()
# adding controls
self.sliders = Sliders(self.player)
class Sliders(QtGui.QWidget):
def __init__(self, player):
QtGui.QWidget.__init__(self)
self.seek_slider = Phonon.SeekSlider(player , self)
class Player(Phonon.MediaObject):
def __init__(self):
Phonon.MediaObject.__init__(self)
self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
self.path = Phonon.createPath(self, self.audioOutput)
This slider will directly pilot the play and its position will be updated during media playing. But, as that's a drawback I'm looking for a workaround, there is no valueChanged signal :-/

Related

How to make a layout with only aboslute positioning in pyqt

I want to make a layout where the user is able to move the widget inside freely with no constraints of rows or column. So I'm using absolute positioning. I'm able to make it happen in a window with no layout but I don't know what to use as a layout , problem is in the commented line in the code.
from PyQt5.QtWidgets import QWidget, QLabel, QApplication,QVBoxLayout,QHBoxLayout
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QDrag
class DragLabel(QLabel):
def mouseMoveEvent(self, e):
if e.buttons() == Qt.LeftButton:
drag = QDrag(self)
mime = QMimeData()
drag.setMimeData(mime)
drag.exec_(Qt.MoveAction)
class Window(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.label = DragLabel('first', self)
self.label2 = DragLabel('second', self)
self.label3 = DragLabel('third', self)
self.label.move(50, 50)
self.label2.move(100, 100)
self.label3.move(150, 150)
# self.main_frame=QHBoxLayout()
# self.classic_frame=QVBoxLayout()
#what should I use here to make a frame working with absolute position only
# self.absolute_frame = ???
# self.absolute_frame.addWidget(self.label)
# self.absolute_frame.addWidget(self.label2)
# self.absolute_frame.addWidget(self.label3)
# self.main_frame.addLayout(self.classic_frame)
# self.main_frame.addLayout(self.absolute_frame)
# self.setLayout(self.main_frame)
def dragEnterEvent(self, e):
widget = e.source()
if isinstance(widget,DragLabel):
e.accept()
def dropEvent(self, e):
pos = e.pos()
widget_dep = e.source()
if isinstance(widget_dep,DragLabel):
widget_dep_name = widget_dep.text()
print(widget_dep_name)
widget_dep.move(pos.x(),pos.y())
if __name__ == '__main__':
app = QApplication([])
w = Window()
w.show()
app.exec_()

PyQt5 add second function to thread but not work

Previously I have tried to use Flask for doing the followings simultaneously:
Display live video streaming
Display real-time data streaming
Control the robot car
As the above is just for demonstration, with the video streaming performance not good enough, I decided to change the whole application to PyQt5 for further development and production. Now I can create the GUI for displaying live video streaming well, while the real-time data streaming cannot be done well. The error is
QObject::startTimer: Timers can only be used with threads started with QThread
The following is the whole program. Please help to see what's wrong in the adding thread issue. Thanks!
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
import cv2
from vidgear.gears import CamGear
from random import random
data_list=[]
fps=60
options_cam={"CAP_PROP_FRAME_WIDTH":640,"CAP_PROP_FRAME_HEIGHT":480,"CAP_PROP_FPS":fps}
stream=CamGear(source=0,logging=False,**options_cam).start()
class MainWindow(QtWidgets.QWidget):
def __init__(self,*args):
super(MainWindow, self).__init__()
self.setWindowTitle('Vehicle control')
self.grid_layout=QtWidgets.QGridLayout()
self.video_label = QtWidgets.QLabel('Video streaming',self)
self.video_frame = QtWidgets.QLabel(self)
self.grid_layout.addWidget(self.video_label,0,0)
self.grid_layout.addWidget(self.video_frame,1,0)
self.data_label = QtWidgets.QLabel('Data streaming',self)
self.data_frame = QtWidgets.QListWidget(self)
self.grid_layout.addWidget(self.data_label,0,1)
self.grid_layout.addWidget(self.data_frame,1,1)
self.setLayout(self.grid_layout)
#self.thread=QtCore.QThread()
#self.thread.started.connect(self.nextFrameSlot)
#self.thread.start()
self.timer=QtCore.QTimer()
self.timer.timeout.connect(self.video_stream)
self.timer.start(0)
self.thread=QtCore.QThread()
self.thread.start()
self.timer2=QtCore.QTimer()
self.timer2.moveToThread(self.thread)
self.timer2.timeout.connect(self.data_stream)
self.timer2.start(0)
def video_stream(self):
frame = stream.read()
# My webcam yields frames in BGR format
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
pix = QtGui.QPixmap.fromImage(img)
self.video_frame.setPixmap(pix)
QtCore.QThread.sleep(0)
def data_stream(self):
print("data stream")
stream_data=round(random()*10,3)
data_list.insert(0,str(stream_data)+'\n')
if len(data_list)>10:
del data_list[-1]
for i in range(len(data_list)):
self.data_frame.addItem(data_list[i])
self.data_frame.show()
QtCore.QThread.sleep(1000)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Edit:
Thanks #musicamante's answer. I have updated the code as follows but still have the error "segmentation fault" for the video streaming, while if I run for data stream only, the updated list can be shown. So what's wrong with the setPixmap function? Thanks again!
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
import cv2
from vidgear.gears import CamGear
from random import random
fps=60
options_cam={"CAP_PROP_FRAME_WIDTH":480,"CAP_PROP_FRAME_HEIGHT":480,"CAP_PROP_FPS":fps}
stream=CamGear(source=0,logging=False,**options_cam).start()
class CamGrabber(QtCore.QThread):
frame = QtCore.pyqtSignal(QtGui.QImage)
def run(self):
while True:
new_frame = stream.read()
new_frame = cv2.cvtColor(new_frame, cv2.COLOR_BGR2RGB)
img = QtGui.QImage(new_frame, new_frame.shape[1], new_frame.shape[0], QtGui.QImage.Format_RGB888)
self.frame.emit(img)
class DataProvider(QtCore.QThread):
data = QtCore.pyqtSignal(object)
def run(self):
while True:
newData = round(random()*10,3)
self.data.emit(newData)
QtCore.QThread.sleep(1)
class MainWindow(QtWidgets.QWidget):
def __init__(self,*args):
super(MainWindow, self).__init__()
self.setWindowTitle('Vehicle control')
self.grid_layout=QtWidgets.QGridLayout()
self.video_label = QtWidgets.QLabel('Video streaming',self)
self.video_frame = QtWidgets.QLabel(self)
self.grid_layout.addWidget(self.video_label,0,0)
self.grid_layout.addWidget(self.video_frame,1,0)
self.data_label = QtWidgets.QLabel('Data streaming',self)
self.data_frame = QtWidgets.QListWidget(self)
self.grid_layout.addWidget(self.data_label,0,1)
self.grid_layout.addWidget(self.data_frame,1,1)
self.setLayout(self.grid_layout)
self.camObject = CamGrabber()
self.camObject.frame.connect(self.newFrame)
self.camObject.start()
self.dataProvider = DataProvider()
self.dataProvider.data.connect(self.newData)
self.dataProvider.start()
def newFrame(self, img):
self.video_frame.setPixmap(QtGui.QPixmap.fromImage(img))
def newData(self, data):
self.data_frame.insertItem(0,str(data))
if self.data_frame.count() > 10:
self.data_frame.takeItem(9)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())```
The QTimer error basically means that the a QTimer can only be started from the thread it exists.
Besides that, GUI element should always be directly accessed or modified from the main thread, not from another one.
In order to accomplish that, you'll need to create a separate "worker" thread, and communicate with the main one by taking advantage of the signal/slot mechanism.
class CamGrabber(QtCore.QThread):
frame = QtCore.pyqtSignal(QtGui.QImage)
def run(self):
while True:
frame = stream.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QtGui.QImage(frame, frame.shape[1], frame.shape[0], QtGui.QImage.Format_RGB888)
self.frame.emit(img)
class DataProvider(QtCore.QThread):
data = QtCore.pyqtSignal(object)
def run(self):
while True:
newData = round(random()*10,3)
self.data.emit(newData)
# note that QThread.sleep unit is seconds, not milliseconds
QtCore.QThread.sleep(1)
class MainWindow(QtWidgets.QWidget):
def __init__(self,*args):
super(MainWindow, self).__init__()
self.setWindowTitle('Vehicle control')
self.grid_layout=QtWidgets.QGridLayout()
self.video_label = QtWidgets.QLabel('Video streaming',self)
self.video_frame = QtWidgets.QLabel(self)
self.grid_layout.addWidget(self.video_label,0,0)
self.grid_layout.addWidget(self.video_frame,1,0)
self.data_label = QtWidgets.QLabel('Data streaming',self)
self.data_frame = QtWidgets.QListWidget(self)
self.grid_layout.addWidget(self.data_label,0,1)
self.grid_layout.addWidget(self.data_frame,1,1)
self.setLayout(self.grid_layout)
self.camObject = CamGrabber()
self.camObject.frame.connect(self.newFrame)
self.camObject.start()
self.dataProvider = DataProvider()
self.dataProvider.data.connect(self.newData)
self.dataProvider.start()
def newFrame(self, img):
self.video_frame.setPixmap(QtGui.QPixmap.fromImage(img))
def newData(self, data):
self.data_frame.addItem(str(data))
if self.data_frame.count() > 10:
self.data_frame.takeItem(0)
If, for any reason, you want to control the data fetching from the main thread via a QTimer, you could use a Queue:
from queue import Queue
class DataProvider(QtCore.QObject):
data = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.queue = Queue()
def run(self):
while True:
multi = self.queue.get()
# simulate a time consuming process
QtCore.QThread.sleep(5)
newData = round(multi * 10, 3)
self.data.emit(newData)
def pushData(self, data):
self.queue.put(data)
class MainWindow(QtWidgets.QWidget):
def __init__(self,*args):
# ...
self.requestTimer = QtCore.QTimer()
self.requestTimer.setInterval(1000)
self.requestTimer.timeout.connect(self.requestData)
# this will cause the timer to be executed only once after each time
# start() is called, so no new requests will overlap
self.requestTimer.setSingleShot(True)
self.requestTimer.start()
def requestData(self):
value = random()
print('requesting data with value {}'.format(value))
self.dataProvider.pushData(value)
print('waiting for result')
def newFrame(self, img):
self.video_frame.setPixmap(QtGui.QPixmap.fromImage(img))
def newData(self, data):
print('data received')
self.data_frame.addItem(str(data))
if self.data_frame.count() > 10:
self.data_frame.takeItem(0)
# restart the timer
self.requestTimer.start()

Using dynamically added widgets in PyQt/Pyside

I have modified the answer given here as written below. The code is basically creating pushbuttons with a counter as pushButton_0, pushButton_1..
Here, I know that when I press to self.addButton I am creating widgets named like self.pushButton_0, self.pushButton_1 etc. So, my question is, how I'm supposed to use this pushbuttons? Because when I'm trying to do something like self.pushButton_0.clicked.connect(self.x), it' s telling me that "there is no attribute named 'pushButton_0'".
Thanks!
from PyQt4 import QtGui, QtCore
import sys
class Main(QtGui.QMainWindow):
def __init__(self, parent = None):
super(Main, self).__init__()
self.GUI()
def GUI(self):
self.count = 0
# main button
self.addButton = QtGui.QPushButton('button to add other widgets')
self.addButton.clicked.connect(self.addWidget)
# scroll area widget contents - layout
self.scrollLayout = QtGui.QFormLayout()
# scroll area widget contents
self.scrollWidget = QtGui.QWidget()
self.scrollWidget.setLayout(self.scrollLayout)
# scroll area
self.scrollArea = QtGui.QScrollArea()
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setWidget(self.scrollWidget)
# main layout
self.mainLayout = QtGui.QVBoxLayout()
# add all main to the main vLayout
self.mainLayout.addWidget(self.addButton)
self.mainLayout.addWidget(self.scrollArea)
# central widget
self.centralWidget = QtGui.QWidget()
self.centralWidget.setLayout(self.mainLayout)
# set central widget
self.setCentralWidget(self.centralWidget)
def addWidget(self):
self.scrollLayout.addRow(Test(self))
self.count = self.count + 1
print(self.count)
class Test(QtGui.QWidget):
def __init__( self, main):
super(Test, self).__init__()
self.Main = main
self.setup()
def setup(self):
print(self.Main.count)
name = "pushButton_"+str(self.Main.count)
print(name)
self.name = QtGui.QPushButton('I am in Test widget '+str(self.Main.count))
layout = QtGui.QHBoxLayout()
layout.addWidget(self.name)
self.setLayout(layout)
app = QtGui.QApplication(sys.argv)
myWidget = Main()
myWidget.show()
app.exec_()
After hours, I found the problem!
You have to declare the signal while creating the pushbutton!
To fix this, I rewrote the setup function as below;
def setup(self):
print(self.Main.count)
name = "pushButton_"+str(self.Main.count)
print(name)
self.name = QtGui.QPushButton('I am in Test widget '+str(self.Main.count))
self.name.clicked.connect(self.x) # self.x is any function
layout = QtGui.QHBoxLayout()
layout.addWidget(self.name)
self.setLayout(layout)
So know, you will run function x whenever you push the new created pushbuttons!

opencv pyqt video normal frame rate

I’m creating a special purpose video player in Python 3.6 using OpenCV3 and ffmepg for handling the images and using PyQt5 for the Windows environment. I chose this combination of packages because ffmpeg handles a wider variety of codecs than QtMultimedia.
I’ve run into one snag. My player does not play at regular speed – it plays at roughly ¾ of normal speed. I use QTimer.timer to loop my display engine (nextFrameSlot) at a speed of 1/framerate.
Any suggestions on how to get the video to play at regular speed? Here is an abbreviated set of code that demonstrates my problem.
import sys
from PyQt5.QtWidgets import QWidget, QLabel, QFormLayout, QPushButton, QMainWindow
from PyQt5.QtWidgets import QAction, QMessageBox, QApplication, QFileDialog
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtCore import QTimer
import cv2
class VideoCapture(QWidget):
def __init__(self, filename, parent):
super(QWidget, self).__init__()
self.cap = cv2.VideoCapture(str(filename[0]))
self.length = int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.frame_rate = self.cap.get(cv2.CAP_PROP_FPS)
#self.codec = self.cap.get(cv2.CAP_PROP_FOURCC)
self.video_frame = QLabel()
parent.layout.addWidget(self.video_frame)
def nextFrameSlot(self):
ret, frame = self.cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
img = QImage(frame, frame.shape[1], frame.shape[0], QImage.Format_RGB888)
pix = QPixmap.fromImage(img)
self.video_frame.setPixmap(pix)
def start(self):
self.timer = QTimer()
self.timer.timeout.connect(self.nextFrameSlot)
self.timer.start(1000.0/self.frame_rate)
def pause(self):
self.timer.stop()
def deleteLater(self):
self.cap.release()
super(QWidget, self).deleteLater()
class VideoDisplayWidget(QWidget):
def __init__(self,parent):
super(VideoDisplayWidget, self).__init__(parent)
self.layout = QFormLayout(self)
self.startButton = QPushButton('Start', parent)
self.startButton.clicked.connect(parent.startCapture)
self.startButton.setFixedWidth(50)
self.pauseButton = QPushButton('Pause', parent)
self.pauseButton.setFixedWidth(50)
self.layout.addRow(self.startButton, self.pauseButton)
self.setLayout(self.layout)
class ControlWindow(QMainWindow):
def __init__(self):
super(ControlWindow, self).__init__()
self.setGeometry(50, 50, 800, 600)
self.setWindowTitle("PyTrack")
self.capture = None
self.isVideoFileLoaded = False
self.quitAction = QAction("&Exit", self)
self.quitAction.setShortcut("Ctrl+Q")
self.quitAction.triggered.connect(self.closeApplication)
self.openVideoFile = QAction("&Open Video File", self)
self.openVideoFile.setShortcut("Ctrl+Shift+V")
self.openVideoFile.triggered.connect(self.loadVideoFile)
self.mainMenu = self.menuBar()
self.fileMenu = self.mainMenu.addMenu('&File')
self.fileMenu.addAction(self.openVideoFile)
self.fileMenu.addAction(self.quitAction)
self.videoDisplayWidget = VideoDisplayWidget(self)
self.setCentralWidget(self.videoDisplayWidget)
def startCapture(self):
if not self.capture and self.isVideoFileLoaded:
self.capture = VideoCapture(self.videoFileName, self.videoDisplayWidget)
self.videoDisplayWidget.pauseButton.clicked.connect(self.capture.pause)
self.capture.start()
def endCapture(self):
self.capture.deleteLater()
self.capture = None
def loadVideoFile(self):
try:
self.videoFileName = QFileDialog.getOpenFileName(self, 'Select a Video File')
self.isVideoFileLoaded = True
except:
print ("Please Select a Video File")
def closeApplication(self):
choice = QMessageBox.question(self, 'Message','Do you really want to exit?',QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
print("Closing....")
sys.exit()
else:
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
window = ControlWindow()
window.show()
sys.exit(app.exec_())
Solved - I needed to specify self.timer.setTimerType(Qt.PreciseTimer) after the statement self.timer = QTimer() in the function start(self). By default, QTimer() uses a coarse timer. For Windows, the coarse time is 15.6 msec intervals.

How do I auto fit a Matplotlib figure inside a PySide QFrame?

I'm creating a simple PySide application that also uses MatPlotLib. However, when I add the figure into a QFrame, the figure doesn't automatically fit to the frame:
My graph is created using the following code:
class GraphView(gui.QWidget):
def __init__(self, name, title, graphTitle, parent = None):
super(GraphView, self).__init__(parent)
self.name = name
self.graphTitle = graphTitle
self.dpi = 100
self.fig = Figure((5.0, 3.0), dpi = self.dpi, facecolor = (1,1,1), edgecolor = (0,0,0))
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.Title = gui.QLabel(self)
self.Title.setText(title)
self.layout = gui.QVBoxLayout()
self.layout.addStretch(1)
self.layout.addWidget(self.Title)
self.layout.addWidget(self.canvas)
self.setLayout(self.layout)
def UpdateGraph(self, data, title = None):
self.axes.clear()
self.axes.plot(data)
if title != None:
self.axes.set_title(title)
self.canvas.draw()
And it's added to the main Widget like so:
# Create individual Widget/Frame (fftFrame)
fftFrame = gui.QFrame(self)
fftFrame.setFrameShape(gui.QFrame.StyledPanel)
self.FFTGraph = GraphView('fftFrame', 'FFT Transform:', 'FFT Transform of Signal', fftFrame)
Here's a working code sample that shows you how to get it working. I first thought it was because of the stretch you added to the layout, which will use up the additional space around the other widgets. But when I removed it, it still wouldn't resize. The 'easy' solution is to add a resizeEvent, which lets you define the size of your GraphView widget. In this case I just set its geometry to be that of the QFrame, though you might want to add some padding and make sure you set a sensible minimum size for the QFrame.
from PySide import QtGui
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.fft_frame = FftFrame(self)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.fft_frame)
self.setLayout(self.layout)
self.setCentralWidget(self.fft_frame)
class FftFrame(QtGui.QFrame):
def __init__(self, parent=None):
super(FftFrame, self).__init__(parent)
self.setFrameShape(QtGui.QFrame.StyledPanel)
self.parent = parent
self.graph_view = GraphView('fftFrame', 'FFT Transform:', 'FFT Transform of Signal', self)
def resizeEvent(self, event):
self.graph_view.setGeometry(self.rect())
class GraphView(QtGui.QWidget):
def __init__(self, name, title, graph_title, parent = None):
super(GraphView, self).__init__(parent)
self.name = name
self.graph_title = graph_title
self.dpi = 100
self.fig = Figure((5.0, 3.0), dpi = self.dpi, facecolor = (1,1,1), edgecolor = (0,0,0))
self.axes = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
self.canvas.setParent(self)
self.Title = QtGui.QLabel(self)
self.Title.setText(title)
self.layout = QtGui.QVBoxLayout()
self.layout.addWidget(self.Title)
self.layout.addWidget(self.canvas)
self.layout.setStretchFactor(self.canvas, 1)
self.setLayout(self.layout)
self.canvas.show()
def update_graph(self, data, title = None):
self.axes.clear()
self.axes.plot(data)
if title != None:
self.axes.set_title(title)
self.canvas.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

Resources