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.
Related
I am trying to decouple entirely my GUI from my controller class, and for some reason I can't seem to manage to connect my buttons from outside of my GUI class itself.
Here's a small example of what I mean :
import sys
from PySide6 import QtWidgets
class Gui(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Gui, self).__init__(parent)
layout = QtWidgets.QHBoxLayout(self)
self.button = QtWidgets.QPushButton("Do stuff")
layout.addWidget(self.button)
class Controller(object):
def do_stuff(self):
print("something")
def startup(parent):
ctrl = Controller()
gui = Gui(parent)
gui.button.clicked.connect(ctrl.do_stuff)
return gui
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = QtWidgets.QDialog()
gui = startup(dialog)
dialog.show()
sys.exit(app.exec_())
I would expect this code, when run, to display a GUI with one push button (which it does), and when pressing the push button, I'd expect the word "something" to get printed. However this doesn't seem to be the case.
I might just be too tired, but I can't find the solution.
What am I missing?
Thanks a lot in advance!
ctrl = None
gui = None
def startup(parent):
global ctrl
global gui
ctrl = Controller()
gui = Gui(parent)
gui.button.clicked.connect(ctrl.do_stuff)
return gui
try this, and it does work. when the variable is in the function, it will be destroyed before the function is finished. the global variable is not a good coding style but is a simple way to figure out your confusion.
Currently trying to build a GUI.
Whenever I try to exit the GUI using either the button I created or the exit button on the OS it takes some time and at the end the console shows Restarting kernel... without any other msg.
I am using Spyder with Python 3.9.
BTW I am not using sys.exit() as I saw in some other questions that it was the problem kind of.. I really don't get it..
The code :
class Class_Thread(filex.Classx,QThread):
def run(self):
self.setTerminationEnabled(True)
"""
the code the gui runs
"""
class UI(QMainWindow):
def __init__(self):
super(UI,self).__init__()
#load the UI file
uic.loadUi("test.ui", self)
self.Class_instance = Class_Thread()
#define the widgets
self.start_button = self.findChild(QPushButton,"start_button")
self.reset_button = self.findChild(QPushButton,"reset")
self.exit_button = self.findChild(QPushButton,"exit")
self.start_button.clicked.connect(self.start_check)
self.reset_button.clicked.connect(self.reset_code)
self.exit_button.clicked.connect(QtCore.QCoreApplication.instance().quit)
#show the app
self.show()
def reset_code(self):
self.Class_instance.quit()
self.reset_button.setEnabled(False)
self.start_button.setEnabled(True)
def start_check(self):
self.reset_button.setEnabled(True)
self.start_button.setEnabled(False)
self.CC_Instance.start()
app = QApplication(sys.argv)
UIWindow = UI()
UIWindow.show()
UIWindow.activateWindow()
app.exec_()
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 have a PyQt wizard that includes a dialog box that asks the user a question. This dialog box is optional and only for use if the user wants it. A button sends a signal that the app receives and opens the window. The problem I have is that when the dialog is closed, it closes the whole app with it. How do I make sure that when the dialog is closed, the main app stays open and running? Here the code that handles the dialog box:
def new_item(self):
app = QtGui.QApplication(sys.argv)
Dialog = QtGui.QDialog()
ui = Ui_Dialog()
ui.setupUi(Dialog)
Dialog.exec_()
I tried adding a 'Cancel' button to close it manually but the result was the same, the whole app closed.
QtCore.QObject.connect(self.cancel, QtCore.SIGNAL(_fromUtf8("clicked()")), Dialog.close)
You shouldn't create new QApplication objects in your code, and I am not surprised that destroying that object closes the application.
Your code should look something like this:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.dialog = QtGui.QMessageBox(self)
self.dialog.setStandardButtons(QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)
self.dialog.setIcon(QtGui.QMessageBox.Question)
self.dialog.setText("Click on a button to continue.")
self.pushButtonQuestion = QtGui.QPushButton(self)
self.pushButtonQuestion.setText("Open a Dialog!")
self.pushButtonQuestion.clicked.connect(self.on_pushButtonQuestion_clicked)
self.layoutHorizontal = QtGui.QHBoxLayout(self)
self.layoutHorizontal.addWidget(self.pushButtonQuestion)
#QtCore.pyqtSlot()
def on_pushButtonQuestion_clicked(self):
result = self.dialog.exec_()
if result == QtGui.QMessageBox.Ok:
print "Dialog was accepted."
elif result == QtGui.QMessageBox.Cancel:
print "Dialog was rejected."
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())
Try to use Dialog.reject instead of Dialog.close
.close() method is being used mith QMainWindow Widget, .reject() with QDialog.
In my case, I had QSystemTrayIcon as an "entry point" to my app instead of QMainWindow or QWidget.
Calling .setQuitOnLastWindowClosed(False) on my main QApplication instance helped, thanks to this answer
It's difficult to describe what I want to do here, so here's what I want:
-Click a button that "pops up" a dialog.
-This dialog, however, should be non-modal.
-I'd also like for it to be sort of 'locked' with the parent app so if I moved it, it too would come with.
It seems like i'm more or less trying to describe an in-line popup, but I'm not sure if that's what it's called, or even how I should go about approaching this problem. So, PyQt gurus, how would you make something like this? Thanks in advance.
To get a modeless dialog, open it with show() rather than exec_().
To ensure that the dialog is "locked with the parent app", set a parent window in the dialog's constructor.
from PyQt4 import QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
button = QtGui.QPushButton('Open Dialog', self)
button.clicked.connect(self.handleOpenDialog)
self.resize(300, 200)
self._dialog = None
def handleOpenDialog(self):
if self._dialog is None:
self._dialog = QtGui.QDialog(self)
self._dialog.resize(200, 100)
self._dialog.show()
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
for Qt5:
I ignore why dialog.setWindowModality(QtCore.Qt.NonModal) won't work, but
dialog.run()
dialog.exec_()
will do. run will make the dialog non modal and exec_ will block it until the user input.