I'm having problem with QThreads in python. I want to change background color of label.
But My application crash while starting.
"QThread: Destroyed while thread is still running"
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
statusTh = statusThread(self)
self.connect(statusTh, SIGNAL('setStatus'), self.st, Qt.QueuedConnection)
statusTh.start()
def st(self):
if self.status == 'ON':
self.ui.label.setStyleSheet('background-color:green')
else:
self.ui.label.setStyleSheet('background-color:red')
class statusThread(QThread):
def __init__(self, mw):
super(statusThread, self).__init__()
def run(self):
while True:
time.sleep(1)
self.emit(SIGNAL('setStatus'))
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
You're not storing a reference to the thread after it's been created, which means that it will be garbage collected (ie. destroyed) some time after the program leaves MainWindows __init__. You need to store it at least as long as the thread is running, for example use self.statusTh:
self.statusTh = statusThread(self)
self.connect(self.statusTh, SIGNAL('setStatus'), self.st, Qt.QueuedConnection)
self.statusTh.start()
I know it's quite necroposting but this may be useful. In the main section of your script, a first level customized widget need to be stored in variable, not only created.
For example I have a custom widget class called MainWindow that create a QThread. My main is like that:
from myPackage import MainWindow
if __name__ == "__main__":
app = QApplication([])
widget=MainWindow()
sys.exit(app.exec())
if I avoid the widged = definition and only call for MainWindow(), my script will crash with QThread: Destroyed while thread is still running
Related
I'm trying to modify my main layout from another thread. But the function run() is never called
and i'm having the error:
QObject::setParent: Cannot set parent, new parent is in a different
thread
Here's my code:
class FeedRetrievingThread(QtCore.QThread):
def __init__(self, parent=None):
super(FeedRetrievingThread, self).__init__(parent)
self.mainLayout = parent.mainLayout
def run(self):
# Do things with self.mainLayout
class MainWindow(QtGui.QDialog):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.mainLayout = QtGui.QGridLayout()
self.setLayout(self.mainLayout)
self.feedRetrievingThread = FeedRetrievingThread(self)
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.updateFeed)
self.timer.start(1000)
def updateFeed(self):
if not self.feedRetrievingThread.isRunning():
print 'Running thread.'
self.feedRetrievingThread.start()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
sys.exit(app.exec_())
I really don't get it, why is it so difficult to access the GUI with PyQt?
In C# you have Invoke. Is there anything of the kind in PyQt?
I tried creating the thread directly from MainWindow.__init__ (without using the timer) but it didn't work either.
In Qt you should never attempt to directly update the GUI from outside of the GUI thread.
Instead, have your threads emit signals and connect them to slots which do the necessary updating from within the GUI thread.
See the Qt documentation regarding Threads and QObjects.
I want to open another window upon a successful login but it continuously give this error.
QCoreApplication::exec: The event loop is already running
Here is the code.
LoginWindow - Login Page
MainWindow - Post login page
Is there anyway I can open another window upon a successful login and close the current one.
from PyQt5.QtWidgets import QMainWindow, QApplication
from login_ui import *
import sys
LOGGED_IN = False
TOKEN = ""
class LoginWindow(QMainWindow, Ui_LoginWindow):
GLOBAL_STATE = True
loginPortal = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.show()
self.handleButtons()
sys.exit(self.loginPortal.exec_())
def handleButtons(self):
self.CloseBtn.clicked.connect(self.close)
self.MinBtn.clicked.connect(self.showMinimized)
self.loginBtn.clicked.connect(self.login)
def login(self):
if self.username.text() == "admin" and self.password.text() == "password":
LOGGED_IN = True
TOKEN = "admin"
elif self.username.text() == "coord" and self.password.text() == "password":
LOGGED_IN = True
TOKEN = "coord"
else:
LOGGED_IN = False
Token = None
if LOGGED_IN:
MainWindow().show()
else:
self.close()
self.loginPortal.quit()
from main_ui import *
class MainWindow(QMainWindow, Ui_MainWindow):
app = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.showMaximized()
self.handleButtons()
sys.exit(self.app.exec_())
def handleButtons(self):
self.MinBtn.clicked.connect(self.showMinimized)
self.MaxBtn.clicked.connect(self.maximize)
self.CloseBtn.clicked.connect(self.close)
def maximize(self):
if self.GLOBAL_STATE:
self.showNormal()
self.GLOBAL_STATE = False
else:
self.showMaximized()
self.GLOBAL_STATE = True
if __name__ == "__main__":
LoginWindow()
There are two problems with your code:
Only one QApplication instance should exist.
You're creating a QApplication in the class definition.
The second problem is important, as QtWidgets can only be created when a QApplication is created. When python opens a script, it also initializes all the classes in its main indentation, which also means that every function in the base indentation of the class is executed.
In your case, it means that when each class is loaded, each one will try to create a QApplication, since app = QApplication([]) is in their main indentation. As explained in the first point, only one QApplication instance can only exist.
While technically your code could run fine when using a single class (the QApplication is created before actually creating the widget instance), this leads to problems exactly like in your case.
A possible workaround would be to check if a QApplication already exists:
# note: this is just for explanation purposes, do NOT do this unless you
# really know what you're doing
class LoginWindow(QMainWindow, Ui_MainWindow):
if not QApplication.instance():
loginPortal = QApplication([])
# ...
class MainWindow(QMainWindow, Ui_MainWindow):
if not QApplication.instance():
app = QApplication([])
But that's not a good approach anyway, and the reason is that the application should be also executed with exec_() (or exec()). While you're doing it within the __init__ of each widget, that's absolutely not the place for that: exec_() is blocking, which prevents returning the actual instance to the caller until the application quits. Even if you're not actually interested in that, it's not good practice, as the __init__ should always return as soon as possible. Finally, when you try to create the main window instance, it will try to exec the other QApplication instance, and this is the (final) source of your problem.
That's what the common if __name__ == '__main__': is important for: ensuring that only the main program executes what should be actually run, and in case of Qt, to create and run the only QApplication that should ever exist in the program lifetime.
A better approach when dealing with multiple windows is to create a signal and use that to interact with anything else.
This is a possible correct version of your code (be aware of the commented lines)
class LoginWindow(QMainWindow, Ui_LoginWindow):
loggedSignal = pyqtSignal()
GLOBAL_STATE = True
# loginPortal = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.setAttribute(Qt.WA_TranslucentBackground)
self.show()
self.handleButtons()
# sys.exit(self.loginPortal.exec_())
# ...
def login(self):
# ...
if LOGGED_IN:
self.loggedSignal.emit()
else:
QApplication.quit()
self.close()
class MainWindow(QMainWindow, Ui_MainWindow):
# app = QApplication([])
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowFlag(Qt.FramelessWindowHint)
# self.showMaximized()
self.handleButtons()
# sys.exit(self.app.exec_())
if __name__ == '__main__':
app = QApplication(sys.argv)
login = LoginWindow()
mainWindow = MainWindow()
login.loggedSignal.connect(mainWindow.showMaximized)
sys.exit(app.exec_())
Note that you are using the GLOBAL_STATE in the main window, but you defined it in the login window only instead.
I have found a strange behavior if I create QThread as local variable.
For example, the following code will work as single thread, which means I need to wait 10 seconds and the result will come out.
But if I change thread from local variable to member variable, it works as multi thread.
How's it coming? Could anyone give me some tips?
class UI():
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
self.dialog = QtGui.QDialog()
self.ui = Ui_Dialog()
self.ui.setupUi(self.dialog)
self.ui.btn.clicked.connect(self.btnclick)
def run(self):
self.dialog.show()
sys.exit(self.app.exec_())
def btnclick(self):
## if change to self.thread, the behavior changes!!
signal = QtCore.SIGNAL("Log(QString)")
thread = testThread(signal)
QtCore.QObject.connect(thread, signal, self.output)
thread.start()
def output(self, txt):
self.ui.logText.append(str(txt))
class testThread(QThread):
def __init__(self, signal):
QThread.__init__(self)
self.signal = signal
def __del__(self):
self.wait()
def run(self):
for i in range(10):
time.sleep(1)
self.output(str(i))
def output(self, txt):
self.emit(self.signal, txt)
if __name__ == "__main__":
ui = UI()
ui.run()
The problem as to point out is that it is a local variable that will be destroyed a moment after having started the QThread so the thread that is handled by QThread(QThread is not a thread, it is a thread handler) will be removed and when using wait() it is expected that run() method will be executed but in the main thread generating the GUI s freeze.
So the solution is to extend the life of the variable thread, one way you point out that it works: make it a member of the class, but there is another way that only works with QObjects as QThread and that is to pass a parent (the parent must to be another QObject) that will extend the life of the object to the same capacity of the parent, for that reason I will use a dialog.
Finally nowadays it is not recommended to create signals dynamically, it is better to create it as part of a class, also for the connection you must use the new syntax.
class UI():
def __init__(self):
self.app = QtGui.QApplication(sys.argv)
self.dialog = QtGui.QDialog()
self.ui = Ui_Dialog()
self.ui.setupUi(self.dialog)
self.ui.btn.clicked.connect(self.btnclick)
self.dialog.show()
def btnclick(self):
thread = testThread(self.dialog)
thread.signal.connect(self.output)
thread.start()
def output(self, txt):
self.ui.logText.append(str(txt))
class testThread(QtCore.QThread):
signal = QtCore.pyqtSignal(str)
def __del__(self):
self.wait()
def run(self):
for i in range(10):
QtCore.QThread.sleep(1)
self.output(str(i))
def output(self, txt):
self.signal.emit(txt)
if __name__ == '__main__':
ui = UI()
app = QtGui.QApplication.instance()
if app is not None:
sys.exit(app.exec_())
I'm trying to open a second window in a new process to not freeze the main window with PyQt5. For this reason, I define a new class that inherits from multiprocessing.Process and shows the window. This is the main code:
class GuiMain(QMainWindow):
...
# Main window with several functions. When a button is clicked, executes
# self.button_pressed()
def button_pressed(self):
proc1 = OpenWindowProcess()
proc1.start()
class OpenWindowProcess(mp.Process)
def __init__(self):
mp.Process.__init__(self)
print(self.pid)
def run(self):
print("Opening window...")
window = QtGui.QWindow()
window.show()
time.sleep(10)
if __name__ == '__main__':
app = QApplication(sys.argv)
application = GuiMain()
sys.exit(app.exec_())
The process is created and gets a PID. When run() function is called, the message "Opening window.." is displayed, but nothing else happens. No window, no error... I can't figure out whats happening. Thank you in advance!
I've come to a solution. You have to create a new QtApplication and attach to it a new QMainWindow instance. This code works fine:
class GuiMain(QMainWindow):
...
# Main window with several functions. When a button is clicked, executes
# self.button_pressed()
def button_pressed(self):
proc1 = OpenWindowProcess()
proc1.start()
class OpenWindowProcess(mp.Process)
def __init__(self):
mp.Process.__init__(self)
print("Process PID: " + self.pid)
def run(self):
print("Opening window...")
app = QApplication(sys.argv)
window = QMainWindow()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
app = QApplication(sys.argv)
application = GuiMain()
sys.exit(app.exec_())
I want my code to run by showing the qtwidget and then running the forloop,
but it runs the forloop then shows my widget to me. Why is this?
class tes(QWidget):
def __init__(self):
super(tes, self).__init__()
self.initUI()
for i in range (1000000):
print("s")
def initUI(self):
t = QTableWidget(8,8,self)
self.show()
self.resize(1000,1000)
t.setGeometry(0,0,500,500)
t.show()
def main():
app = QApplication(sys.argv)
t = tes()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Add QApplication.processEvents() before loop. Your widget will be shown, but unresponsive. To make application responsive, add processEvents() calls to some steps of your loop.
Example:
def __init__(self):
super(tes, self).__init__()
self.initUI()
QApplication.processEvents()
for i in range (1000000):
if not i % 3: # let application process events each 3 steps.
QApplication.processEvents()
print("s")
It's because you run app.exec_() after the for loop executes during the tes object initialization.
The widget is only shown once the application is running, not when its initialised. What exactly are you trying to do in the loop? It might be better to connect it to a signal or handle it in an event, but it all depends what you're trying to acheive.