Pyqt5 stop QThread worker on QAction - python-3.x

hello I have QTheard worker that start with click at QAction.
....
self.start_update = QAction('&Start', self)
self.start_update.triggered.connect(self._start_thread)
self.stop_update.setVisible(True)
self.stop_update.triggered.connect(self._stop_thread)
self.stop_update.setVisible(False)
...
...
def _start_thread(self):
self.start_update.setVisible(False)
self.stop_update.setVisible(True)
self.myworker.start()
...
...
def _stop_thread(self):
self.myworker.stop() # That close app with error
self.stop_update.setVisible(False)
self.start_update.setVisible(True)
...
Please help how to close terminate Worker correctly. Atm worker have __del__ and run (logic) method. Maybe need to add some method for correct worker closing?

Your application might look like this:
import sys
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class AThread(QThread):
threadSignalAThread = pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self):
count = 0
while count < 1000:
QThread.msleep(200)
count += 1
self.threadSignalAThread.emit(count)
class MsgBoxAThread(QDialog):
def __init__(self):
super().__init__()
layout = QVBoxLayout(self)
self.label = QLabel("")
layout.addWidget(self.label)
close_btn = QPushButton("Close window")
layout.addWidget(close_btn)
close_btn.clicked.connect(self.close)
self.setGeometry(900, 300, 400, 80)
self.setWindowTitle('MsgBox AThread(QThread)')
class Example(QMainWindow):
def __init__(self, parent=None, *args):
super().__init__(parent, *args)
self.setWindowTitle("Pyqt5 stop QThread worker on QAction")
self.setGeometry(550, 300, 300, 300)
centralWidget = QWidget(self)
self.setCentralWidget(centralWidget)
layout = QVBoxLayout(centralWidget)
self.lbl = QLabel("Start")
layout.addWidget(self.lbl)
bar = self.menuBar()
barThread = bar.addMenu('Thread')
quit = bar.addMenu('Quit')
quit.aboutToShow.connect(app.quit)
self.start_update = QAction('&Start', self)
self.start_update.setShortcut('Ctrl+S')
self.start_update.triggered.connect(self._start_thread)
self.stop_update = QAction('Sto&p', self)
self.stop_update.setShortcut('Ctrl+P')
self.stop_update.setVisible(False)
self.stop_update.triggered.connect(self._stop_thread)
barThread.addAction(self.start_update)
barThread.addAction(self.stop_update)
self.msg = MsgBoxAThread()
self.myworker = None
self.counter = 0
self.timer = QTimer()
self.timer.setInterval(1000)
self.timer.timeout.connect(self.recurring_timer)
self.timer.start()
self.show()
def recurring_timer(self):
self.counter += 1
self.lbl.setText(" Do something in the GUI: <b> %d </b>" % self.counter)
def _start_thread(self):
self.start_update.setVisible(False)
self.stop_update.setVisible(True)
self.myworker = AThread()
self.myworker.threadSignalAThread.connect(self.on_threadSignalAThread)
self.myworker.finished.connect(self.finishedAThread)
self.myworker.start()
def finishedAThread(self):
self.myworker = None
self.start_update.setVisible(True)
self.stop_update.setVisible(False)
def on_threadSignalAThread(self, value):
self.msg.label.setText(str(value))
if not self.msg.isVisible():
self.msg.show()
def _stop_thread(self):
self.stop_update.setVisible(False)
self.start_update.setVisible(True)
self.myworker.terminate()
self.myworker = None
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Question',
"Are you sure you want to close the application?",
QMessageBox.Yes,
QMessageBox.No)
if reply == QMessageBox.Yes:
if self.myworker:
self.myworker.quit()
del self.myworker
self.msg.close()
super(Example, self).closeEvent(event)
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
#ex.show()
sys.exit(app.exec_())

Related

PyQt Progress Bar Update using Thread

I've been writing a program that used a MyWindow(QTableWidget) with a thread. A progress bar is displayed above the sub-window(self.win) displayed as a pop-up.
I want a green bar on the status bar to be displayed consecutively, however after resetting the Spyder-kernel, the green bar does not output continuously. And I want to run the 'stop'/'continue' alternately every time I click the push button. This hasn't been resolved for almost three days.
import sys, time
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QThread, pyqtSignal, pyqtSlot, QRect
from PyQt5.QtWidgets import *
progVal = 0
class thread(QThread):
signalTh = pyqtSignal(int)
def __init__(self, *args):
super().__init__()
self.flag = True
def run(self):
global progVal
if self.flag:
self.signalTh.emit(progVal)
time.sleep(0.1)
def stop(self):
self.flag = False
self.quit()
self.wait(2)
class MyWindow(QTableWidget):
def __init__(self):
global progVal
super().__init__()
self.setupUi()
self.show()
self.test = thread(None)
self.test.signalTh.connect(self.signal_function)
self.test.run()
self.saveData()
def saveData(self):
global progVal
counts = range(1, 51)
for row in counts:
progVal = int(row/len(counts)*100)
self.test.signalTh.emit(progVal)
time.sleep(0.1)
def click1_function(self):
if self.test.flag:
self.test.stop()
self.pb_start.setText('Start!')
else:
self.test.flag = True
self.test.run()
self.pb_start.setText('Stop!')
#pyqtSlot(int)
def signal_function(self, val):
self.progress.setValue(val)
self.progress.update()
self.win.update()
self.update()
def setupUi(self):
self.resize(500, 400)
self.pb_start = QPushButton(self)
self.pb_start.setGeometry(QRect(80, 20, 100, 50))
self.pb_start.setText("Start")
self.pb_start.clicked.connect(self.click1_function)
self.win = QDialog(self)
self.win.resize(330, 100)
self.progress = QProgressBar(self.win)
self.progress.setGeometry(10, 10, 300, 30)
self.progress.setMaximum(100)
self.win.show()
def closeEvent(self, event):
quit_msg = "Are you sure you want to exit the program?"
reply = QMessageBox.question(self, 'Message', quit_msg, QMessageBox.Yes, QMessageBox.No)
if reply == QMessageBox.Yes:
self.test.stop()
event.accept()
else:
event.ignore()
if __name__ == "__main__":
app = QApplication(sys.argv)
myApp = MyWindow()
myApp.show()
app.exec_()

Catch final signal from QTimer object

I have a simple PyQt script. When I click a button, it starts a QTimer object and increments a progress bar. What I want is to change the label of my text when my progress bar reaches 100%. It worked for me once, but I can't get it to work anymore. What am I doing wrong?
Here's the main part of my code.
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QProgressBar demo')
self.timerButton = QPushButton("Start", self)
self.timerButton.clicked.connect(self.timerStart)
self.timerObject = QTimer(self)
#self.timerObject.destroyed.connect(lambda:self.timerButton.setText("Finished") )
self.timerObject.destroyed.connect(lambda:print("Called" ))
self.progressBar = QProgressBar(self)
self.progressBar.setGeometry(10, 20, 290, 25)
self.timerButton.move(110,150)
self.progressBar.move(10,100)
self.increment = 0
self.resize(300, 300)
self.show()
#pyqtSlot()
def headsUp(self):
if(self.increment >= 100):
self.timerObject.stop()
else:
self.increment += 1
self.progressBar.setValue(self.increment)
return
def timerStart(self):
if (self.timerObject.isActive()):
self.timerObject.stop()
self.timerButton.setText("Resume")
else:
self.timerObject.timeout.connect(self.headsUp)
self.timerButton.setText("Pause")
self.timerObject.start(100)
destroyed is only issued when you delete the object, that a QTimer peer does not imply that it is deleted from memory, therefore it does not emit that signal, a possible solution is to create a signal for the QProgressBar when the value takes the maximum value as shown below:
import sys
from PyQt5 import QtCore, QtWidgets
class ProgressBar(QtWidgets.QProgressBar):
finished = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super(ProgressBar, self).__init__(*args, *kwargs)
self.valueChanged.connect(self.on_valueChanged)
#QtCore.pyqtSlot(int)
def on_valueChanged(self, val):
if val == self.maximum():
self.finished.emit()
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QProgressBar demo')
self.timerButton = QtWidgets.QPushButton("Start", self)
self.timerButton.clicked.connect(self.timerStart)
self.timerObject = QtCore.QTimer(self)
self.progressBar = ProgressBar(self)
self.progressBar.finished.connect(lambda: print("Called" ))
self.increment = 0
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.progressBar)
lay.addWidget(self.timerButton)
self.resize(300, 300)
#QtCore.pyqtSlot()
def headsUp(self):
if self.increment >= 100:
self.timerObject.stop()
else:
self.increment += 1
self.progressBar.setValue(self.increment)
#QtCore.pyqtSlot()
def timerStart(self):
if self.timerObject.isActive():
self.timerObject.stop()
self.timerButton.setText("Resume")
else:
self.timerObject.timeout.connect(self.headsUp)
self.timerButton.setText("Pause")
self.timerObject.start(100)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
Another best option is to use QTimeLine and its finished signal:
import sys
from PyQt5 import QtCore, QtWidgets
class MainWindow(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.setWindowTitle('QProgressBar demo')
self.timerButton = QtWidgets.QPushButton("Start", self)
self.timerButton.clicked.connect(self.timerStart)
self.timerObject = QtCore.QTimeLine(1000, self)
self.timerObject.setFrameRange(0, 100)
self.progressBar = QtWidgets.QProgressBar(self)
self.timerObject.frameChanged.connect(self.progressBar.setValue)
self.timerObject.finished.connect(lambda: print("Called" ))
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.progressBar)
lay.addWidget(self.timerButton)
self.resize(300, 300)
#QtCore.pyqtSlot()
def timerStart(self):
if self.timerObject.state() == QtCore.QTimeLine.Running:
self.timerObject.stop()
self.timerButton.setText("Resume")
else:
self.timerButton.setText("Pause")
self.timerObject.resume()
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
This isn't working because you are connecting to the timers destoryed signal, but the timer is not being destroyed. To use this code as is, call self.timerObject.deleteLater() after you stop the timer.

Reset QProgressBar for another run

I have a ProgressBar for downloading data.
Sometimes the task can timeout and if so I want to be able to restart the process.
Task is started
Timer is started
if the timer finishes, end the task
press the button to restart the task
All of the above works fine except for the progressbar does not start again.
I can reset it to 0 but it will not move (before that it was stuck at 100%!)
If the task finishes before the timer runs out, the progress bar works fine again.
How can I restart the progressbar if the timer runs out before the task?
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QProgressBar, QPushButton
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class DownloadThread(QThread):
taskFinished = pyqtSignal()
def __init__(self, parent=None):
super(DownloadThread, self).__init__(parent)
def run(self):
for i in range(20000):
print(i)
self.taskFinished.emit()
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# Set Progress Bard
self.progressBar = QProgressBar(self)
self.progressBar.setGeometry(30, 40, 200, 25)
# Button
self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(self.start_download_thread)
# Set Timer
self.timer = QBasicTimer()
self.step = 0
# Display
self.setGeometry(300, 300, 280, 170)
self.show()
def start_download_thread(self):
# Set Progress bar to 0%
self.progressBar.setValue(0)
#Start Thread
self.Download = DownloadThread()
self.Download.taskFinished.connect(self.onDownloaded)
self.onDownloading()
self.Download.start()
def onDownloading(self):
#start the timer
self.progressBar.show()
self.timerSwitch()
def timerSwitch(self):
# Turn timer off or on
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(2, self)
def onDownloaded(self):
# Stop the timer
self.timerSwitch()
# progress bar 100%
self.progressBar.setValue(1)
self.progressBar.setRange(0, 1)
def timerEvent(self, e):
if self.step >= 100:
self.timer.stop()
# self.Download.quit()
return
self.step = self.step + 1
self.progressBar.setValue(self.step)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
I solved this by moving the Qtimer() step into the start_download_thread
def start_download_thread(self):
self.step = 0 # reset the steps to 0
This means it resets the steps and the progress bar starts again.
import sys
from PyQt5.QtWidgets import QWidget, QApplication, QProgressBar, QPushButton
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class DownloadThread(QThread):
taskFinished = pyqtSignal()
def __init__(self, parent=None):
super(DownloadThread, self).__init__(parent)
def run(self):
for i in range(20000):
print(i)
self.taskFinished.emit()
class Example(QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
# Set Progress Bard
self.progressBar = QProgressBar(self)
self.progressBar.setGeometry(30, 40, 200, 25)
# Button
self.btn = QPushButton('Start', self)
self.btn.move(40, 80)
self.btn.clicked.connect(self.start_download_thread)
# Set Timer
self.timer = QBasicTimer()
# Display
self.setGeometry(300, 300, 280, 170)
self.show()
def start_download_thread(self):
self.step = 0
# Set Progress bar to 0%
self.progressBar.setValue(0)
#Start Thread
self.Download = DownloadThread()
self.Download.taskFinished.connect(self.onDownloaded)
self.onDownloading()
self.Download.start()
def onDownloading(self):
#start the timer
self.progressBar.show()
self.timerSwitch()
def timerSwitch(self):
# Turn timer off or on
if self.timer.isActive():
self.timer.stop()
else:
self.timer.start(2, self)
def onDownloaded(self):
# Stop the timer
self.timerSwitch()
# progress bar 100%
self.progressBar.setValue(100)
self.progressBar.setRange(0, 100)
def timerEvent(self, e):
if self.step >= 100:
self.timer.stop()
return
self.step = self.step + 1
self.progressBar.setValue(self.step)
def main():
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Fading on Tab Change in pyqt

i have a page containing two tabs.i want to add a fadeIn effect when i change the tabs.Is that possible?
import sys
from PyQt4.QtCore import QTimeLine
from PyQt4.QtGui import *
class FaderWidget(QWidget):
def __init__(self, old_widget, new_widget):
QWidget.__init__(self, new_widget)
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.pixmap_opacity = 1.0
self.timeline = QTimeLine()
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
self.timeline.setDuration(333)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
painter.end()
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.repaint()
class StackedWidget(QStackedWidget):
def __init__(self, parent = None):
QStackedWidget.__init__(self, parent)
def setCurrentIndex(self, index):
self.fader_widget = FaderWidget(self.currentWidget(), self.widget(index))
QStackedWidget.setCurrentIndex(self, index)
def setPage1(self):
self.setCurrentIndex(0)
def setPage2(self):
self.setCurrentIndex(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
stack = StackedWidget()
stack.addWidget(QCalendarWidget())
editor = QTextEdit()
editor.setPlainText("Hello world! "*100)
stack.addWidget(editor)
page1Button = QPushButton("Page 1")
page2Button = QPushButton("Page 2")
page1Button.clicked.connect(stack.setPage1)
page2Button.clicked.connect(stack.setPage2)
layout = QGridLayout(window)
layout.addWidget(stack, 0, 0, 1, 2)
layout.addWidget(page1Button, 1, 0)
layout.addWidget(page2Button, 1, 1)
window.show()
sys.exit(app.exec_())
this is code that shows some fade effect but i m getting nothing from it and how it works and how to implement on tabs. it will be really appreciable if someone could help me implement it on tabs as well.
thanks in advance.
With the same logic as the code you show, each widget will be placed inside a QStackedWidget, where one of them will be the widget that will be displayed and the other will be the FaderWidget.
class FaderWidget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
self.pixmap_opacity = None
self.timeline = QTimeLine(333, self)
self.timeline.valueChanged.connect(self.animate)
self.timeline.finished.connect(self.close)
def start(self, old_widget, new_widget):
self.pixmap_opacity = 1.0
self.old_pixmap = QPixmap(new_widget.size())
old_widget.render(self.old_pixmap)
self.timeline.start()
self.resize(new_widget.size())
self.show()
def paintEvent(self, event):
if self.pixmap_opacity:
QWidget.paintEvent(self, event)
painter = QPainter(self)
painter.setOpacity(self.pixmap_opacity)
painter.drawPixmap(0, 0, self.old_pixmap)
def animate(self, value):
self.pixmap_opacity = 1.0 - value
self.update()
class FaderTabWidget(QTabWidget):
def __init__(self, parent=None):
QTabWidget.__init__(self, parent)
self.currentChanged.connect(self.onCurrentIndex)
self.last = -1
self.current = self.currentIndex()
def onCurrentIndex(self, index):
self.last = self.current
self.current = self.currentIndex()
if self.widget(self.last):
self.widget(self.last).setCurrentIndex(1)
old_widget = self.widget(self.last).widget(0)
current_widget = self.widget(self.current).widget(0)
fade = self.widget(self.current).widget(1)
fade.start(old_widget, current_widget)
def addTab(self, widget, text):
stack = QStackedWidget(self)
stack.addWidget(widget)
fade = FaderWidget(self)
fade.timeline.finished.connect(lambda: stack.setCurrentIndex(0))
stack.addWidget(fade)
stack.setCurrentIndex(0 if self.currentIndex() == -1 else 1)
QTabWidget.addTab(self, stack, text)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = QWidget()
tabWidget = FaderTabWidget()
tabWidget.addTab(QCalendarWidget(), "Tab1")
editor = QTextEdit()
editor.setPlainText("Hello world! " * 100)
tabWidget.addTab(editor, "Tab2")
layout = QVBoxLayout(window)
layout.addWidget(tabWidget)
window.show()
sys.exit(app.exec_())

PySide QThread moveToThread subclass locks up main thread

I'm having an issue when I try to subclass my worker class the main UI thread locks up and becomes unresponsive until the Worker Thread has finished. I'm at a loss here and suspect something with how the GIL interacts with inheritance?
In MainWindow.clicked() these two lines:
#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread
Here is my sample code that reproduces this:
from PySide import QtGui, QtCore
import sys
import time
class Worker(QtCore.QObject):
started = QtCore.Signal()
progress = QtCore.Signal(int)
finished = QtCore.Signal(bool)
def __init__(self, parent=None):
QtCore.QObject.__init__(self, parent)
self.lock = QtCore.QMutex()
#QtCore.Slot()
def start(self):
self.flag_to_quit = False
try:
self.started.emit()
time.sleep(5)
time.sleep(0.5)
for i in range(10):
self.progress.emit(i)
time.sleep(1)
with QtCore.QMutexLocker(self.lock):
print(self.flag_to_quit)
if self.flag_to_quit:
raise RuntimeError("Requested to quit.")
self.finished.emit(True)
except Exception as ex:
print(ex)
self.finished.emit(False)
finally:
print("start() finished!")
##QtCore.Slot()
def stop(self):
print("Stopping...")
with QtCore.QMutexLocker(self.lock):
self.flag_to_quit = True
#QtCore.Slot()
def __quit_handler(self):
self.flag_to_quit = True
class SubWorker(Worker):
def __init__(self):
Worker.__init__(self)
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.label = QtGui.QLabel("")
self.layout().addWidget(self.label)
self.button = QtGui.QPushButton("Start Thread")
self.layout().addWidget(self.button)
self.button.clicked.connect(self.clicked)
def closeEvent(self, event):
if hasattr(self, "_thread") and self._thread:
self.worker.flag_to_quit = True
self._thread.quit()
print("waiting for stop...")
self._thread.wait()
self._thread.deleteLater()
#QtCore.Slot()
def worker_started(self):
self.label.setText("Started!")
#QtCore.Slot(int)
def worker_progress(self, interval):
self.label.setText(str(interval))
#QtCore.Slot()
def worker_finished(self, success):
msg = "Finished "
msg += "successfully!" if success else "with errors"
self.label.setText(msg)
#QtCore.Slot()
def clicked(self):
if hasattr(self, "_thread") and self._thread:
self.worker.stop()
self._thread.quit()
print("waiting for stop...")
self._thread.wait()
self._thread.quit()
self._thread.deleteLater()
self._thread = QtCore.QThread()
#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread
self.worker.started.connect (self.worker_started, QtCore.Qt.QueuedConnection)
self.worker.progress.connect(self.worker_progress, QtCore.Qt.QueuedConnection)
self.worker.finished.connect(self.worker_finished, QtCore.Qt.QueuedConnection)
self.worker.moveToThread(self._thread)
self._thread.started.connect(self.worker.start)
self._thread.finished.connect(self.worker.stop)
self._thread.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
sys.exit()
Version information:
Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 19:28:18) [MSC v.1600 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
edit:
Looks like QThread::moveToThread() is failing with Subclasses. this is output of QtCore.QThread.currentThread() in various places:
#self.worker = Worker():
Main:<PySide.QtCore.QThread object at 0x7ff89df384d0>
Real thread: <PySide.QtCore.QThread object at 0x7ff89df383f8>
Worker:<PySide.QtCore.QThread object at 0x7ff89df384d0>
#self.worker = SubWorker()
Main:<PySide.QtCore.QThread object at 0x7f968602c3b0>
Real thread: <PySide.QtCore.QThread object at 0x7f968602c488>
Worker:<PySide.QtCore.QThread object at 0x7f968602c488>
Okay I managed to fix this by doing it the "Wrong" way by inheriting QThread and overriding the run method. I'm not sure why or how but it works.
from PySide import QtGui, QtCore
import sys
import time
class Worker(QtCore.QThread):
_started = QtCore.Signal()
progress = QtCore.Signal(int)
_finished = QtCore.Signal(bool)
def __init__(self, parent=None):
QtCore.QThread.__init__(self, parent)
self.lock = QtCore.QMutex()
#QtCore.Slot()
def run(self):
self.flag_to_quit = False
print("Worker:" + str(QtCore.QThread.currentThread()))
try:
self._started.emit()
time.sleep(2)
for i in range(10):
self.progress.emit(i)
time.sleep(1)
with QtCore.QMutexLocker(self.lock):
if self.flag_to_quit:
raise RuntimeError("Requested to quit.")
self._finished.emit(True)
except Exception as ex:
print(ex)
self._finished.emit(False)
finally:
print("start() finished!")
#QtCore.Slot()
def stop(self):
with QtCore.QMutexLocker(self.lock):
self.flag_to_quit = True
#QtCore.Slot()
def __quit_handler(self):
self.flag_to_quit = True
class SubWorker(Worker):
def __init__(self, parent=None):
Worker.__init__(self, parent)
#QtCore.Slot()
def start(self):
Worker.start(self)
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setLayout(QtGui.QVBoxLayout())
self.label = QtGui.QLabel("")
self.layout().addWidget(self.label)
self.button = QtGui.QPushButton("Start Thread")
self.layout().addWidget(self.button)
self.button.clicked.connect(self.clicked)
def closeEvent(self, event):
self.hide()
if hasattr(self, "worker") and self.worker:
self.worker.stop()
self.worker.quit()
self.worker.wait()
self.worker.deleteLater()
#QtCore.Slot()
def worker_started(self):
self.label.setText("Started!")
#QtCore.Slot(int)
def worker_progress(self, interval):
self.label.setText(str(interval))
#QtCore.Slot()
def worker_finished(self, success):
msg = "Finished "
msg += "successfully!" if success else "with errors!"
self.label.setText(msg)
def clicked(self):
if hasattr(self, "worker") and self.worker:
self.worker.stop()
self.worker.quit()
self.worker.wait()
self.worker.deleteLater()
print("Main:" + str(QtCore.QThread.currentThread()))
#self.worker = Worker() # This works
self.worker = SubWorker() # This locks up main thread
self.worker._started.connect (self.worker_started)
self.worker.progress.connect(self.worker_progress)
self.worker._finished.connect(self.worker_finished)
self.worker.start()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
app.exec_()
sys.exit()

Resources