python - Simple application of QThread -
i getting grip on qthread in order not lock gui, while performing possibly longer threads in background. trying practice write simple application: countdown timer can start clicking on button "start", thereby initiating countdown loop, can pause clicking on button "pause".
i want in "proper way" of using qthread (explained over here), i.e. subclassing qobject , attach instance of subclass qthread via movetothread. did gui qtdesigner , modified far (from other examples on net) in python:
from pyqt4 import qtgui, qtcore pyqt4.qtcore import qthread, signal import time, sys, mydesign class someobject(qtcore.qobject): def __init__(self, lcd): super(self.__class__, self).__init__() self.lcd = lcd self.looping = true finished = qtcore.pyqtsignal() def pauseloop(self): print "hello?" self.looping = false def longrunning(self): count = 10 self.lcd.display(count) while count > 0 , self.looping: time.sleep(1) count -= 1 self.lcd.display(count) self.finished.emit() class threadingtutorial(qtgui.qmainwindow, mydesign.ui_mainwindow): def __init__(self): super(self.__class__, self).__init__() self.setupui(self) #an instance of someobject gets attached #an instance of wrapper class qthread() #objthread wrapper object instance of # self-defined class someobject self.objthread = qtcore.qthread() self.obj = someobject(self.lcdnumber) self.obj.movetothread(self.objthread) #connect signals self.obj.finished.connect(self.objthread.quit) self.objthread.started.connect(self.obj.longrunning) self.startcountdown.clicked.connect(self.objthread.start) self.pausebutton.clicked.connect(self.obj.pauseloop) if __name__ == "__main__": app = qtgui.qapplication(sys.argv) form = threadingtutorial() form.show() app.exec_()
the gui not locked function pauseloop() gets executed after loop finished. how have in order accomplish goal able pause loop in longrunning()?
thx in advance!
update:
with of steve's , three_pineapples's remarks, come following solution using inner event loop of objthread:
from pyqt4 import qtgui, qtcore pyqt4.qtcore import qthread, signal, qtimer import time, sys, mydesign class someobject(qtcore.qobject): def __init__(self): super(self.__class__, self).__init__() self.looping = true self.count = 10 self.timer = qtimer(self) self.timer.start(1000) self.connect(self.timer, signal("timeout()"), self.longrunning) finished = qtcore.pyqtsignal() iterated = qtcore.pyqtsignal() def pauseloop(self): self.looping = false def longrunning(self): if self.count > 0 , self.looping: self.count -= 1 self.iterated.emit() else: self.finished.emit()# sends signal stopping event loop class threadingtutorial(qtgui.qmainwindow, mydesign.ui_mainwindow): def __init__(self): super(self.__class__, self).__init__() self.setupui(self) #an instance of someobject gets attached #an instance of wrapper class qthread() #objthread wrapper object instance of # self-defined class someobject self.objthread = qtcore.qthread() self.obj = someobject() self.lcdnumber.display(self.obj.count) self.obj.movetothread(self.objthread) #connect signals self.obj.finished.connect(self.objthread.quit) self.obj.iterated.connect(lambda: self.lcdnumber.display(self.obj.count)) self.objthread.started.connect(self.obj.longrunning) self.startcountdown.clicked.connect(self.objthread.start) self.pausebutton.clicked.connect(self.obj.pauseloop) if __name__ == "__main__": app = qtgui.qapplication(sys.argv) form = threadingtutorial() form.show() app.exec_()
this happening because tell qt run code someobject
object in single thread. in code, have 2 threads, main gui thread, , self.objthread
. pauseloop()
called in same thread longrunning()
, means longrunning()
must complete before pauseloop()
can run. instead, need call pauseloop()
different thread, not self.objthread
.
note when start changing data 2 different threads (your main thread , new thread), start run race conditions. since you're setting 1 boolean variable, you'll fine, it's keep in mind.
edit: pointed out in comment three_pineapples
, accessing gui objects (ex: qwidget
) should done in main gui thread. show how work, have updated answer use signals update lcd in gui thread instead of printing value stdout. added code print out current thread @ different sections.
from pyside import qtgui, qtcore pyside.qtcore import qthread, signal import time, sys class someobject(qtcore.qobject): updatecounter = qtcore.signal(int) finished = qtcore.signal() def __init__(self): super(self.__class__, self).__init__() self.looping = true def pauseloop(self): self.looping = false print 'pause loop: '+str(qthread.currentthreadid()) def longrunning(self): print 'long running: '+str(qthread.currentthreadid()) count = 10 while count > 0 , self.looping: count -= 1 self.updatecounter.emit(count) time.sleep(1) self.finished.emit() class threadingtutorial(qtgui.qwidget): def __init__(self): super(self.__class__, self).__init__() # self.thislayout = qtgui.qvboxlayout(self) self.startcountdown = qtgui.qpushbutton('start', self) self.pausebutton = qtgui.qpushbutton('stop', self) self.lcd = qtgui.qlabel('', self) self.thislayout.addwidget(self.startcountdown) self.thislayout.addwidget(self.pausebutton) self.thislayout.addwidget(self.lcd) # print 'main gui thread: '+str(qthread.currentthreadid()) self.objthread = qtcore.qthread() self.obj = someobject() self.obj.movetothread(self.objthread) self.obj.updatecounter.connect(self._updatetimer) # self.obj.finished.connect(self.objthread.quit) self.objthread.started.connect(self.obj.longrunning) self.startcountdown.clicked.connect(self.objthread.start) self.pausebutton.clicked.connect(self._stoptimer) def _stoptimer(self): self.obj.pauseloop() def _updatetimer(self, num): self.lcd.settext(str(num)) print 'update timer: '+str(qthread.currentthreadid()) if __name__ == "__main__": app = qtgui.qapplication(sys.argv) form = threadingtutorial() form.show() app.exec_()
example output:
main gui thread: 3074717376 long running: 3034471232 update timer: 3074717376 update timer: 3074717376 update timer: 3074717376 update timer: 3074717376 update timer: 3074717376 update timer: 3074717376 pause loop: 3074717376
Comments
Post a Comment