I'm in the process of writing my very first GUI application with PyQt4 and I've come upon a question that seems very basic, yet I don't seem to find a good answer:
I'm using a thread to continuously perform a repeated task without blocking the main window. The thread needs some information from the main window (e.g. the current value of a spinbox) that can also change during the runtime of the thread. In this situation, what is the proper way to share such data between the main window and the thread?
Naively, I could come up with the following possibilities:
All three options would most likely work for my particular use case (though 2 would be a bit complicated), but I have a feeling there should be a better/more Pythonic/more Qt-like way.
Here is a minimum working example illustrating what I want to do, in this case using option 1:
from PyQt4 import QtGui, QtCore
import time, sys
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.layout = QtGui.QVBoxLayout(self)
self.spinbox = QtGui.QSpinBox(self)
self.spinbox.setValue(1)
self.layout.addWidget(self.spinbox)
self.output = QtGui.QLCDNumber(self)
self.layout.addWidget(self.output)
self.worker = Worker(self)
self.connect(self.worker, QtCore.SIGNAL('beep'), self.update)
self.worker.start()
def update(self, number):
self.output.display(number)
class Worker(QtCore.QThread):
def __init__(self, host_window):
super(Worker, self).__init__()
self.host = host_window
self.running = False
def run(self):
self.running = True
i = 0
while self.running:
i += 1
self.emit(QtCore.SIGNAL('beep'), i)
sleep_time = self.host.spinbox.value()
time.sleep(sleep_time)
def stop(self):
self.running = False
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
PS: Since I'm completely unexperienced with PyQt it's not unlikely that there are other problems with the code or the question is unclear. In this case, please feel free to comment or edit the question.
Widgets are not thread safe, see Threads and QObjects:
Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread.
And see more definitions here: Reentrancy and Thread-Safety
You should only use widgets in the main thread, and use signal and slots to communicate with other threads.
I don't think a global variable would work, but I honestly don't know why.
How to use signals in this example:
#in main
self.worker = Worker(self.spinbox.value())
self.worker.beep.connect(self.update)
self.spinbox.valueChanged.connect(self.worker.update_value)
class Worker(QtCore.QThread):
beep=QtCore.pyqtSignal(int)
def __init__(self,sleep_time):
super(Worker, self).__init__()
self.running = False
self.sleep_time=sleep_time
def run(self):
self.running = True
i = 0
while self.running:
i += 1
self.beep.emit(i)
time.sleep(self.sleep_time)
def stop(self):
self.running = False
def update_value(self,value):
self.sleep_time=value
NB: I use the new style signal and slots
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With