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.
Related
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.
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)
Using PyQt5, I want to implement a two windows displaying one after another automatically, without the user interacting with any window. Something like this:
While True:
Show Window1
wait 2 seconds
Close Window1
Show Window2
wait 2 seconds
Close Window2
The problem I am having is that the main UI thread is stuck in app.exec_() function, so it cannot implement the opening and closing logic.
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
class Win1(QMainWindow):
def __init__(self):
super(Win1, self).__init__()
uic.loadUi('win1.ui', self)
self.show()
class Win2(QMainWindow):
def __init__(self):
super(Win2, self).__init__()
uic.loadUi('win2.ui', self)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
while True:
win = Win1()
time.sleep(1)
win.close()
win = Win2()
time.sleep(1)
win.close()
app.exec_() # <--------- Program blocks here
I would appreciate if someone can share a minimal example for this working without blocking. Or please point to the mechanism that should be used.
If you are going to work with Qt then you should forget about sequential logic but you have to implement the logic using events. For example, in your case you want one window to be shown every time T and another to be hidden, so that can be implemented with a QTimer and a flag:
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5 import uic
class Win1(QMainWindow):
def __init__(self):
super(Win1, self).__init__()
uic.loadUi('win1.ui', self)
self.show()
class Win2(QMainWindow):
def __init__(self):
super(Win2, self).__init__()
uic.loadUi('win2.ui', self)
self.show()
if __name__ == "__main__":
app = QApplication(sys.argv)
timer = QTimer()
timer.setProperty("flag", True)
win1 = Win1()
win2 = Win2()
def on_timeout():
flag = timer.property("flag")
if flag:
win1.show()
win2.close()
else:
win2.show()
win1.close()
timer.setProperty("flag", not flag)
timer.timeout.connect(on_timeout)
timer.start(1 * 1000)
on_timeout()
app.exec_()
You should not use while loop or time.sleep since they block the eventloop in which the GUI lives, that is: they freeze the windows
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!
I'm making an application in PyQt5 that runs a variety of long running tasks, such as scraping web pages. In order to avoid crashing the GUI, I've been using QThreads and QObjects
Currently I have a class that inherits QObject and contains a method for scraping the web pages. I move that object on to a QThread, connect a finished signal with a method that quits the thread and then start the thread.
import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
import threading
from concurrent.futures import ThreadPoolExecutor
import requests
class Main(QMainWindow):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
self.init_ui()
self.test_obj = None
self.thread = QThread(self)
def init_ui(self):
widget = QWidget()
layout = QHBoxLayout()
widget.setLayout(layout)
thread_button = QPushButton("Start Thread")
check_button = QPushButton("Check Thread")
layout.addWidget(thread_button)
layout.addWidget(check_button)
thread_button.clicked.connect(self.work)
check_button.clicked.connect(self.check)
self.setCentralWidget(widget)
self.show()
def work(self):
self.test_obj = TestObject()
self.test_obj.moveToThread(self.thread)
self.test_obj.finished.connect(self.finished)
self.thread.started.connect(self.test_obj.test)
self.thread.start()
def check(self):
for t in threading.enumerate():
print(t.name)
#pyqtSlot()
def finished(self):
self.thread.quit()
self.thread.wait()
print("Finished")
class TestObject(QObject):
finished = pyqtSignal()
def __init__(self):
super(TestObject, self).__init__()
def test(self):
with ThreadPoolExecutor() as executor:
executor.submit(self.get_url)
self.finished.emit()
def get_url(self):
res = requests.get("http://www.google.com/")
print(res)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Main()
sys.exit(app.exec_())
Everything works as expected, I get a:
"Response [200]"
"Finished"
Printed in the console. However when I check the running threads, it shows dummy-1 thread is still running. Every time I run, it creates an additional dummy thread. Eventually I am unable to create any more threads and my application crashes.
Is it possible to use a ThreadPoolExecutor on a QThread like this? If so, is there a correct way to ensure that I don't have these dummy threads still running once I've finished my task?