QThread causing entire program to sleep in pyqt5 - python

This is my first attempt at trying to subclass QThreads and use it in a program but I'm getting something kind of weird. I'm not sure if my constructor is wrong or something like that, but basically when I run my QThread the entire program sleeps (not just the thread) when the QThread is sleeping
For example, the provided code will print "Hello there" after 3 seconds which is how long the QThread is supposed to sleep for
How do I fix my code so that I can have my thread running in the background while the program is running
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QThread, pyqtSignal
import time
class MyThread(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
self.sleep(3)
print("Slept for 3 seconds")
def main():
qThread = MyThread()
qThread.run()
print("Hello there")
main()

Use start not run:
def main():
qThread = MyThread()
qThread.start()
print("Hello there")
Since run is the starting point for the thread (which exists in case you wanted to reuse the code not in a thread),
whilst start is the method to start the thread itself, so it will in turn call run

To complete ptolemy0's answer, i can share you the code i'm using to practice on Qt:
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import QWidget, QApplication
import sys, time
class MyThread(QThread):
def __init__(self, parent=None):
super(MyThread, self).__init__(parent)
def __del__(self):
self.wait()
def run(self):
while True:
self.sleep(3)
print("Slept for 3 seconds")
class Main(QWidget):
def __init__(self, parent=None):
super(Main,self).__init__(parent)
qThread = MyThread()
qThread.start()
i=0
while True:
print(i)
i+=1
time.sleep(1)
def main():
app = QApplication(sys.argv)
example = Main()
print("Hello there")
sys.exit(app.exec())
main()
Hope it can help you!

Related

Reuse QThread for different workers in python

I want to use the same QThread for different workers. That is, first worker1 is passed to the thread and runs. After it finishes, worker2 should be passed to the thread and runs.
So, this is my attempt
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QAction
from PyQt5 import uic
from PyQt5.QtCore import QObject, QThread, pyqtSignal
from time import sleep
class MainWin(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.thread = QThread()
self.worker1 = Worker1()
self.worker1.finished.connect(self.w1_finish)
self.thread.started.connect(self.worker.run)
self.thread.start()
def w1_finish():
self.thread.quit()
self.worker1.deleteLater()
self.worker2 = Worker2()
self.worker2.finished.connect(self.w2_finish)
self.thread.started.connect(self.worker2.run)
self.thread.start()
def w2_finish():
print("all done")
class Worker1(QObject):
finished = pyqtSignal()
def run():
for i in range(5):
print(f"This is worker1: {i}")
sleep(1)
self.finished.emit()
class Worker2(QObject):
finished = pyqtSignal()
def run():
for i in range(5):
print(f"This is worker2: {i}")
sleep(1)
self.finished.emit()
Strangely, this sometimes works as intended. I mean, I first get the outputs from worker 1 and then from worker2. However, in some cases, worker 2 seems to be executed after worker 1 has finished. Is there a problem? Should I use two separate thread instances for the task?

When using a QThread, how do i pass data to the function that is called with .connect()?

I am using a QThread and, when my worker class is dont with the big work, i want to call a function that does changes to my UI (in this case with .finished.connect(), but i dont fully understand how the .connect() step works and how i can pass variables to the function called.
As an example: A button that, when pressed, opens a Thread in which it counts up, and changes the button text to the number its on. When the counter is finished, it prints something. Very simple and it works just fine.
main.py:
from PyQt5 import QtWidgets
import sys
import worker
from PyQt5.QtCore import QThread
from start import test
class App(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(App, self).__init__(parent)
self.init_ui()
self.initiate_buttons()
self.obj = worker.Worker()
self.thread = QThread()
self.obj.intReady.connect(self.onIntReady)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.obj.finished.connect(test)
self.thread.started.connect(self.obj.procCounter)
def init_ui(self):
self.pushButton = QtWidgets.QPushButton(self)
def onIntReady(self, i):
self.pushButton.setText("{}".format(i))
def initiate_buttons(self):
print("clicked")
self.pushButton.clicked.connect(lambda: self.thread.start())
def run():
application = QtWidgets.QApplication(sys.argv)
main_app = App()
main_app.show()
application.exec()
if __name__ == '__main__':
run()
worker.py:
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot
import time
class Worker(QObject):
finished = pyqtSignal()
intReady = pyqtSignal(int)
#pyqtSlot()
def procCounter(self):
for i in range(1, 5):
time.sleep(1)
self.intReady.emit(i)
self.finished.emit()
start.py:
def test(x="bar"):
print("test"+x)
So the thing i am talking about is start.py - i can put the function in its own module just fine and it worked. But what if i want to pass a parameter to it? I tried the obvious attempt, changing self.obj.finished.connect(test) to self.obj.finished.connect(test(x="foo")) - but that doesnt work. But i dont understand why it does not work, and how else i am supposed to do this. In the end, i want the module to do changes to my UI, just like onIntReady. I found this and this but did not understand how i can apply it.

PyQt5 terminate application from worker

This is an example of the application that starts the QApp -> Worker -> Something that causes sys.exit() inside the worker
import sys
from PyQt5.QtCore import QRunnable, pyqtSlot, QThreadPool
from PyQt5.QtWidgets import QWidget, QApplication
class App(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.threadpool = QThreadPool()
worker = Worker()
self.threadpool.start(worker)
def init_ui(self):
self.setFixedSize(600, 300)
self.show()
class Worker(QRunnable):
def __init__(self):
super(Worker, self).__init__()
#pyqtSlot()
def run(self):
print("Running worker...")
run_application()
def run_window():
app = QApplication([])
ex = App()
sys.exit(app.exec_())
def run_application():
print('Running application...')
sys.exit(10)
if __name__ == '__main__':
run_window()
There are several cases when I run it:
exits with code 0
exits with code 10
window keeps hanging
It feels like a racing.
So what is the correct way to terminate the window in case of the sys.exit() or the exception inside the worker?
Edit: forgot to mention that I need to return the exit code and track it since I run the window through the subprocess/Popen.
If you want to close the windows then a possible solution is to terminate the eventloop using QCoreApplication::quit():
def run_application():
print("Running application...")
QCoreApplication.quit()
Or QCoreApplication::exit():
def run_application():
print("Running application...")
QCoreApplication.exit(10)

QThread stops event processing

I would like to start a Thread with PyQt, that performs some actions independently of the rest of the app. This includes running an external program. This may take some minutes.
My problem is that using QThread for this stops the whole app.
When running this small program, clicking the button will freeze the app for 10 seconds.
What can I do to make QThread behave as a Thread. I know that I can add some timers and divide it into events, but that is not my idea of a thread.
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtCore import QThread
class ProgramThread(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
QThread.sleep(10)
class App(QWidget):
def __init__(self):
super().__init__()
thread = QPushButton('Start Thread',self)
thread.clicked.connect(self.startthread)
thread.move(20,100)
self.show()
def startthread(self):
t = ProgramThread()
t.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = App()
sys.exit(app.exec_())
QThread is not a thread, it is a thread handler. In your case the problem is that the variable t is local so it will be deleted after executing the start, at that moment the __del__ method is called, and this calls wait() that runs in the main thread blocking the GUI, this blocking It will be until the run method finishes executing, so in conclusion the problem is that t is a local variable, the solution is to keep it in time so there are 2 possibilities: 1) pass it to self as a parent or 2) make it a member of the class:
class ProgramThread(QThread):
def run(self):
QThread.sleep(10)
def __del__(self):
self.wait()
class App(QWidget):
def __init__(self):
super().__init__()
thread = QPushButton('Start Thread',self)
thread.clicked.connect(self.startthread)
thread.move(20,100)
self.show()
def startthread(self):
# method 1
t = ProgramThread(self)
t.start()
# method 2
# self.t = ProgramThread()
# self.t.start()

PyQt5 Run function after displaying window

I would like to call a function after the GUI displays. If I run function in init it prevents gui from displaying until after it is completed.
class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.function() #waits for this to finish until gui displayed
def function(self):
self.guiBox.setValue(initData)
#inits stuff, takes 5 seconds
The function initializes a piece of equipment via serial port... It takes s few seconds, and it takes gui attributes and updates gui display boxes.
Add single shot timer 1 ms and after call function
class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
QTimer.singleShot(1,self.function) #waits for this to finish until gui displayed
def function(self):
self.guiBox.setValue(initData)
#inits stuff, takes 5 seconds
Time-consuming tasks are blocking, and this goes against the natural way of working on the GUI, an option is to use qApp.processEvents(), for example:
def function(self):
self.guiBox.setValue(initData)
code1
QtWidgets.qApp.processEvents()
code2
QtWidgets.qApp.processEvents()
...
I would recommend QThreads, especially if you are performing a ton of other actions in your "function." This example isn't the only way to thread in PyQt, but thought an example where you are able to pass data back and forth between the thread and the main gui would be best.
import os
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
from PyQt5.QtCore import QUrl, QEventLoop, QThread, QObject, pyqtSlot, pyqtSignal
from PyQt5.QtWebEngineWidgets import QWebEngineView
class time_consuming_function(QObject):
def __init__(self, widget):
super(time_consuming_function, self).__init__()
self.widget = widget
self.run_trigger.connect(self.run)
run_trigger = pyqtSignal(int, int)
#pyqtSlot(int, int)
def run(self, int1, int2):
print("In Time Consuming Function")
for i in range(100000000):
i*i
print("Finished with Time Consuming Function")
self.widget.someTrigger.emit([1, 2, 3])
class WebPage(QWebEngineView):
def __init__(self):
QWebEngineView.__init__(self)
self.load(QUrl("https://www.google.com"))
self.loadFinished.connect(self._on_load_finished)
self.someTrigger.connect(self.gui_response)
self.thread = QThread()
self.thread.start()
self.consume = time_consuming_function(self)
self.consume.moveToThread(self.thread)
self.consume.run_trigger.emit(1,1)
someTrigger = pyqtSignal(list)
def _on_load_finished(self):
print("Finished Loading")
def gui_response(self, data):
print("Responding to GUI: ", str(data))
if __name__ == "__main__":
app = QApplication(sys.argv)
web = WebPage()
web.show()
sys.exit(app.exec_())
I'd say the easiest way without using thread and related would be to verify if the last event of showing up the window was executed and then call your method.
You'd have something like that:
import sys
from PyQt5.QtCore import QEvent
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
self.setFixedSize(500, 500)
self.move(300, 50)
def print_1_bi(self):
for i in range(10**9): print(i)
def event(self, event):
if event.type() == QEvent.InputMethodQuery:
self.print_1_bi()
return super(MainWindow, self).event(event)
if __name__ == "__main__":
app = QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
Note: Remember that even though the UI already showed up your application will be waiting your method to finish, if it's a problem you'll have to use process like the guys told you to in the other answers.

Categories