Using dynamically added widgets in PyQt/Pyside - pyqt

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!

Related

Open another wx.python frame cause the main UI to be blocked

I am trying to open another wx frame from a main frame UI. I followed the example shown here: https://wiki.wxpython.org/CallAfter but my main UI is still blocked.
Here is the event listener on the main UI:
def testShowGUI(self):
# This process is a long one
# It uses the vtk to read point cloud file and reconstruct the surface
file = "cache/d9c5e8ef-7b7f-485e-8fc8-23098c32afcb.ply"
reader = vtk.vtkPLYReader()
reader.SetFileName(file)
reader.Update()
delaunay = vtk.vtkDelaunay2D()
delaunay.SetAlpha(0.1)
delaunay.SetTolerance(0.0001)
delaunay.SetOffset(1.25)
delaunay.BoundingTriangulationOff()
delaunay.SetInputData(reader.GetOutput())
delaunay.Update()
#Once finish reading and processing the point cloud, pass to the next function for rendering
wx.CallAfter(self.AfterProcess, delaunay)
def AfterProcess(self, data):
meshVisGui = MesVisGUI.MeshVisGui(data)
meshVisGui.Show()
def OnEnter(self, event):
#Event listener when user click on Enter button
my_thread = threading.Thread(target=self.testShowGUI)
my_thread.start()
The code for the separate frame is as below:
class MeshVisGui(wx.Frame):
SPACING = 4
def __init__(self, delaunay, parent=None):
self.delaunayData = delaunay
self.title = "Mesh Visualization"
wx.Frame.__init__(self, None, -1, self.title)
self.Initialize()
def Initialize(self):
self.panel = wx.Panel(self, -1, size=(600, 400), style=wx.BORDER_RAISED)
self.widget3d = wxVTKRenderWindowInteractor(self.panel, -1)
self.widget3d.Enable()
self.render = vtk.vtkRenderer()
self.render.SetBackground(params.BackgroundColor)
self.widget3d.GetRenderWindow().AddRenderer(self.render)
self.interactor = self.widget3d.GetRenderWindow().GetInteractor()
self.interactor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
self.axesWidget = utils.createAxes(self.interactor)
self.meshActor = utils.build_actor(self.delaunayData)
self.render.AddActor(self.meshActor)
self.render.ResetCamera()
box = wx.BoxSizer(wx.VERTICAL)
box.Add(self.widget3d, 1, wx.EXPAND, self.SPACING)
self.panel.SetSizer(box)
self.Layout()
However, on my main UI, it is still showing a spinning icon and block the UI while it is trying to process the point cloud data. Can someone help me spot what I have done wrong?
Wxpython version: 4.0.1
Python version: 3.6.5
Following on from Mike Driscoll's answer and comment, here is a sample of a threaded task running from another panel.
The thread reports back to the 2nd panel (it's parent) using an event. This allows a progress bar to be updated.
The 2nd panel includes a "Cancel" option for the threaded task, whilst the main frame has a button to test if it is not frozen.
The use of wx.GetApp().Yield() may be slightly old fashioned but I have always found it to be reliable.
import time
import wx
from threading import Thread
import wx.lib.newevent
progress_event, EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class ThreadFrame(wx.Frame):
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
panel = wx.Panel(self)
self.btn = wx.Button(panel,label='Stop Long running process', size=(200,30), pos=(10,10))
self.btn.Bind(wx.EVT_BUTTON, self.OnExit)
self.progress = wx.Gauge(panel,size=(300,10), pos=(10,50), range=300)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT, self.OnProgress)
#Bind to Exit on frame close
self.Bind(wx.EVT_CLOSE, self.OnExit)
self.Show()
self.mythread = TestThread(self)
#Enable the GUI to be responsive by briefly returning control to the main App
while self.mythread.isAlive():
time.sleep(0.1)
wx.GetApp().Yield()
continue
try:
self.OnExit(None)
except:
pass
def OnProgress(self, event):
self.progress.SetValue(event.count)
#or for indeterminate progress
#self.progress.Pulse()
def OnExit(self, event):
if self.mythread.isAlive():
self.mythread.terminate() # Shutdown the thread
self.mythread.join() # Wait for it to finish
self.Destroy()
class TestThread(Thread):
def __init__(self,parent_target):
Thread.__init__(self)
self.parent = parent_target
self.stopthread = False
self.time = time.time()
self.start() # start the thread
def run(self):
# A loop that will run for 5 minutes then terminate
while self.stopthread == False:
curr_loop = int(time.time() - self.time)
if curr_loop < 300:
time.sleep(1)
evt = progress_event(count=curr_loop)
#Send back current count for the progress bar
try:
wx.PostEvent(self.parent, evt)
except: # The parent frame has probably been destroyed
self.terminate()
else:
self.terminate()
def terminate(self):
self.stopthread = True
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
self.text_count = 0
self.parent=parent
btn = wx.Button(self, wx.ID_ANY, label='Start Long running process', size=(200,30), pos=(10,10))
btn.Bind(wx.EVT_BUTTON, self.Thread_Frame)
btn2 = wx.Button(self, wx.ID_ANY, label='Is the GUI still active?', size=(200,30), pos=(10,50))
btn2.Bind(wx.EVT_BUTTON, self.AddText)
self.txt = wx.TextCtrl(self, wx.ID_ANY, style= wx.TE_MULTILINE, pos=(10,90),size=(400,100))
def Thread_Frame(self, event):
frame = ThreadFrame(title='Threaded Task', parent=self.parent)
def AddText(self,event):
self.text_count += 1
txt = self.txt.GetValue()
txt += "More text " + str(self.text_count)+"\n"
self.txt.SetValue(txt)
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame', size=(600,400))
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
You do not need threads to open new frames / windows in your wxPython application. You just need to create a sub-class of wx.Frame to hold the code of your other frame. Then from your main application's frame, you can instantiate the other frame and show it. You use the same concept when you create a wx.Dialog or a wx.MessageDialog.
Here is a simple example:
import wx
class OtherFrame(wx.Frame):
"""
Class used for creating frames other than the main one
"""
def __init__(self, title, parent=None):
wx.Frame.__init__(self, parent=parent, title=title)
self.Show()
class MyPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
btn = wx.Button(self, label='Create New Frame')
btn.Bind(wx.EVT_BUTTON, self.on_new_frame)
def on_new_frame(self, event):
frame = OtherFrame(title='SubFrame',
parent=wx.GetTopLevelParent(self))
class MainFrame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None, title='Main Frame')
panel = MyPanel(self)
self.Show()
if __name__ == '__main__':
app = wx.App(False)
frame = MainFrame()
app.MainLoop()
In this example, I set the other frame's parent to the MainFrame instance by using wx.GetTopLevelParent(self). The benefit of setting a parent for the sub-frames is that if I close the main frame, it will cause the other frames to also be closed.

in Python3/tkinter is it possible to create a toolbar into a separate class, but still allowing it to interact with the main app?

I am about to begin a new Python3/tkinter project and I want to make sure that I get as much code out of my way as possible. I am creating an app that will have, for now, one window composed of 3 areas:
Toolbar
Center/main area
Statusbar
I am trying to keep the main app class as clean as possible, offloading code to other auxiliary classes. So, following some tutorials and adapting from what I have been doing until now, I was able already to set an external toolbar class that can be changed on demand, from the main app. Now, I am trying to create a class for the toolbar, but I am afraid it won't be possible to create the buttons and their respective callbacks in a separate class, as I don't know how to make them call functions that are in the main app. Is that even possible?
This is what I got right now:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
class App:
""" main class for the application """
def __init__(self,master):
mainframe = ttk.Frame(master)
topframe = ttk.Frame(mainframe)
centerframe = ttk.Frame(mainframe)
bottomframe = ttk.Frame(mainframe)
my_toolbar = Toolbar(topframe)
my_statusbar = StatusBar(mainframe)
my_statusbar.set("This is the statusbar")
centerlabel = ttk.Label(centerframe, text="Center stuff goes here")
centerlabel.pack()
topframe.pack(side=TOP, fill=X)
centerframe.pack(side=TOP, fill=BOTH)
bottomframe.pack(side=BOTTOM, fill=X)
mainframe.pack(side=TOP, expand=True, fill=BOTH)
def button_function(self, *event):
print("filter")
class StatusBar(ttk.Frame):
""" Simple Status Bar class - based on Frame """
def __init__(self,master):
ttk.Frame.__init__(self,master)
self.label = ttk.Label(self,anchor=W)
self.label.pack()
self.pack(side=BOTTOM, fill=X)
def set(self,texto):
self.label.config(text=texto)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
class Toolbar:
""" Toolbar """
def button_one(self):
print("button 1 pressed")
def button_two(self):
print("button 2 pressed")
def __init__(self,master):
self.button1 = ttk.Button(master,text="One",command=self.button_one())
self.button2 = ttk.Button(master,text="Two",command=self.button_two())
self.button1.grid(row=0,column=0)
self.button2.grid(row=0,column=1)
if __name__ == "__main__":
root = Tk()
app = App(root)
root.mainloop()
Let's say that I need to make button1 to trigger button_function() in order to update some info being shown there. Should I simply move the toolbar into the App class, for instance in a class method called from its __init__()? Or is there a better way?
Maybe I should add that i intend later to add some Toplevelwindows that probably could make use of some of these general classes. I want to pave the road in a nice way.
This is certainly possible. There are two possibilities here. The first one to make app inherit from ttk.Frame instead of using mainframe. Then you can pass App as master to toolbar etc. Here is the redone code:
#!/usr/bin/python3
import tkinter as tk
from tkinter import ttk
class App(ttk.Frame):
""" main class for the application """
def __init__(self,master,*args,**kwargs):
super().__init__(master,*args,**kwargs)
self.my_toolbar = Toolbar(self)
self.my_statusbar = StatusBar(self)
self.my_statusbar.set("This is the statusbar")
self.centerframe = CenterFrame(self)
self.pack(side=tk.TOP, expand=True, fill=tk.BOTH)
def button_function(self, *event):
print("filter")
class CenterFrame(ttk.Frame):
def __init__(self,master,*args,**kwargs):
super().__init__(master,*args,**kwargs)
self.master = master
self.pack(side=tk.BOTTOM, fill=tk.X)
self.centerlabel = ttk.Label(self, text="Center stuff goes here")
self.centerlabel.pack()
class StatusBar(ttk.Frame):
""" Simple Status Bar class - based on Frame """
def __init__(self,master):
ttk.Frame.__init__(self,master)
self.master = master
self.label = ttk.Label(self,anchor=tk.W)
self.label.pack()
self.pack(side=tk.BOTTOM, fill=tk.X)
def set(self,texto):
self.label.config(text=texto)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
class Toolbar(ttk.Frame):
""" Toolbar """
def button_one(self):
print("button 1 pressed")
def button_two(self):
print("button 2 pressed")
self.master.button_function()
def __init__(self,master):
super().__init__(master)
self.master = master
self.pack(side=tk.TOP, fill=tk.X)
self.button1 = ttk.Button(self,text="One",command=self.button_one)
self.button2 = ttk.Button(self,text="Two",command=self.button_two)
self.button1.grid(row=0,column=0)
self.button2.grid(row=0,column=1)
if __name__ == "__main__":
root = tk.Tk()
app = App(root)
root.mainloop()
The second one is to just pass App as an argument to the other classes.
You are missing some self., button commands assignments don't need parenthesis and after that you can call the button configuration from anywhere in your program. So for the button1 command this will be:
app.my_toolbar.button1.config(command=app.button_function)
I am fixing your errors as is, not make the program better:
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
class App:
""" main class for the application """
def __init__(self,master):
self.mainframe = ttk.Frame(master)
self.topframe = ttk.Frame(self.mainframe)
self.centerframe = ttk.Frame(self.mainframe)
self.bottomframe = ttk.Frame(self.mainframe)
self.my_toolbar = Toolbar(self.topframe)
self.my_statusbar = StatusBar(self.mainframe)
self.my_statusbar.set("This is the statusbar")
self.centerlabel = ttk.Label(self.centerframe, text="Center stuff goes here")
self.centerlabel.pack()
self.topframe.pack(side=TOP, fill=X)
self.centerframe.pack(side=TOP, fill=BOTH)
self.bottomframe.pack(side=BOTTOM, fill=X)
self.mainframe.pack(side=TOP, expand=True, fill=BOTH)
def button_function(self, *event):
print("filter")
class StatusBar(ttk.Frame):
""" Simple Status Bar class - based on Frame """
def __init__(self,master):
ttk.Frame.__init__(self,master)
self.label = ttk.Label(self,anchor=W)
self.label.pack()
self.pack(side=BOTTOM, fill=X)
def set(self,texto):
self.label.config(text=texto)
self.label.update_idletasks()
def clear(self):
self.label.config(text="")
self.label.update_idletasks()
class Toolbar:
""" Toolbar """
def button_one(self):
print("button 1 pressed")
def button_two(self):
print("button 2 pressed")
def __init__(self,master):
self.button1 = ttk.Button(master,text="One",command=self.button_one)
self.button2 = ttk.Button(master,text="Two",command=self.button_two)
self.button1.grid(row=0,column=0)
self.button2.grid(row=0,column=1)
if __name__ == "__main__":
root = Tk()
app = App(root)
app.my_toolbar.button1.config(command=app.button_function)
root.mainloop()

PYQT how to draw line between two buttons

from PyQt4 import QtGui, QtCore
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.view = View(self)
self.button = QtGui.QPushButton('Clear View', self)
self.button.clicked.connect(self.handleClearView)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
layout.addWidget(self.button)
def handleClearView(self):
self.view.scene().clear()
class DragButton(QtGui.QPushButton):
def mousePressEvent(self, event):
self.__mousePressPos = None
self.__mouseMovePos = None
if event.button() == QtCore.Qt.LeftButton:
self.__mousePressPos = event.globalPos()
self.__mouseMovePos = event.globalPos()
#super(DragButton, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
# adjust offset from clicked point to origin of widget
currPos = self.mapToGlobal(self.pos())
globalPos = event.globalPos()
diff = globalPos - self.__mouseMovePos
newPos = self.mapFromGlobal(currPos + diff)
self.move(newPos)
self.__mouseMovePos = globalPos
#super(DragButton, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.__mousePressPos is not None:
moved = event.globalPos() - self.__mousePressPos
if moved.manhattanLength() > 3:
event.ignore()
return
#super(DragButton, self).mouseReleaseEvent(event)
class View(QtGui.QGraphicsView):
def __init__(self, parent):
QtGui.QGraphicsView.__init__(self, parent)
self.setScene(QtGui.QGraphicsScene(self))
self.setSceneRect(QtCore.QRectF(self.viewport().rect()))
btn1=DragButton('Test1', self)
btn2=DragButton('Test2', self)
def mousePressEvent(self, event):
self._start = event.pos()
def mouseReleaseEvent(self, event):
start = QtCore.QPointF(self.mapToScene(self._start))
end = QtCore.QPointF(self.mapToScene(event.pos()))
self.scene().addItem(
QtGui.QGraphicsLineItem(QtCore.QLineF(start, end)))
for point in (start, end):
text = self.scene().addSimpleText(
'(%d, %d)' % (point.x(), point.y()))
text.setBrush(QtCore.Qt.red)
text.setPos(point)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.resize(640, 480)
window.show()
sys.exit(app.exec_())
Here is my code. There are two movable buttons on the QGraphicsView and I can draw line on the QGraphicsView with mouse dragging. But what I want to do is to draw line between two buttons. For detail, If I right click the btn1(Test1) and then right click the btn2(Test2) , the line would be created between two buttons. I'm struggling this problem for a month. Plz Help!
I am assuming that the line you need to draw between the buttons must also be movable. If not it is just simple you can just use :
lines = QtGui.QPainter()
lines.setPen(self)
lines.drawLine(x1,y1,x2,y2)
So, if the line needs to be movable along with the buttons then first you create a mini widget consisting of Two Buttons and the Line, so you can move the whole widget. This might help!, in that case.

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_()

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

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 :-/

Resources