PyQt5 closeEvent reimplementation
-
i've just started using
PyQt5
and i like it so far!but as soon as you start to write code... bugs come in :D
i've subclassed
QMainWindow
and reimplementedcloseEvent()
, like this:def closeEvent(self, event): """docstring""" self.__write_settings() event.accept()
i launch the application by
python main.py
from integrated terminal (i'm using visual studio code). when i try to close the program from the terminal withCtrl+C
, nothing happens. then i try to close it withX
on window, i get:Traceback (most recent call last): File "D:\...\MainWindow.py", line 143, in closeEvent def closeEvent(self, event): KeyboardInterrupt
if i remove this method and do the same again, i get:
Traceback (most recent call last): File ".\main.py", line 16, in <module> main() File ".\main.py", line 13, in main sys.exit(app.exec_()) KeyboardInterrupt
is this some windows-specific behavior or what?
-
Hi,
Using Ctrl+C you are not ending your application properly, you are interrupting it. Hence the close event is not called.
You should call the base class closeEvent once you're done doing your stuff.
-
Personally, I usually write down settings in the destructor of a class rather than close event.
As for your trace back, what are you doing in
__write_settings
? -
@user4592357
I really don't understand what it is you want to happen or not happen when user clicks Ctrl+C. But have you looked at:- https://stackoverflow.com/questions/4938723/what-is-the-correct-way-to-make-my-pyqt-application-quit-when-killed-from-the-co
- https://stackoverflow.com/questions/5160577/ctrl-c-doesnt-work-with-pyqt
- the other various pages out there for handling Ctrl+C from PyQt5
?
-
i've tried to do what's said in the 2nd link, namely:
import signal signal.signal(signal.SIGINT, signal.SIG_DFL)
it kinda works. i mean, when i do "ctrl+c" then close the window with
X
, i don't getKeyboardInterrupt
execption.
it gives this error though when pressing ctrl+c:QObject::~QObject: Timers cannot be stopped from another thread
i don't understand how and why this works though (please explain!). i read thatSIG_DFL
is the default handler but still no idea.what i want is the application to exit gracefully when
Ctrl+C
is pressed. but i guess from the explanation of the article that it's not possible to interrupt python while qt loop is running -
This is not my area, so I can only give you some ideas/explanations. I'm newish to Qt & Python/PyQt. My background is more pure C stuff.
When
Ctrl+C
is pressed, a signal is raised, "interrupt signal",SIGINT
. Thesignal
function has 3 possible "dispositions" which can be specified forSIGINT
:SIG_DFL
: Default, which is terminate the program.SIG_IGN
: Ignore, do nothing, treat it just like a key press.- Some function: call that function.
Reading the posts, the problem is that signal is passed to the Python interpreter which is running your Python code, but that interpreter is not executing while you are in a Qt loop such as the standard
sys.exit(app.exec_())
a Qt program is usually sitting in. Hence all the shenanigan solutions, none of which work perfectly.My own thought would be: why are you/do you need to react to
Ctrl+C
at all? Given that with Qt you have a GUI program with a window, it's not "normal" to have anywhere to pressCtrl+C
in the first place. When you run, say, Notepad, you don't have a terminal to press anything in anyway. You just use the GUI to close the main window, or similar. there is no "asynchronous interrupt signal". I don't know about your "from integrated terminal (I'm using visual studio code)". When I start my Python/PyQt application from a terminal (Linux), pressingCtrl+C
in that launching terminal does nothing (other than echo^C
in the terminal window), it does not exit my application.Furthermore, there is no reason your Python app should be launched from a terminal in the first place. Since it's a GUI application, aren't you going to provide for it being launched from a desktop icon? Then there will be no invoking terminal for anyone to press
Ctrl+C
in the first place.I'd be tempted to turn
Ctrl+C
signal handling off (signal.signal(signal.SIGINT, signal.SIG_IGN)
), and if you really want to recognise and act onCtrl+C
do it purely in a Qt key press event like https://stackoverflow.com/a/24252234/489865. That would meanCtrl+C
is only handled as & when it's pressed in the Qt application window and Qt is prepared to handle the event.Otherwise did you have a try of https://stackoverflow.com/a/11705366/489865 ? Ugly but might work acceptably.
Finally, if none of these is acceptable to you this is a question you might like to raise by joining up at https://www.riverbankcomputing.com/mailman/listinfo/pyqt. Since your question is about PyQt handling, the guys there are the absolute experts on what can or cannot be done from Python/PyQt, and I'm sure they'd tell you if there are any good alternatives.
Let us know!
-
thanks for a good answer. i've tried many "solutions" (including ones in the mentioned links) but none of works as a expected. now i understand why.
and you're right, since i'm going to deploy the application sooner or later, there is no need to worry about such things
-
btw, is there a way to pass command line arguments to a deployed app (sounds surreal but im asking)?
-
Do you mean like QCommandLineParser ? Or Python's argparse module ?
-
@user4592357
What do you mean about deployed app & argument passing? If by any chance you mean can you pass arguments once the app is launched from the desktop via a shortcut, then yes. In general, your PyQt app's__main__
seessys.argv
like normal. -
-
Can you give the reference ?
-
@SGaist
https://fatima-python.wikispaces.com/file/view/Rapid+GUI+Programming+with+Python+and+Qt.pdf
page 79 (book page, pdf page is 93) -
@user4592357
I wouldn't put anything as complicated as saving settings in a destructor. YourcloseEvent()
sounded right for this. Why are we suddenly discussing__del__()
? -
Because of this:
@SGaist said in PyQt5 closeEvent reimplementation:
Personally, I usually write down settings in the destructor of a class rather than close event.
That's what I do in C++.
-
@SGaist
Ohhh, didn't see that.Umm, far be it for me to disagree, but personally I wouldn't. I thought destructors were supposed to be cheap and have no side-effects. So many things can happen when saving settings. I wouldn't do it in C# FWIW, and I wouldn't do it in Python. (I also would not load settings in
__init__()
.) -
In the python case, that's indeed something debatable. Most of the time, people don't need to implement
__del__
.What I would do is to store the settings once you close the corresponding dialog so that you avoid the trouble you had with the unintended interruption you have (unless it's done while the dialog is open).
In the extreme case, you can even save you settings on modification if you have an "apply immediately" without cancel style of application preferences.