I must say it is a beginner's question. But I tried/searched a lot without success.
There is a QLabel
, by running the code I want to have the values from a sub-thread shown onto it.
Also I want to have the QTimer
in the sub-thread, because the time is controlled there(not in the main QThread
).
This is the effect on the QLable
I want to achieve...
0 (show for 1 second)
1 (show for 1 second)
2 (show for 1 second)
...
11 (show for 2 second)
12 (show for 2 second)
...
21 (show for 3 second)
22 (show for 3 second)
...
Here is my code:
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
self._running = False
self.timer = QTimer() # Question: define the QTimer here?
self.timer.timeout.connect(self.doWork)
self.timer.start(1000)
def run(self):
self._running = True
while self._running:
self.doWork()
def doWork(self):
for i in range(40): # Question: How to return the i onto the QLable?
if i <= 10:
# show the value of i on the QLabel and wait for 1 second
pass
elif i <= 20:
# show the value of i on the QLable and wait for 2 second
pass
elif i <= 30:
# show the value of i on the QLable and wait for 3 second
pass
else:
# show the value of i on the QLable and wait for 4 second
pass
def stop(self, wait = False):
self._running = False
if wait:
self.wait()
class MyApp(QWidget):
def __init__(self, parent= None):
super(MyApp, self).__init__(parent)
self.thread = WorkerThread()
self.initUI()
def initUI(self):
self.text = "hello world"
self.setGeometry(100, 100, 500, 100)
self.setWindowTitle('test')
self.lbl_1 = QLabel("for every number between 0-10 wait 1 second, for 11-20 wait 2 sec, ...", self)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
In summary there are two questions:
1. How to define the QTimer
in the sub-thread?
2. How to return the value from the sub-thread to QLabel
?
Thank you for the help!
It is not necessary to use a QTimer
to perform the task you need, in a thread you can have blocking elements such as QThead::sleep()
that generates a pause. To send the information we can use the parent()
to pass the GUI and combining it with QMetaObject::invokeMethod()
we can update the label respecting the rules of Qt:
class WorkerThread(QThread):
[...]
def doWork(self):
for i in range(40): # Question: How to return the i onto the QLable?
if i <= 10:
QThread.sleep(1)
elif i <= 20:
QThread.sleep(2)
elif i <= 30:
QThread.sleep(3)
else:
QThread.sleep(4)
gui = self.parent()
QMetaObject.invokeMethod(gui.lbl_1, "setText", Qt.QueuedConnection,
Q_ARG(str, str(i)))
[...]
class MyApp(QWidget):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.thread = WorkerThread(self) # passing the window as a parent
self.thread.start()
self.initUI()
def initUI(self):
[...]
We can also use QTimer
together with QEventLoop
to generate the same effect as QThread::sleep()
:
def doWork(self):
for i in range(40): # Question: How to return the i onto the QLable?
if i <= 10:
interval = 1000
elif i <= 20:
interval = 2000
elif i <= 30:
interval = 3000
else:
interval = 4000
loop = QEventLoop()
QTimer.singleShot(interval, loop.quit)
loop.exec_()
gui = self.parent()
QMetaObject.invokeMethod(gui.lbl_1, "setText", Qt.QueuedConnection,
Q_ARG(str, str(i)))
Instead of using QMetaObject::invokeMethod
we can use signals:
class WorkerThread(QThread):
numberChanged = pyqtSignal(str)
def __init__(self, parent=None):
[...]
def doWork(self):
for i in range(40): # Question: How to return the i onto the QLable?
if i <= 10:
interval = 1000
elif i <= 20:
interval = 2000
elif i <= 30:
interval = 3000
else:
interval = 4000
loop = QEventLoop()
QTimer.singleShot(interval, loop.quit)
loop.exec_()
self.numberChanged.emit(str(i))
[...]
class MyApp(QWidget):
def __init__(self, parent=None):
super(MyApp, self).__init__(parent)
self.thread = WorkerThread(self)
self.thread.start()
self.initUI()
def initUI(self):
self.text = "hello world"
self.setGeometry(100, 100, 500, 100)
self.setWindowTitle('test')
self.lbl_1 = QLabel("for every number between 0-10 wait 1 second, for 11-20 wait 2 sec, ...", self)
self.thread.numberChanged.connect(self.lbl_1.setText, Qt.QueuedConnection)
self.show()