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()
Related
This is the test code about QThread and Signal.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import time
import sys
class Thread1(QThread):
set_signal = pyqtSignal(int) # (1) ##
def __init__(self, parent):
super().__init__(parent)
def run(self):
for i in range(10):
time.sleep(1)
self.set_signal.emit(i) # (3) ##
class MainWidget(QWidget):
def __init__(self):
super().__init__()
thread_start = QPushButton("시 작!")
thread_start.clicked.connect(self.increaseNumber)
vbox = QVBoxLayout()
vbox.addWidget(thread_start)
self.resize(200,200)
self.setLayout(vbox)
def increaseNumber(self):
x = Thread1(self)
x.set_signal.connect(self.print) # (2) ##
x.start()
def print(self, number):
print(number)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = MainWidget()
widget.show()
sys.exit(app.exec_())
In the example of QThread I searched for, a pyqtSignal()(step 1) object was created, the desired slot function was connected(step 2) by connect, and then called by emit()(step 3).
I don't know the difference from calling the desired method immediately without connecting the connect().
So, the goal of codes is triggering a desired function every second. You are right about it.
But if you create multiple objects, you can connect it to different desired functions. Or connect multiple function to one signal.
x = Thread1(self)
x.set_signal.connect(self.print) # (2) ##
x.start()
y = Thread1(self)
y.set_signal.connect(self.print2)
time.sleep(0.5)
y.start()
I am trying to make an application that will use OpenCV to read the live stream from the webcam along with Python Text to speech (Pyttsx3) library which will simultaneously read out the text and give a live video from the webcam but the stream is freezed when it is speaking out text.
So I created separate threads to get the stream, show it, and also a separate thread for pyttsx3 but again the video gets freeze when it says out the text.
I have tried this code
from threading import Thread
import cv2
from datetime import datetime
import pyttsx3
class VideoGet:
"""
Class that continuously gets frames from a VideoCapture object
with a dedicated thread.
"""
def __init__(self, src=0):
self.stream = cv2.VideoCapture(src)
(self.grabbed, self.frame) = self.stream.read()
self.stopped = False
def start(self):
Thread(target=self.get, args=()).start()
return self
def get(self):
while not self.stopped:
if not self.grabbed:
self.stop()
else:
(self.grabbed, self.frame) = self.stream.read()
def stop(self):
self.stopped = True
class VideoShow:
"""
Class that continuously shows a frame using a dedicated thread.
"""
def __init__(self, frame=None):
self.frame = frame
self.stopped = False
def start(self):
Thread(target=self.show, args=()).start()
return self
def show(self):
while not self.stopped:
cv2.imshow("Video", self.frame)
if cv2.waitKey(1) == ord("q"):
self.stopped = True
def stop(self):
self.stopped = True
class CountsPerSec:
"""
Class that tracks the number of occurrences ("counts") of an
arbitrary event and returns the frequency in occurrences
(counts) per second. The caller must increment the count.
"""
def __init__(self):
self._start_time = None
self._num_occurrences = 0
def start(self):
self._start_time = datetime.now()
return self
def increment(self):
self._num_occurrences += 1
def countsPerSec(self):
elapsed_time = (datetime.now() - self._start_time).total_seconds()
#elapsed_time!=0
return self._num_occurrences/elapsed_time
def putIterationsPerSec(frame, iterations_per_sec):
"""
Add iterations per second text to lower-left corner of a frame.
"""
cv2.putText(frame, "{:.0f} iterations/sec".format(iterations_per_sec),
(10, 450), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 255))
return frame
class TextToSpeech:
def __init__(self):
self.engine=pyttsx3.init(debug=True)
#self.engine.startLoop(True)
def start(self):
Thread(target=self.speech("i am "),args=()).start()
return self
def speech(self,saytext):
self.engine.say(saytext)
self.engine.runAndWait()
def stop(self):
self.stopped=True
def threadBoth(source=0):
"""
Dedicated thread for grabbing video frames with VideoGet object.
Dedicated thread for showing video frames with VideoShow object.
Dedicated thread for text to speech object.
Main thread serves only to pass frames between VideoGet and
VideoShow objects/threads.
"""
#tts=TextToSpeech()
tts1=TextToSpeech().start()
video_getter = VideoGet(source).start()
video_shower = VideoShow(video_getter.frame).start()
cps = CountsPerSec().start()
while True:
if video_getter.stopped or video_shower.stopped:
video_shower.stop()
video_getter.stop()
break
tts1.speech("hey anmol")
frame = video_getter.frame
frame = putIterationsPerSec(frame, cps.countsPerSec())
video_shower.frame = frame
threadBoth(0)
Hi I was looking for a way to implement this too. I found this to be working for me:
import pyttsx3
import threading
import queue
# To play audio text-to-speech during execution
class TTSThread(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
self.daemon = True
self.start()
def run(self):
tts_engine = pyttsx3.init()
tts_engine.startLoop(False)
t_running = True
while t_running:
if self.queue.empty():
tts_engine.iterate()
else:
data = self.queue.get()
if data == "exit":
t_running = False
else:
tts_engine.say(data)
tts_engine.endLoop()
q = queue.Queue()
tts_thread = TTSThread(q)
and then you can change the text to use in your while loop by using:
q.put(text)
I am trying to add new lines to an existing and open Plot.
I wrote a watchdog watching a folder with measurement data. Every few secs there will be a new data file. The Application I try to generate should read the file when triggered by the watchdog and add the data to the plot.
Dynamic plots using QTimer and stuff is easy when updating existing data, but I don't get a hook for new lines.
Also, do I have to use multithreading when I want to run a script while having a plot on _exec()?
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pq
import sys
import numpy as np
import time
class Plot2d(object):
def __init__(self):
self.traces = dict()
self.num = 0
pq.setConfigOptions(antialias=True)
self.app = QtGui.QApplication(sys.argv)
self.win = pq.GraphicsWindow(title='examples')
self.win.resize(1000, 600)
self.win.setWindowTitle('Windowtitle')
self.canvas = self.win.addPlot(title='Plot')
def starter(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def trace(self, name, dataset_x, dataset_y):
if name in self.traces:
self.traces[name].setData(dataset_x, dataset_y)
else:
self.traces[name] = self.canvas.plot(pen='y')
def update(self, i):
x_data = np.arange(0, 3.0, 0.01)
y_data = x_data*i
self.trace(str(i), x_data, y_data)
self.trace(str(i), x_data, y_data)
if __name__ == '__main__':
p = Plot2d()
p.update(1)
p.starter()
time.sleep(1)
p.update(2)
This is what I tried. The update function should be called by the watchdog when new data is available in the dir.
import time as time
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
if __name__ == "__main__":
patterns = "*"
ignore_patterns = ""
ignore_directories = False
case_sensitive = True
go_recursively = False
my_event_handler = PatternMatchingEventHandler(patterns, ignore_patterns, ignore_directories, case_sensitive)
def on_created(event):
print(f" {event.src_path} has been created!") #this function should call the plot update
my_event_handler.on_created = on_created
path = (r'C:\Users\...') #path from GUI at some point
my_observer = Observer()
my_observer.schedule(my_event_handler, path, recursive=go_recursively)
my_observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
my_observer.stop()
my_observer.join()
The exec_() method is blocking, so starter() will be too, and will be unlocked when the window is closed, which implies that all the code after starter will only be executed after closing the window. On the other hand you should not use time.sleep in the GUI thread as it blocks the execution of the GUI event loop.
Depending on the technology used, ways of updating elements are offered, in the case of Qt the appropriate way is to use signals so that all the logic of the watchdog will be encapsulated in a QObject that will transmit the information to the GUI through a signal, Then the GUI takes that information to plot.
import sys
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pq
import numpy as np
class QObserver(QtCore.QObject):
dataChanged = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super(QObserver, self).__init__(parent)
patterns = "*"
ignore_patterns = ""
ignore_directories = False
case_sensitive = True
go_recursively = False
path = r"C:\Users\..." # path from GUI at some point
event_handler = PatternMatchingEventHandler(
patterns, ignore_patterns, ignore_directories, case_sensitive
)
event_handler.on_created = self.on_created
self.observer = Observer()
self.observer.schedule(event_handler, path, recursive=go_recursively)
self.observer.start()
def on_created(self, event):
print(
f" {event.src_path} has been created!"
) # this function should call the plot update
self.dataChanged.emit(np.random.randint(1, 5))
class Plot2d(QtCore.QObject):
def __init__(self, parent=None):
super().__init__(parent)
self.traces = dict()
self.num = 0
pq.setConfigOptions(antialias=True)
self.app = QtGui.QApplication(sys.argv)
self.win = pq.GraphicsWindow(title="examples")
self.win.resize(1000, 600)
self.win.setWindowTitle("Windowtitle")
self.canvas = self.win.addPlot(title="Plot")
def starter(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, "PYQT_VERSION"):
QtGui.QApplication.instance().exec_()
def trace(self, name, dataset_x, dataset_y):
if name in self.traces:
self.traces[name].setData(dataset_x, dataset_y)
else:
self.traces[name] = self.canvas.plot(pen="y")
#QtCore.pyqtSlot(object)
def update(self, i):
x_data = np.arange(0, 3.0, 0.01)
y_data = x_data * i
self.trace(str(i), x_data, y_data)
self.trace(str(i), x_data, y_data)
if __name__ == "__main__":
p = Plot2d()
qobserver = QObserver()
qobserver.dataChanged.connect(p.update)
p.starter()
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.
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 :-/