Below is my code for listing all the sub-directories of a directory. I'm using it to understand QThread and signal and slots in PySide. The problem is, when I'm not using Qtcore.QApplication.processEvents() in the scan() method of the Main class, the code does not work. Is the event-loop not already running?
import sys
import os
import time
from PySide import QtGui, QtCore
class Scanner(QtCore.QObject):
folderFound = QtCore.Signal(str)
done = QtCore.Signal()
def __init__(self, path):
super(Scanner, self).__init__()
self.path = path
#QtCore.Slot()
def scan(self):
for folder in os.listdir(self.path):
time.sleep(1)
self.folderFound.emit(folder)
class Main(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(420,130)
self.setWindowTitle('Threads')
self.lyt = QtGui.QVBoxLayout()
self.setLayout(self.lyt)
self.topLyt = QtGui.QHBoxLayout()
self.lyt.addLayout(self.topLyt)
self.scanBtn = QtGui.QPushButton('Scan')
self.scanBtn.clicked.connect(self.scan)
self.clearBtn = QtGui.QPushButton('Clear')
self.topLyt.addWidget(self.scanBtn)
self.topLyt.addWidget(self.clearBtn)
self.folders = list()
self.show()
def scan(self):
self.th = QtCore.QThread()
scanner = Scanner(r"D:\\")
scanner.moveToThread(self.th)
scanner.folderFound.connect(self.addFolder)
scanner.done.connect(scanner.deleteLater)
scanner.done.connect(self.quit)
self.th.started.connect(scanner.scan)
self.th.start()
QtCore.QApplication.processEvents()
#QtCore.Slot()
def addFolder(self, folder):
lbl = QtGui.QLabel(folder)
self.folders.append(lbl)
self.lyt.addWidget(lbl)
#QtCore.Slot()
def quit(self):
self.th.quit()
self.th.wait()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
main = Main()
app.exec_()
It is a pure fluke that your example works at all.
When you attempt to call QtCore.QApplication.processEvents(), a NameError will be raised, because the QApplication class is actually in the QtGui module, not the QtCore module. However, raising the exception has the side-effect of preventing your scanner object from being garbage-collected, and so the thread appears to run normally.
The correct way to fix your code is to keep a reference to the scanner object, and get rid of the processEvents line, which is not needed:
def scan(self):
self.th = QtCore.QThread()
# keep a reference to the scanner
self.scanner = Scanner(r"D:\\")
self.scanner.moveToThread(self.th)
self.scanner.folderFound.connect(self.addFolder)
self.scanner.done.connect(self.scanner.deleteLater)
self.scanner.done.connect(self.quit)
self.th.started.connect(self.scanner.scan)
self.th.start()
The rest of your code is okay, and the example will now work as expected.
Event loop is not something that runs behind your back. You're always the one who has to run it. A thread cannot be doing two things at once: if your code is the locus of control, then obviously the event loop isn't! You need to return from your code to the event loop, and make sure that your code was called from the event loop. Any sort of a UI signal, networking event, or timeout is invoked from the event loop, so most likely your code already has the event loop on the call stack. To keep the loop spinning, you have to return to it, though.
Never use processEvents - instead, invert the control, so that the event loop calls into your code, and then you perform a chunk of work, and finally return back to the event loop.
The idiom for "keep my code working from event loop" is a zero-duration timer. The callable that performs the work is attached to the timeout signal.
Related
After read and searching I am trying to use the generate a QObject then use the movetoThread method to run an independent process and allow the QMainWindow to continue to respond. This has not worked when I have tried to implement the operation in a QThread.run() method. The following code is my attempt to make a simple example. While the code works in running thread independent of the MainWindow, it does not abort. The only way I can get a thread to stop is to set worker.end = True. Which I think should not be the way to do it.
"""
This is a program to test Threading with Objects in PyQt4.
"""
from time import sleep
import sys
from PyQt4.QtCore import QObject, pyqtSlot, pyqtSignal, QThread
from PyQt4.QtGui import QMainWindow, QApplication, QProgressBar
from PyQt4.QtGui import QPushButton, QVBoxLayout, QWidget
class workerObject(QObject):
bar_signal = pyqtSignal(int)
res_signal = pyqtSignal(str)
term_signal = pyqtSignal()
def __init__(self, maxIters):
super(workerObject, self).__init__()
self.maxIters = maxIters
def run(self):
self.bar_signal.emit(self.maxIters)
sleep(1)
self.end = False
for step in range(self.maxIters):
if self.end:
self.maxIters = step
break
self.bar_signal.emit(step)
sleep(2)
self.res_signal.emit("Got to {}".format(self.maxIters))
self.term_signal.emit()
#pyqtSlot()
def mystop(self):
print "stop signalled?"
self.end = True
class MCwindow(QMainWindow):
abort_signal = pyqtSignal(name='abort_signal')
def __init__(self):
super(MCwindow,self).__init__()
self.maxIters = 50
widget = QWidget()
layout = QVBoxLayout(widget)
self.go_btn = QPushButton()
self.go_btn.setText('Go')
layout.addWidget(self.go_btn)
self.abort_btn = QPushButton()
self.abort_btn.setText('Stop')
layout.addWidget(self.abort_btn)
self.simulation_bar = QProgressBar()
self.simulation_bar.setRange(0, self.maxIters)
self.simulation_bar.setFormat("%v")
layout.addWidget(self.simulation_bar)
self.setCentralWidget(widget)
self.go_btn.clicked.connect(self.run_mc)
# The button calls the windows method to stop --- it could
# be that is 'clicked' calls the worker.mystop
# self.abort_btn.clicked.connect(self.stop_mc)
# This allows for the abort button to do somethign in the MainWindow
# before the abort_signal is sent, this works
self.abort_btn.clicked.connect(self.stop_mc)
def run_mc(self):
self.thread = QThread()
self.worker = workerObject(self.maxIters)
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.run)
# This is the simple stop method, but does not work
# self.abort_btn.clicked.connect(self.worker.mystop)
# This uses the signal in the MCwindow - this connection does NOT works
self.abort_signal.connect(self.worker.mystop)
# This does NOT stop the thread
# and would not allow for any clean up in the worker.
# self.abort_signal.connect(self.thread.terminate)
# This is a 'bad' way to stop the woker ... It does, however, work
# self.abort_signal.connect(self.stopper)
self.worker.bar_signal.connect(self.setBar)
self.worker.res_signal.connect(self.setData)
self.worker.term_signal.connect(self.thread.terminate)
self.thread.start()
def stop_mc(self):
print "Stopping?!"
# This signal is NEVER seen by the Worker.
self.abort_signal.emit()
def stopper(self):
print "I should stop?!"
# Should use signals to tell the worker to stop - and not setting a attribute
self.worker.end=True
#pyqtSlot(int)
def setBar(self, val):
self.simulation_bar.setValue(val)
#pyqtSlot(str)
def setData(self, txt):
print "Got done Sig!", txt
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MCwindow()
window.show()
sys.exit(app.exec_())
The reason why the slot connected to abort_signal doesn't seem to get called, is because cross-thread signals are queued by default. This means the signal will be wrapped as an event and posted to the event queue of whichever thread the receiver is living in.
In your particular example, the receiver is a worker object which has been moved to a worker thread. Calling start() on the worker thread will start its event-loop, and that is where abort_signal will be queued. However, the run() method of the worker object starts a for loop, which will block the thread's event processing in exactly the same way it would if it was executed in the main gui thread!
You can more clearly see what's happening if you make a few adjustments to your example:
class MCwindow(QMainWindow):
abort_signal = pyqtSignal(name='abort_signal')
def __init__(self):
super(MCwindow,self).__init__()
# use a sane default
self.maxIters = 5
...
# DO NOT use QThread.terminate
self.worker.term_signal.connect(self.thread.quit)
Now run the example, and then click the Go button, click the Stop button, and wait for the worker to complete normally. This should produce output like this:
Stopping?!
Got done Sig! Got to 5
stop signalled?
Note that "stop signalled" is output last - i.e. after run() exits and control has returned to the thread's event-loop. In order to process in-coming signals while the worker is running, you will need to force immediate processing of the thread's pending events. This can be done like this:
for step in range(self.maxIters):
QApplication.processEvents()
...
With that in place, you should then see output like this:
Stopping?!
stop signalled?
Got done Sig! Got to 2
Which is presumably what you intended.
Typically a thread will close when it exits the run method. The other way to get a regular python thread to close is by calling it's join method.
For PyQt the join method should either be the quit or terminate method. You should probably still set your end variable to True.
I am making window application in PyQt5 and I want to parse data from XML file in the backgrond and send it to second class. I want to use threads with queue to handle between them. When I want to display window app i see black window. It would be nice to use python threads but i tried to do it on QThread and it is not working too idk why...
This is code example
import queue
import sys
import threading
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import *
class Test_generator:
def __init__(self, queue_obj):
super().__init__()
self.queue_obj = queue_obj
#self.parser_thread = threading.Thread(target=self.parser())
def sender(self):
for _ in range(100):
string ="test"
self.queue_obj.put(string)# send to queue
time.sleep(1)
#print(string)
class Test_collectioner:
def __init__(self,queue_obj):
self.queue_obj = queue_obj
def get_data(self):
collection = []
while self.queue_obj.empty() is False:
print("xd")
print(self.queue_obj.get(), "xd")
#I found this example in the internet(Not working)
class Threaded(QThread):
result = pyqtSignal(int)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
#pyqtSlot(int)
def run(self):
while True:
print("test")
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.GUI()
self.setWindowTitle("PyQt5 app")
global q
q = queue.Queue()
# BLACKSCREEN (for Test_generator)
test_generator_obj = Test_generator(q)
test_collectioner_obj = Test_collectioner(q)
t1 = threading.Thread(target=test_generator_obj.sender())
t2 = threading.Thread(target=test_collectioner_obj.get_data())
t1.start()
t2.start()
# BLACKSCREEN TOO
"""self.thread = QThread()
self.threaded = Threaded()
self.thread.started.connect(self.threaded.run())
self.threaded.moveToThread(self.thread)
qApp.aboutToQuit.connect(self.thread.quit)
self.thread.start()"""
def GUI(self):
self.showMaximized()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
sys.exit(app.exec_())
There are two main problems in your code:
you're executing the methods used for the thread, but you should use the callable object instead; you did the same mistake for the QThread, since connections to signals also expects a callable, but you're actually executing the run function from the main thread (completely blocking everything), since you're using the parentheses; those lines should be like this:
t1 = threading.Thread(target=test_generator_obj.sender)
t2 = threading.Thread(target=test_collectioner_obj.get_data)
or, for the QThread:
self.thread.started.connect(self.threaded.run)
due to the python GIL, multithreading only releases control to the other threads whenever it allows to, but the while cycle you're using prevents that; adding a small sleep function ensures that control is periodically returned to the main thread;
Other problems also exist:
you're already using a subclass of QThread, so there's no use in using another QThread and move your subclass to it;
even assuming that an object is moved to another thread, the slot should not be decorated with arguments, since the started signal of a QThread doesn't have any; also consider that slot decorators are rarely required;
the thread quit() only stops the event loop of the thread, but if run is overridden no event loop is actually started; if you want to stop a thread with a run implementation, a running flag should be used instead; in any other situation, quitting the application is normally enough to stop everything;
Note that if you want to interact with the UI, you can only use QThread and custom signals, and basic python threading doesn't provide a suitable mechanism.
class Threaded(QThread):
result = pyqtSignal(int)
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
def run(self):
self.keepGoing = True
while self.keepGoing:
print("test")
self.msleep(1)
def stop(self):
self.keepGoing = False
class MainWindow(QMainWindow):
def __init__(self):
# ...
self.threaded = Threaded()
self.threaded.start()
qApp.aboutToQuit.connect(self.threaded.stop)
I am building a GUI with a socket connection running in a backround thread in PyQt5. Everything is working pretty well except the Qthread never emits the finished signal. Maybe this isn't a problem and I could change my implementation around to work around it, but is the expected behavior that the Qthread would continue to run once the object that has been moved has stopped doing anything?
Should I write a function in the main class to stop the thread once I'm done with it, or can I just move new things to that thread with no consequences?
class MyClass(PyQt5.QtWidgets.QMainWindow)
def __init__(self, parent=None):
# Setup thread for the socket control
self.simulThread = PyQt5.QtCore.QThread()
self.socketController = SocketController()
self.socketController.moveToThread(self.simulThread)
self.simulThread.started.connect(self.socketController.controlSocket)
self.simulThread.finished.connect(self.deadThread)
# Bind controls to events
self.ui.buttonConnect.clicked.connect(self.simulThread.start)
self.ui.buttonDisconnect.clicked.connect(lambda: self.socketController.stop())
def deadThread(self, data):
print ("THREAD IS DEAD.")
class SocketController(PyQt5.QtCore.QObject):
finished = PyQt5.QtCore.pyqtSignal()
def __init__(self):
super(SocketController, self).__init__()
self.run = True;
def controlSocket(self):
#setup the socket
while self.run:
# do socket stuff
time.sleep(1)
#close the socket
self.finished.emit()
def stop(self):
self.run = False;
A QThread has it's own event loop to handle signals. The controlSocket method blocks this event loop until self.run == False. But this never happens because self.run is only set to False when control is returned to the event lop and it can process the signal that runs the stop method.
You probably want to rearchitect your thread so that instead of having a while loop which blocks the QThread event loop, you construct a QTimer in the thread which calls the #do socket stuff code every 1 second. This way, control is returned to the threads event loop and it can process the stop signal (which would be changed to stop the QTimer from triggering again)
The answer is actually a combination of what three_pineapples and OP posted in their answers:
As noted by three_pineapples, the main problem is that controlSocket(), which gets called by the thread event loop when started signal is emitted, doesn't return until the stop() method gets called. However, in the OP's design, the stop() method can only be called by Qt if the socket thread can process events (since this is how cross-thread signals are dispatched, via the event loops). This is not possible while controlSocket() is busy looping and sleeping.
In order for the thread to exit, its event loop must be stopped.
The first issue must be fixed by either allowing the thread to process events during the loop, or by using a timer instead of a loop. Processing events is shown in this piece of code, based on the OP code:
import time
from PyQt5.QtWidgets import QMainWindow, QWidget, QPushButton, QApplication, QHBoxLayout
from PyQt5.QtCore import QThread, QObject, pyqtSignal
class MyClass(QWidget):
def __init__(self, parent=None):
super().__init__()
self.resize(250, 150)
self.setWindowTitle('Simple')
# Setup thread for the socket control
self.socketController = SocketController()
self.simulThread = QThread()
self.socketController.moveToThread(self.simulThread)
self.simulThread.started.connect(self.socketController.controlSocket)
self.simulThread.finished.connect(self.deadThread)
# Bind controls to events
self.buttonConnect = QPushButton('connect')
self.buttonConnect.clicked.connect(self.simulThread.start)
self.buttonDisconnect = QPushButton('disconnect')
self.buttonDisconnect.clicked.connect(self.socketController.stop)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.buttonConnect)
hbox.addWidget(self.buttonDisconnect)
self.setLayout(hbox)
def deadThread(self, data):
print("THREAD IS DEAD.")
class SocketController(QObject):
finished = pyqtSignal()
def __init__(self):
print('initialized')
super().__init__()
self.run = True
def controlSocket(self):
# setup the socket
print('control socket starting')
while self.run:
# do socket stuff
app.processEvents()
print('control socket iterating')
time.sleep(1)
# close the socket
self.finished.emit()
print('control socket done')
def stop(self):
print('stop pending')
self.run = False
app = QApplication([])
mw = MyClass()
mw.move(300, 300)
mw.show()
app.exec()
The QTimer is a little more complicated, but the idea is to have controlSocket() only do one iteration of the loop, and call it repeatedly via QTimer:
QTimer.singleShot(0, self.controlSocket)
and
def controlSocket(self):
# do socket stuff, then:
if self.run:
QTimer.singleShot(1000, self.controlSocket)
else:
self.finished.emit()
Either of the above approaches fixes the first problem, and allows the SocketController to stop doing its socket-related work.
The second problem is that even after the socket controller is done its job, the thread loop is still running (the two loops are independent). To make the event loop exit, it must be stopped via the thread's quit() method. The quit and the stop should be done together:
class MyClass(QWidget):
def __init__(self, parent=None):
...
self.buttonDisconnect = QPushButton('disconnect')
self.buttonDisconnect.clicked.connect(self.stop)
hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.buttonConnect)
hbox.addWidget(self.buttonDisconnect)
self.setLayout(hbox)
def stop(self):
self.simulThread.quit()
self.socketController.stop()
Ok, so I found A solution, but I don't really know if its correct. Instead of mapping the disconnect button to the socket stop function, I mapped it to function defined in MyClass that calls a new function I wrote called controlDisconnect which also tells the thread explicitly to quit.
# The changed line
self.ui.buttonDisconnect.clicked.connect(self.controlDisconnect)
def controlDisconnect(self):
self.socketController.stop()
self.simulThread.quit()
While this is working (the thread prints out the message that indicates it died), I'm not sure its really good practice. There should probably at least be some code to make sure the socketController has actually stopped before telling the thread to quit.
I have a GUI which needs to perform work that takes some time and I want to show the progress of this work, similar to the following:
import sys
import time
from PyQt4 import QtGui, QtCore
class MyProgress(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
# start loop with signal
self.button = QtGui.QPushButton('loop', self)
self.connect(self.button, QtCore.SIGNAL('clicked()'), self.loop)
# test button
self.test_button = QtGui.QPushButton('test')
self.connect(self.test_button, QtCore.SIGNAL('clicked()'), self.test)
self.pbar = QtGui.QProgressBar(self)
self.pbar.setMinimum(0)
self.pbar.setMaximum(100)
# layout
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.test_button)
vbox.addWidget(self.button)
vbox.addWidget(self.pbar)
self.setLayout(vbox)
self.show()
def update(self):
self.pbar.setValue(self.pbar.value() + 1)
def loop(self):
for step in range(100):
self.update()
print step
time.sleep(1)
def test(self):
if self.test_button.text() == 'test':
self.test_button.setText('ok')
else:
self.test_button.setText('test')
app = QtGui.QApplication(sys.argv)
view = MyProgress()
view.loop() # call loop directly to check whether view is displayed
sys.exit(app.exec_())
When I execute the code the loop method is called and it prints out the values as well as updates the progress bar. However the view widget will be blocked during the execution of loop and although this is fine for my application it doesn't look nice with Ubuntu. So I decided to move the work to a separate thread like this:
import sys
import time
from PyQt4 import QtGui, QtCore
class Worker(QtCore.QObject):
def __init__(self, parent=None):
QtCore.QObject.__init__(self, parent)
def loop(self):
for step in range(10):
print step
time.sleep(1)
class MyProgress(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
# test button
self.test_button = QtGui.QPushButton('test')
self.connect(self.test_button, QtCore.SIGNAL('clicked()'), self.test)
self.pbar = QtGui.QProgressBar(self)
self.pbar.setMinimum(0)
self.pbar.setMaximum(100)
# layout
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.test_button)
vbox.addWidget(self.pbar)
self.setLayout(vbox)
self.show()
def test(self):
if self.test_button.text() == 'test':
self.test_button.setText('ok')
else:
self.test_button.setText('test')
app = QtGui.QApplication(sys.argv)
view = MyProgress()
work = Worker()
thread = QtCore.QThread()
work.moveToThread(thread)
# app.connect(thread, QtCore.SIGNAL('started()'), work.loop) # alternative
thread.start()
work.loop() # not called if thread started() connected to loop
sys.exit(app.exec_())
When I run this version of the script the loop starts running (the steps are displayed in the terminal) but the view widget is not shown. This is the first thing I can't quite follow. Because the only difference from the previous version here is that the loop runs in a different object however the view widget is created before and therefore should be shown (as it was the case for the previous script).
However when I connected the signal started() from thread to the loop function of worker then loop is never executed although I start the thread (in this case I didn't call loop on worker). On the other hand view is shown which makes me think that it depends whether app.exec_() is called or not. However in the 1st version of the script where loop was called on view it showed the widget although it couldn't reach app.exec_().
Does anyone know what happens here and can explain how to execute loop (in a separate thread) without freezing view?
EDIT: If I add a thread.finished.connect(app.exit) the application exits immediately without executing loop. I checked out the 2nd version of this answer which is basically the same what I do. But in both cases it finishes the job immediately without executing the desired method and I can't really spot why.
The example doesn't work because communication between the worker thread and the GUI thread is all one way.
Cross-thread commnunication is usually done with signals, because it is an easy way to ensure that everything is done asynchronously and in a thread-safe manner. Qt does this by wrapping the signals as events, which means that an event-loop must be running for everything to work properly.
To fix your example, use the thread's started signal to tell the worker to start working, and then periodically emit a custom signal from the worker to tell the GUI to update the progress bar:
class Worker(QtCore.QObject):
valueChanged = QtCore.pyqtSignal(int)
def loop(self):
for step in range(0, 10):
print step
time.sleep(1)
self.valueChanged.emit((step + 1) * 10)
...
thread = QtCore.QThread()
work.moveToThread(thread)
thread.started.connect(work.loop)
work.valueChanged.connect(view.pbar.setValue)
thread.start()
sys.exit(app.exec_())
I am trying to use a timer with pyqt. The code is below, but it does not print anything and I do not get an error. Does anyone know what is wrong?
Thanks
import functools
from PyQt4.QtCore import QTimer
def onTimer(initParams):
print initParams
print "HERE"
# your code here...
def update():
print "Upd"
myInitParams = "Init!"
timerCallback = functools.partial(onTimer, myInitParams)
myTimer = QTimer()
myTimer.timeout.connect(timerCallback)
myTimer.start(1000) #once a sec
t = QTimer()
t.start(500)
t.timeout.connect(update)
EDIT UPDATE:
So this is a snip of the full code im using, which is a bit more complicated and maybe is a different problem, but the result is the same.
The structure is like this:
from PyQt4 import QtCore, QtGui
# Import the UI Classes from the ui directory in this package
from ui.MoAnGUI import Ui_MoAnWindow
class MoAnWin(QtGui.QDialog, Ui_MoAnWindow):
def __init__(self, parent=None):
super(MoAnWin, self).__init__(parent=parent)
self.setupUi(self)
self.playMarkers.clicked.connect(self._PlayMarkers)
self.connect(self,QtCore.SIGNAL("PlayMarker"),PlayMarkerCall)
#Marker Stop
self.stopMarkers.clicked.connect(self._StopMarkers)
self.connect(self,QtCore.SIGNAL("StopMarker"),StopMarkerCall)
def _StopMarkers(self):
arg = "Stop"
self.emit(QtCore.SIGNAL("StopMarker"),arg)
def _PlayMarkers(self):
fps = self.fpsBox.value()
starttime = self.fromTime.value()
endtime = self.ToTime.value()
arg = [fps,starttime,endtime]
self.emit(QtCore.SIGNAL("PlayMarker"),arg)
def StopMarkerCall(arg):
#I want to stop the timer here
def PlayMarkerCall(arg):
#I tried this, but again, nothing happens, no error
myInitParams = "Init!"
timerCallback = functools.partial(TimerCall, myInitParams)
myTimer = QtCore.QTimer()
myTimer.timeout.connect(timerCallback)
myTimer.start(1000) #once a sec
#I Imagine something like this:
myTimer = QtCore.QTimer()
for i in range(start,end):
# I want to connect to my view marker function but i need to pass a variable
myTimer.timeout.connect(ViewMarkerCall(i))
myTimer.start(1000)
def TimerCall(args):
print "HERE", args
def show():
global globalMoAnWindow
app = QtGui.QApplication(sys.argv)
globalMoAnWindow = MoAnWin()
globalMoAnWindow.show()
sys.exit(app.exec_())
return globalMoAnWindow
show()
My goal is to have a button click play, and stuff happens in a qtgraphic widget at a certain time interval, then the stop button stops the playing. I found the functools from another question on here, but im not sure if its the correct thing to do.
To correctly use Qt, you need to set up a QtGui.QApplication or QtCore.QCoreApplication and use it's event loop to process events.
This should work:
#from utils import sigint
import functools
from PyQt4.QtCore import QTimer, QCoreApplication
app = QCoreApplication([])
def onTimer(initParams):
print initParams
print "HERE"
# your code here...
def update():
print "Upd"
myInitParams = "Init!"
timerCallback = functools.partial(onTimer, myInitParams)
myTimer = QTimer()
myTimer.timeout.connect(timerCallback)
myTimer.start(1000) #once a sec
t = QTimer()
t.start(500)
t.timeout.connect(update)
# use a timer to stop the event loop after some time
stopTimer = QTimer(timeout=app.quit, singleShot=True)
stopTimer.start(4000)
app.exec_()
Edit:
In your updated version you don't keep any reference to your timer object you create in the PlayMarkerCall method. When myTimer goes out of scope, it is destroyed (together with the underlying C++ Qt object), that's why your timer never fires.
And you can't pass parameters when you connect a signal to a slots, at that time you only set up the connection. Parameters can only be passed when the signal is emitted, to do that you could create a class derived from QTimer and override it's timerEvent method to emit a signal with arguments specified when instantiating the Timer.