In this simple example I want to have:
When I run my file, it works fine but shows:
QObject::killTimer: Timers cannot be stopped from another thread
I'm new to PyQT, I have no idea how to fix this.
Does it even make sense to use threading.Thread
, or should I use QThread
for my use-case, or some entirely different code organization?
Is it common to use threading to not block the GUI or am I reinventing the wheel?
from PySide6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton, QLabel
)
from PySide6.QtCore import QTime, QTimer
import threading
import time
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 100)
self.button = QPushButton('Do work', self)
self.button.clicked.connect(self.run_work_thread)
self.timer_label = QLabel('', self) # hide label at the start (but save its space)
layout = QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.timer_label)
self.setLayout(layout)
def run_work_thread(self):
"""Do work in a separate thread, not to block the GUI"""
self.button.setEnabled(False)
thread = threading.Thread(target=self.do_work)
thread.start()
self.start_time = QTime(0, 0, 0)
self.timer = QTimer(self)
self.timer_label.setText('Doing work for: 0 s')
self.timer.timeout.connect(self.update_timer)
self.timer.start(1000) # update every 1 sec
def do_work(self):
time.sleep(2) # Simulate work
self.timer.stop()
self.timer_label.setText('') # clear the timer label
self.button.setEnabled(True)
def update_timer(self):
"""Update the timer, and its label with the elapsed time"""
self.start_time = self.start_time.addMSecs(1000)
self.timer_label.setText(f'Doing work for: {self.start_time.toString("s")} s')
if __name__ == '__main__':
app = QApplication([])
window = MyWindow()
window.show()
app.exec()
Following the @musicamante 's helpful suggestion, here is the solution I did:
from PySide6.QtWidgets import (
QApplication, QWidget, QVBoxLayout, QPushButton, QLabel
)
from PySide6.QtCore import QTime, QTimer, QThread, Signal
import time
class Worker(QThread):
def run(self):
time.sleep(2) # Simulate work
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.resize(200, 100)
self.button = QPushButton('Do work', self)
self.button.clicked.connect(self.run_work)
self.timer_label = QLabel('', self)
layout = QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.timer_label)
self.setLayout(layout)
### ADDED: ###
self.worker = Worker()
self.worker.finished.connect(self.on_work_finished)
##############
def run_work(self):
"""Start the work and the timer"""
self.button.setEnabled(False)
self.start_time = QTime(0, 0, 0)
self.timer = QTimer(self)
self.timer_label.setText('Doing work for: 0 s')
self.timer.timeout.connect(self.update_timer)
self.timer.start(1000) # update every 1 sec
self.worker.start()
def on_work_finished(self):
"""Stop the timer and reset the UI when work is done"""
self.timer.stop()
self.timer_label.setText('')
self.button.setEnabled(True)
def update_timer(self):
"""Update the timer and its label with the elapsed time"""
self.start_time = self.start_time.addSecs(1)
self.timer_label.setText(f'Doing work for: {self.start_time.toString("s")} s')
if __name__ == '__main__':
app = QApplication([])
window = MyWindow()
window.show()
app.exec()