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_())
Related
When I open the application MyWindow showing and I get some information from the user and clase this window with self.close and open opencv window recording. After a time, record fnishing I try to restart MyWindow (first) process with below codes but it doesnt restart.
When timer end, I am calling below line:
QtGui.qApp.exit(MyWindow.EXIT_CODE_REBOOT)
And this is the main code:
if __name__ == '__main__':
currentExitCode = MyWindow.EXIT_CODE_REBOOT
while currentExitCode == MyWindow.EXIT_CODE_REBOOT:
a = QApplication(sys.argv)
w = MyWindow()
w.show()
currentExitCode = a.exec_()
a = None # delete the QApplication object
and I also have this value:
class MyWindow(QtWidgets.QWidget):
EXIT_CODE_REBOOT = -123
Where is my fault ? How can I restart application ?
I'm trying to make a small python programs which is able to have several windows. The issue is when I try to implement a menu entry to quit the programs, closing all the windows at once. I've tried to use qApp.close() and qApp.exit() but if those allow to effectively quit the program, there is no close events generated for the windows still opened, which prevent me to save modified data or to prevent leaving the application. What's the best practice for that? I could understand not being able to cancel the exit process, but being able to propose to save modified data is something I really want.
import sys
from PyQt5.QtWidgets import *
opened_windows = set()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.create_actions()
opened_windows.add(self)
def closeEvent(self, ev):
if QMessageBox.question(self, 'Closing', 'Really close?') == QMessageBox.Yes:
ev.accept()
opened_windows.remove(self)
else:
ev.ignore()
def create_action(self, action_callback, menu, action_name):
action = QAction(action_name, self)
action.triggered.connect(action_callback)
menu.addAction(action)
def create_actions(self):
_file_menu = self.menuBar().addMenu('&File')
self.create_action(self.on_new, _file_menu, '&New')
_file_menu.addSeparator()
self.create_action(self.on_close, _file_menu, '&Close')
self.create_action(self.on_quit, _file_menu, '&Quit')
self.create_action(self.on_exit, _file_menu, '&Exit')
def on_new(self):
win = MainWindow()
win.show()
def on_close(self):
self.close()
def on_quit(self):
qApp.quit()
def on_exit(self):
qApp.exit(1)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
status = app.exec()
print(len(opened_windows), ' window(s) opened')
print('status = ', status)
sys.exit(status)
Currently I'm modifying on_close and on_exit like this:
def on_exit(self):
for w in opened_windows.copy():
w.on_close()
if len(opened_windows) == 0:
qApp.exit(1)
but I wonder if I'm missing a better way which would not force me to maintain a set of opened windows.
Cause
It is important to understand, that the app and the main window are related, but are not the same thing. So, when you want to close the program, don't bother closing the app. Close the main window instead. From the documentation of QCloseEvent :
Close events are sent to widgets that the user wants to close, usually by choosing "Close" from the window menu, or by clicking the X title bar button. They are also sent when you call QWidget::close() to close a widget programmatically.
Solution
Connect your exit-action's triggered signal to the close slot of your MainWindow. In your case, instead of:
self.create_action(self.on_exit, _file_menu, '&Exit')
write:
self.create_action(self.close, _file_menu, '&Exit').
Define in MainWindow a signal closed and emit it from your implementation of the closedEvent, e.g. in the place of opened_windows.remove(self)
In on_new connect win.closed to self.close
Example
Here is how I suggest you to change your code in order to implement the proposed solution:
import sys
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
closed = pyqtSignal()
def __init__(self):
super().__init__()
self.create_actions()
def closeEvent(self, ev):
if QMessageBox.question(self, 'Closing', 'Really close?') == QMessageBox.Yes:
ev.accept()
self.closed.emit()
else:
ev.ignore()
def create_action(self, action_callback, menu, action_name):
action = QAction(action_name, self)
action.triggered.connect(action_callback)
menu.addAction(action)
def create_actions(self):
_file_menu = self.menuBar().addMenu('&File')
self.create_action(self.on_new, _file_menu, '&New')
_file_menu.addSeparator()
self.create_action(self.close, _file_menu, '&Exit')
def on_new(self):
win = MainWindow()
win.show()
win.closed.connect(self.close)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MainWindow()
win.show()
status = app.exec()
print('status = ', status)
sys.exit(status)
Edit: I wonder how I missed it before. There is the QApplication::closeAllWindows slot which does exactly what I want and whose example is a binding to exit.
There is a way to propose to save modified data on quit and exit, the signal QCoreApplication::aboutToQuit.
Note that although the Qt documentation says that user interaction is not possible, at least with PyQt5 I could use a QMessageBox without apparent issues.
I've put together some test code to figure out how to launch a simple pyqt Ui_Dialog multiple times during a script. I want launch the window depending on certain conditions as my script is running. The idea is that it is just a popup notification window with different text and the user will click ok. I designed the window in QtDesigner and inherited the generated code in another class:
class MsgWindow(QtGui.QDialog):
def __init__(self,parent=None):
QtGui.QWidget.__init__(self,parent)
self.app = QtGui.QApplication(sys.argv)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
QtCore.QObject.connect(self.ui.OK, QtCore.SIGNAL('clicked()'),self.close)
def AddText(self,text):
""" Method to add text to textEdit box """
self.ui.textEdit.append(text + '\n')
def closeEvent(self, event):
print "Closing the app"
#self.deleteLater()
if __name__=='__main__':
app = QtGui.QApplication(sys.argv)
myapp = MsgWindow()
myapp.show()
sys.exit(app.exec_())
So then my test script just launches the window (user closes it) and repeats. The issue is that after the last close, python crashes (python.exe has stopped working). Here is the test script code:
app = QtGui.QApplication(sys.argv)
dlg = MsgWindow()
for x in range(0,3):
dlg.AddText("This is a test, iteration: %d"%x)
dlg.exec_()
#sys.exit(app.exec_())
time.sleep(2)
What is the correct way to do this without crashing?
Thank you.
I have a small application, that requires login before it starts.
But if user rejects login(press cancel button), application won't close, it's just freeze.
Here is the simplified code:
import sys
from PyQt5 import QtWidgets, QtCore
class LoginWindow(QtWidgets.QDialog):
def __init__(self, parent=None):
super(LoginWindow, self).__init__(parent)
self.resize(250, 150)
self.move(500, 500)
self.setWindowTitle('Login')
self.login_input = QtWidgets.QLineEdit(self)
self.login_input.move(10, 10)
self.password_input = QtWidgets.QLineEdit(self)
self.password_input.move(10, 50)
self.password_input.setEchoMode(QtWidgets.QLineEdit.Password)
self.button_box = QtWidgets.QDialogButtonBox(self)
self.button_box.move(10, 80)
self.button_box.setOrientation(QtCore.Qt.Horizontal)
self.button_box.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel |
QtWidgets.QDialogButtonBox.Ok)
self.button_box.accepted.connect(self.login)
self.button_box.rejected.connect(self.reject)
def login(self):
self.accept()
def cancel(self):
self.reject()
class MainWindow(QtWidgets.QDialog):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.resize(250, 150)
self.move(500, 500)
self.setWindowTitle('Main')
self.input = QtWidgets.QLineEdit(self)
self.input.move(10, 10)
self.show()
def main():
app = QtWidgets.QApplication([])
l = LoginWindow()
l.show()
login_result = l.exec_()
print(login_result)
if login_result == QtWidgets.QDialog.Accepted:
m = MainWindow()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
What am I doing wrong?
I use python 3 and PyQt5
This happens because PySide has not processed any of it's events.
app.exec_()
This starts the main event loop that continually processes every GUI interaction. This should be called before you execute any GUI code, so the events can be processed correctly from the event Queue.
The QDialog.exec_() is a blocking operation preventing the code from continuing until it gets a response.
If you want to see the dialog items then you may be able to get around this.
QtGui.QApplication.processEvents()
This processes all of the events in the event Queue, so you would probably have to keep calling this method.
Also after you initialize your main window you will have to show the main window.
I find a way to avoid this bug:
I've changed main function:
def main():
app = QtWidgets.QApplication([])
if LoginWindow().exec_() == QtWidgets.QDialog.Accepted:
m = MainWindow()
sys.exit(app.exec_())
And it works normal, but is still can't understand, what was the root cause of the problem
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