0

Background: It's quite common for applications that hide itself (or minimize it, or just close the "main window") when "x" button is clicked, instead of closing the whole application. In mac, closing window is "command+w" while closing application is "command+q". They are different.

Problem: closeEvent is inadequate. In PyQT5, wo can hook the closeEvent in whatever QWindow/QDialog/QWidget and reject the event. We can hide the window by self.hide().

def closeEvent(self, event):
  event.reject()
  self.hide()

However, if we do that, the window becomes unstoppable because other ways of closing (Command+Q or right click the icon then quit or quit via menubar) are all blocked. (That is funny because the app cannot be closed anyway.)

Potential solution:

  1. Hide the x button. It is an expedient way but is off this question because what I want is to hide the window via x button rather than removing the button.
  2. Find a different event other than closeEvent which can distinguish X button from others and block that event. But unforunately I cannot see such events. If there exists the event, maybe I will have another problem[1]
  3. Continue to block closeEvent and hook other events that are binded to "command+Q" or "Quit by right click". Then force quit the application in that even hook. In this case my question becomes: can I connect quit or (command+Q) via some Qt objects?
  4. Maybe there are other better solutions.

This post might be similar to How to write event for window close(X) button in pyqt4 python but is absolutely different from the so-called "duplicate" of "How to disable the close button of window using Qt"

[1]. It's not a big problem but relevent. Once the window is hidden, we need a "click to the icon in taskbar/dock" event to show it. Is there some object binding to the event?

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
Zealseeker
  • 823
  • 1
  • 7
  • 23
  • I'm not sure I follow you. You want to *still* be able to close a window/quit program when using cmd+q, but just hide it when using cmd+w or the close button? – musicamante Feb 21 '21 at 23:43
  • @musicamante Exactly! – Zealseeker Feb 21 '21 at 23:49
  • Mh, I seem to remember I faced a similar problem, as I remember the strange behavior of duplicate closeEvents. Unfortunately I cannot run macOS right now, but you could try to check *if* setting an internal flag connected to the [`aboutToQuit`](https://doc.qt.io/qt-5/qcoreapplication.html#aboutToQuit) signal is actually done *before* the final `closeEvent` is called: if that's the case, you can just check if it's spontaneous *and* if that flag is already set. – musicamante Feb 21 '21 at 23:52
  • @musicamante Thanks! Currently it doesn't work for me because the "spontaneous" is always true in my case. I will try it in Windows. And also maybe it can work if I put everything in a QWidget rather than QMainWindow. – Zealseeker Feb 22 '21 at 00:12

3 Answers3

1

Try setting WA_QuitOnClose to False. The following works for me:

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = QMainWindow()
    window.setCentralWidget(QWidget())
    window.setAttribute(Qt.WA_QuitOnClose, False)
    QShortcut(QKeySequence.Close, window, window.close)
    window.show()
    sys.exit(app.exec_())
alec
  • 5,799
  • 1
  • 7
  • 20
  • Hi @alec Thanks. It is useful if I just want to close window (cmd+w) without closing the entire application (cmd+q). But I want to hide the window rather than really close it. I mentioned cmd+w because I wanted to distinguish cmd+w and cmd+q so that I could hook the right event. – Zealseeker Feb 28 '21 at 10:47
1

Here is an example of a gui that can be hidden without stopping a program by closing it in the top left while still being able to use the "command+q" function.

import tkinter as tk

def run_gui_example():
    global root
    # Create the main window
    root = tk.Tk()
    root.title("Simple GUI Example")

    # return root
    root.protocol("WM_DELETE_WINDOW", on_close) #Hide window instead of close it

    # Start the GUI event loop
    root.mainloop()

def on_close():
    root.withdraw()
    print('window hidden, program running')

# Call the function to run the GUI example
run_gui_example()
0

One way to check whether the application is closing (by right clicking the icon in dock or cmd+Q) is to catch the events of application.

I think the event chain is: Application closing (19) -> Window closing (19) -> AboutToQuit

So if the window closing is blocked, nothing is helpful in AboutToQuit. We just need to catch the upstream event, application closing.

class MyApplication(QApplication):
    def event(self, e):
        if e.type() == 19: # Close event
            sys.exit() # Or we can do other things to "force close"
class MyWindow(QMainWindow):
    def closeEvent(self, e):
        e.ignore()

if __name__ == '__main__':
    app = MyApplication(sys.argv)
    window = MyWindow()
    window.show()
    sys.exit(app.exec_())
Zealseeker
  • 823
  • 1
  • 7
  • 23