How to interrupt a script execution on a QThread in Python PyQt? - python

What I'm Doing:
I'm making a PyQt app that allows users to select a script file from their machine, the app then executes it using exec() on a separate QThread then shows them the results. I've already implemented all that, and now I'm trying to add a "Stop Executing" button.
The Problem:
I'm not able to interrupt the script execution, which should happen whenever the user presses the "Stop Executing" button. I can't stop the QObject's task that's executing the script or terminate the QThread that's hosting the object.
My Code:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
class Execute(QObject):
def __init__(self, script):
super().__init__()
self.script = script
def run(self):
exec(open(self.script).read())
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.thread = QThread()
self.test = Execute(user_script)
self.test.moveToThread(self.thread)
self.thread.started.connect(self.test.run)
self.thread.start()
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
# Somehow stop script execution
My Attempts:
There's a ton of question related to stopping an exec() or a QThread, but none of them work in my case. Here's what I've tried:
Calling thread.quit() from GUI (kills thread after script execution ends - same with wait())
Raising a SystemExit from object (exits the whole app after script execution ends)
Calling thread.terminate() from GUI (app crashes when "Stop Executing" button is pressed)
Using a termination flag variable (not applicable in my case as run() isn't loop based)
So, is there any other solution to stop the exec() or kill the thread right when the button is pressed?

Thank's to #ekhumoro's hint about using multiprocessing instead of multithreading, I was able to find a solution.
I used a QProcess to execute the script, and then called process.kill() when the "Stop Executing" button is clicked. Like so:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QProcess
class GUI(QMainWindow):
# Lots of irrelevant code here ...
# Called when "Start Executing" button is pressed
def startExecuting(self, user_script):
self.process = QProcess()
self.process.setProcessChannelMode(QProcess.MergedChannels)
self.process.start("python", ["-u", user_script])
# Called when "Stop Executing" button is pressed
def stopExecuting(self):
self.process.kill()
This stops the script execution right away without interrupting the GUI process, which's exactly what I was looking for.

Check next code, maybe it can help you:
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import QObject, QThread
from PyQt5 import Qt #+
class WorkThread(Qt.QThread):
threadSignal = Qt.pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self, *args, **kwargs):
c = 0
while True:
Qt.QThread.msleep(100)
c += 1
self.threadSignal.emit(c)
class MsgBox(Qt.QDialog):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.label = Qt.QLabel("")
layout.addWidget(self.label)
close_btn = Qt.QPushButton("Close")
layout.addWidget(close_btn)
close_btn.clicked.connect(self.close)
self.setGeometry(900, 65, 400, 80)
self.setWindowTitle('MsgBox from WorkThread')
class GUI(Qt.QWidget): #(QMainWindow):
def __init__(self):
super().__init__()
layout = Qt.QVBoxLayout(self)
self.btn = Qt.QPushButton("Start thread.")
layout.addWidget(self.btn)
self.btn.clicked.connect(self.startExecuting)
self.msg = MsgBox()
self.thread = None
# Lots of irrelevant code here ...
# Called when "Start/Stop Executing" button is pressed
def startExecuting(self, user_script):
if self.thread is None:
self.thread = WorkThread()
self.thread.threadSignal.connect(self.on_threadSignal)
self.thread.start()
self.btn.setText("Stop thread")
else:
self.thread.terminate()
self.thread = None
self.btn.setText("Start thread")
def on_threadSignal(self, value):
self.msg.label.setText(str(value))
if not self.msg.isVisible():
self.msg.show()
if __name__ == '__main__':
app = Qt.QApplication([])
mw = GUI()
mw.show()
app.exec()

Related

Problems getting QTimer to start when using QThread

I'm trying to implement a webcam using PyQt5 from an example I found (here, but not really relevant).
Getting the example to work wasn't an issue, but I wanted to modify some things and I am stuck on one particular problem.
I have two classes, one QObject Capture which has a QBasicTimer that I want to start, and a QWidget MyWidget with a button that is supposed to start the timer of the Capture object, which is inside a QThread.
If I directly connect the button click to the method that starts the timer, everything works fine.
But I want to do some other things when I click the button, so I connected the button to a method of MyWidget first and call the start method of Capture from there. This, however, doesn't work: the timer doesn't start.
Here is a minimal working example:
from PyQt5 import QtCore, QtWidgets
import sys
class Capture(QtCore.QObject):
def __init__(self, parent=None):
super(Capture, self).__init__(parent)
self.m_timer = QtCore.QBasicTimer()
def start(self):
print("capture start called")
self.m_timer.start(1000, self)
def timerEvent(self, event):
print("time event")
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
self.btn_start = QtWidgets.QPushButton("Start")
lay.addWidget(self.btn_start)
self.capture = Capture()
captureThread = QtCore.QThread(self)
captureThread.start()
self.capture.moveToThread(captureThread)
# self.btn_start.clicked.connect(self.capture.start) # this works
self.btn_start.clicked.connect(self.startCapture) # this doesn't
# self.capture.start() # this doesn't either
self.show()
def startCapture(self):
self.capture.start()
def run_app():
app = QtWidgets.QApplication(sys.argv)
mainWin = MyWidget()
mainWin.show()
app.exec_()
run_app()
It is some problem with the QThread, because if I don't use threading it works. I thought maybe it has something to do with the thread not being in some way available when called from a different method than the one it was created in, but calling self.capture.start() directly from the init does not work either.
I only have a very basic grasp of threads. Can someone tell me how I can properly call self.capture.start() from MyWidget and why it works without problems when directly connecting it to the button click?
If you connect the button's clicked signal to the worker's start slot, Qt will automatically detect that it's a cross-thread connection. When the signal is eventually emitted, it will be queued in the receiving thread's event-queue, which ensures the slot will be called within the worker thread.
However, if you connect the button's clicked signal to the startCapture slot, there's no cross-thread connection, because the slot belongs to MyWidget (which lives in the main thread). When the signal is emitted this time, the slot tries to create the timer from within the main thread, which is not supported. Timers must always be started within the thread that creates them (otherwise Qt will print a message like "QBasicTimer::start: Timers cannot be started from another thread").
A better approach is to connect the started and finished signals of the thread to some start and stop slots in the worker, and then call the thread's start and quit methods to control the worker. Here's a demo based on your script, which shows how to implement that:
from PyQt5 import QtCore, QtWidgets
import sys
class Capture(QtCore.QObject):
def __init__(self, parent=None):
super(Capture, self).__init__(parent)
self.m_timer = QtCore.QBasicTimer()
def start(self):
print("capture start called")
self.m_timer.start(1000, self)
def stop(self):
print("capture stop called")
self.m_timer.stop()
def timerEvent(self, event):
print("time event")
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
lay = QtWidgets.QVBoxLayout(self)
self.btn_start = QtWidgets.QPushButton("Start")
lay.addWidget(self.btn_start)
self.capture = Capture()
self.captureThread = QtCore.QThread(self)
self.capture.moveToThread(self.captureThread)
self.captureThread.started.connect(self.capture.start)
self.captureThread.finished.connect(self.capture.stop)
self.btn_start.clicked.connect(self.startCapture)
self.show()
def startCapture(self):
if not self.captureThread.isRunning():
self.btn_start.setText('Stop')
self.captureThread.start()
else:
self.btn_start.setText('Start')
self.stopCapture()
def stopCapture(self):
self.captureThread.quit()
self.captureThread.wait()
def closeEvent(self, event):
self.stopCapture()
def run_app():
app = QtWidgets.QApplication(sys.argv)
mainWin = MyWidget()
mainWin.show()
app.exec_()
run_app()

Why are my QThreads consistently crashing Maya?

I have a UI that I am wanting to use threading with inside of Maya. The reason for doing this is so I can run Maya.cmds without hanging/freezing the UI while updating the UI with progress bars, etc.
I have read a few examples from StackOverflow but my code is crashing every second time I run it. Examples I have followed are here and here
import maya.cmds as cmds
from PySide2 import QtWidgets, QtCore, QtGui, QtUiTools
import mainWindow #Main window just grabs the Maya main window and returns the object to use as parent.
class Tool(QtWidgets.QMainWindow):
def __init__(self, parent=mainWindow.getMayaMainWindow()):
super(Tool, self).__init__(parent)
UI = "pathToUI/UI.ui"
loader = QtUiTools.QUiLoader()
ui_file = QtCore.QFile(UI)
ui_file.open(QtCore.QFile.ReadOnly)
self.ui = loader.load(ui_file, self)
#Scans all window objects and if one is open with the same name as this tool then close it so we don't have two open.
mainWindow.closeUI("Tool")
###HERE'S WHERE THE THREADING STARTS###
#Create a thread
thread = QtCore.QThread()
#Create worker object
self.worker = Worker()
#Move worker object into thread (This creates an automatic queue if multiples of the same worker are called)
self.worker.moveToThread(thread)
#Connect buttons in the UI to trigger a method inside the worker which should run in a thread
self.ui.first_btn.clicked.connect(self.worker.do_something)
self.ui.second_btn.clicked.connect(self.worker.do_something_else)
self.ui.third_btn.clicked.connect(self.worker.and_so_fourth)
#Start the thread
thread.start()
#Show UI
self.ui.show()
class Worker(QtCore.QObject):
def __init__(self):
super(Worker, self).__init__() #This will immediately crash Maya on tool launch
#super(Worker).__init__() #This works to open the window but still gets an error '# TypeError: super() takes at least 1 argument (0 given)'
def do_something(self):
#Start long code here and update progress bar as needed in a still active UI.
myTool.ui.progressBar.setValue(0)
print "doing something!"
myTool.ui.progressBar.setValue(100)
def do_something_else(self):
#Start long code here and update progress bar as needed in a still active UI.
myTool.ui.progressBar.setValue(0)
print "doing something else!"
myTool.ui.progressBar.setValue(100)
def and_so_fourth(self):
#Start long code here and update progress bar as needed in a still active UI.
myTool.ui.progressBar.setValue(0)
print "and so fourth, all in the new thread in a queue of which method was called first!"
myTool.ui.progressBar.setValue(100)
#A Button inside Maya will import this code and run the 'launch' function to setup the tool
def launch():
global myTool
myTool = Tool()
I'm expecting the UI to stay active (not locked up) and the threads to be running Maya cmds without crashing Maya entirely while updating the UIs progress bars.
Any insight on this would be amazing!
From what I see it has the following errors:
thread is a local variable that is deleted when the constructor is finished executing causing what is executed to be done in the main thread which is not desired, the solution is to extend the life cycle and for this there are several solutions: 1) make class attribute, 2) pass a parent to the cycle of life they are managed by the parent. In this case use the second solution.
You should not modify the GUI from another thread, in your case you have modified the progressBar from another thread, in Qt you must use signals.
You must use the #Slot decorator in the methods that are executed in another thread.
You indicate that you want to modify myTool but you have not declared it, soglobal myTool will not work by making myTool a local variable to be deleted. The solution is to declare myTool:myTool = None
Considering the above, the solution is:
import maya.cmds as cmds
from PySide2 import QtWidgets, QtCore, QtGui, QtUiTools
import mainWindow # Main window just grabs the Maya main window and returns the object to use as parent.
class Tool(QtWidgets.QMainWindow):
def __init__(self, parent=mainWindow.getMayaMainWindow()):
super(Tool, self).__init__(parent)
UI = "pathToUI/UI.ui"
loader = QtUiTools.QUiLoader()
ui_file = QtCore.QFile(UI)
ui_file.open(QtCore.QFile.ReadOnly)
self.ui = loader.load(ui_file, self)
# Scans all window objects and if one is open with the same name as this tool then close it so we don't have two open.
mainWindow.closeUI("Tool")
# Create a thread
thread = QtCore.QThread(self)
# Create worker object
self.worker = Worker()
# Move worker object into thread (This creates an automatic queue if multiples of the same worker are called)
self.worker.moveToThread(thread)
# Connect buttons in the UI to trigger a method inside the worker which should run in a thread
self.ui.first_btn.clicked.connect(self.worker.do_something)
self.ui.second_btn.clicked.connect(self.worker.do_something_else)
self.ui.third_btn.clicked.connect(self.worker.and_so_fourth)
self.worker.valueChanged.connect(self.ui.progressBar.setValue)
# Start the thread
thread.start()
# Show UI
self.ui.show()
class Worker(QtCore.QObject):
valueChanged = QtCore.Signal(int)
#QtCore.Slot()
def do_something(self):
# Start long code here and update progress bar as needed in a still active UI.
self.valueChanged.emit(0)
print "doing something!"
self.valueChanged.emit(100)
#QtCore.Slot()
def do_something_else(self):
# Start long code here and update progress bar as needed in a still active UI.
self.valueChanged.emit(0)
print "doing something else!"
self.valueChanged.emit(100)
#QtCore.Slot()
def and_so_fourth(self):
# Start long code here and update progress bar as needed in a still active UI.
self.valueChanged.emit(0)
print "and so fourth, all in the new thread in a queue of which method was called first!"
self.valueChanged.emit(100)
myTool = None
def launch():
global myTool
myTool = Tool()

How to structure large pyqt5 GUI without subclassing QThread and using QPushButtons to do long-running tasks

I'm looking at creating a program with a PyQt5 GUI. The program will start with a UI with numerous buttons. These buttons will be used to open other programs/completed long running tasks. I know I need to use QThread, but I am unsure how to structure the programs so that it scales properly.
I've been at this for ages and have read numerous posts/tutorials. Most lean down the subclassing route. In the past, I have managed to create a working program subclassing QThread, but I have since read that this metholodogy is not preferred.
I have a feeling I should be creating a generic worker and passing in a function with *args and **kwargs, but that is not in my skillset yet.
I originally created a thread for each button during the GUI init, but that seemed like it was going to get out of hand quickly.
I am currently at the stage of creating a thread under the slot connected to the button.clicked signal. I am not sure if I then have to have a worker for each button or if I can/should make a generic worker and pass in a function. Note: I have tried to do this but have not been able to do it.
#Import standard modules
import sys
#Import third-party modles
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QVBoxLayout, QWidget
class Worker(QObject):
#Custom signals?? or built-in QThread signals?
started = pyqtSignal()
finished = pyqtSignal()
def __init__(self):
super().__init__()
self.started.emit()
#pyqtSlot()
def do_something(self):
for _ in range(3):
print('Threading...')
QThread.sleep(1)
self.finished.emit()
class Window(QMainWindow):
def __init__(self):
super().__init__()
self.initUi()
def initUi(self):
#Create GUI
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget )
self.vertical_layout = QVBoxLayout(self.centralWidget)
self.setWindowTitle('QThread Test')
self.setGeometry(300, 300, 300, 50)
self.button1=QPushButton("Task 1", self, clicked=self._task1_clicked)
self.button2=QPushButton("Task 2", self, clicked=self._task2_clicked)
self.vertical_layout.addWidget(self.button1)
self.vertical_layout.addWidget(self.button2)
self.vertical_layout.addStretch()
def _task1_clicked(self):
print('task1 clicked')
#Create the worker
self.my_worker = Worker()
#Create thread; needs to be done before connecting signals/slots
self.task1_thread = QThread()
#Move the worker to the thread
self.my_worker.moveToThread(self.task1_thread)
#Connect worker and thread signals to slots
self.task1_thread.started.connect(self._thread_started)
self.task1_thread.started.connect(self.my_worker.do_something)
self.my_worker.finished.connect(self._thread_finished)
#Start thread
self.task1_thread.start()
def _task2_clicked(self):
print('task2 clicked')
def _thread_started(self):
print('thread started')
def _thread_finished(self):
print('thread finished')
self.my_worker.isRunning = False
self.task1_thread.quit()
self.task1_thread.wait()
print('The thread is running: ' + str(self.task1_thread.isRunning()))
if __name__ == '__main__':
app = QApplication(sys.argv)
form = Window()
form.show()
app.exec_()
The above seems to work, but I feel like I have stumbled on to it and it is not the correct way of doing this. I do not want this to be my 'go-to' method if it is completely wrong. I'd like to be able to generate more complicated (more buttons doing things) programs compared to a one button/one task program.
In addition, I can't seem to get the QThread started and finished signals to fire without basically making them custom built signals. This is one reason I think I am going about this wrong.
from PyQt5 import QtCore
class AsyncTask(QtCore.QThread):
taskDone = QtCore.pyqtSignal(dict)
def __init__(self, *, task, callback=None, parent = None):
super().__init__(parent)
self.task = task
if callback != None:
self.taskDone.connect(callback)
if callback == None:
callback = self.callback
self.start()
def run(self):
try:
result = self.task()
print(result)
self.taskDone.emit(result)
except Exception as ex:
print(ex)
def callback(self):
print('callback')
Please try code above, call like this:
AsyncTask(task=yourTaskFunction, callback=yourCallbackFunction)

How to display progress without multi-threading

Let's say I have a PyQt program that goes through a given directory, looks for *JPEG images, and does some processing every time it finds one. Depending on the size of the selected directory, this may take from some seconds to minutes.
I would like to keep my user updated with the status - preferably with something like "x files processed out of y files" . If not, a simple running pulse progress bar by setting progressbar.setRange(0,0) works too.
From my understanding, in order to prevent my GUI from freezing, I will need a seperate thread that process the images, and the original thread that updates the GUI every interval.
But I am wondering if there is any possible way for me to do both in the same thread?
Yes, you can easily do this using processEvents, which is provided for this exact purpose.
I have used this technique for implementing a simple find-in-files dialog box. All you need to do is launch the function that processes the files with a single-shot timer, and then periodically call processEvents in the loop. This is should be good enough to update a counter with the number of files processed, and also allow the user to cancel the process, if necessary.
The only real issue is deciding on how frequently to call processEvents. The more often you call it, the more responsive the GUI will be - but this comes at the cost of considerably slowing the processing of the files. So you may have to experiment a little bit in order to find an acceptable compromise.
UPDATE:
Here's a simple demo that shows how the code could be structured:
import sys, time
from PyQt5 import QtWidgets, QtCore
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.button = QtWidgets.QPushButton('Start')
self.progress = QtWidgets.QLabel('0')
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.button)
layout.addWidget(self.progress)
self.button.clicked.connect(self.test)
self._stop = False
self._stopped = True
def test(self):
if self._stopped:
self._stop = False
self.progress.setText('0')
self.button.setText('Stop')
QtCore.QTimer.singleShot(1, self.process)
else:
self._stop = True
def process(self):
self._stopped = False
for index in range(1, 1000):
time.sleep(0.01)
self.progress.setText(str(index))
if not index % 20:
QtWidgets.qApp.processEvents(
QtCore.QEventLoop.AllEvents, 50)
if self._stop:
break
self._stopped = True
self.button.setText('Start')
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
I could not achieve the thing you need without multi threading and this is not possible because gui can be only updated in main thread. Below is an algorithm how I did this with multithreading.
Let's say you have your application processing images. Then there are the following threads:
Main thread (that blocks by GUI/QApplication-derived classes.exec())
Timer with, for example, 1 second interval which updates a variable and calls a slot in GUI thread which updates a variable in user interface.
A thread which is processing images on your pc.
def process(self):
self._status = "processing image 1"
....
def _update(self):
self.status_label.setText(self._status)
def start_processing(self, image_path):
# create thread for process and run it
# create thread for updating by using QtCore.QTimer()
# connect qtimer triggered signal to and `self._update()` slot
# connect image processing thread (use connect signal to any slot, in this example I'll stop timer after processing thread finishes)
#pyqtSlot()
def _stop_timer():
self._qtimer.stop()
self._qtimer = None
_update_thread.finished.connect(_stop_timer)
In pyqt5 it is possible to assign a pyqtvariable from a one nested thread(first level). So you can make your variable a pyqtvariable with setter and getter and update gui in a setter or think how you can do this by yourself.
You could just use the python threading module and emit a signal in your threaded routine.
Here's a working example
from PyQt4 import QtGui, QtCore
import threading
import time
class MyWidget(QtGui.QWidget):
valueChanged = QtCore.pyqtSignal(int)
def __init__(self, parent=None):
super(MyWidget, self).__init__(parent)
self.computeButton = QtGui.QPushButton("Compute", self)
self.progressBar = QtGui.QProgressBar()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.computeButton)
layout.addWidget(self.progressBar)
self.computeButton.clicked.connect(self.compute)
self.valueChanged.connect(self.progressBar.setValue)
def compute(self):
nbFiles = 10
self.progressBar.setRange(0, nbFiles)
def inner():
for i in range(1, nbFiles+1):
time.sleep(0.5) # Process Image
self.valueChanged.emit(i) # Notify progress
self.thread = threading.Thread(target = inner)
self.thread.start()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())

How to use QTimer inside QThread which uses QWaitCondition? (pyside)

I'm using pyside but (I think) is a generic Qt question.
I know that QThread implementation calls ._exec() method so we should have an event loop on a started QThread. This way we can use QTimer on that thread (I've done this and it works perfectly). My problem is when QWaitCondition is also used, I'd like to have a "consumer" thread with a infinite loop waiting to be notify (from producers) on the QWaitCondition. The problem I have is that with this design I cannot use QTimer inside the consumer Thread.
This is a snippet of the scenario I'm trying to explain:
from PySide import QtGui
from PySide import QtCore
import sys
class MainWindow(QtGui.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.button = QtGui.QPushButton(self)
self.button.setText("Periodical")
self.button.clicked.connect(self.periodical_call)
self.thread = QtCore.QThread()
self.worker = Worker()
self.worker.moveToThread(self.thread)
self.thread.started.connect(self.worker.loop)
self.thread.start()
def closeEvent(self, x):
self.worker.stop()
self.thread.quit()
self.thread.wait()
def periodical_call(self):
self.worker.do_stuff("main window") # this works
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.do_stuff) # this also works
self.timer.start(2000)
def do_stuff(self):
self.worker.do_stuff("timer main window")
class Worker(QtCore.QObject):
def do_stuff_timer(self):
do_stuff("timer worker")
def do_stuff(self, origin):
self.origin = origin
self.wait.wakeOne()
def stop(self):
self._exit = True
self.wait.wakeAll()
def loop(self):
self.wait = QtCore.QWaitCondition()
self.mutex = QtCore.QMutex()
self._exit = False
while not self._exit:
self.wait.wait(self.mutex)
print "loop from %s" % (self.origin,)
self.timer = QtCore.QTimer()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.do_stuff_timer)
self.timer.start(1000) # <---- this doesn't work
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
frame = MainWindow()
frame.show()
sys.exit(app.exec_())
Once you click the button we obtain an output like this:
loop from main window
loop from timer main window
loop from timer main window
loop from timer main window
...
This means that the QTimer created inside loop() method is never executed by the event loop.
If I change the design from QWaitCondition to Signals (which is better design imho) the QTimer works, but I'd like to know why they are not working when QWaitCondition is used.
To still process events in a long running task (aka a continuous loop) you need to call QCoreApplication::processEvents().
This will essentially go through all of the queued up slots for your thread.
Calling this function is also necessary for signals (if they are a QueuedConnection signal/slot connection) to make it out of the current thread and into another one.
For PySides, you will need to call PySide.QtCore.QCoreApplication.processEvents()
Your method loop completely occupies thread.
It doesn't return control to event loop. Timer sends its events to event loop which doesn't gain control.
IMO your wile loop is faulty.
One way to fix it is add QApplication.processEvents() in loop (bad approach).
I think you want something else, here are my corrections:
def loop(self):
self.timer = QtCore.QTimer()
self.timer.setSingleShot(False)
self.timer.timeout.connect(self.do_stuff_timer)
self.timer.start(1000)
def stop(self):
self.timer.stop()
this will invoke do_stuff_timer every second until you will call stop.

Categories

Resources