Skip to content

Close all plot windows of a blocking show() on Ctrl+C #27064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 18, 2023

Conversation

timhoffm
Copy link
Member

@timhoffm timhoffm commented Oct 11, 2023

Addresses the Qt part of #23385 (comment).

It appears that qapp.quit() does not automatically close the windows of the app. We therefore do it explicitly.

A unit test for this would be quite complex. Test this by hand by Ctrl+C in an interactive shell.

@timhoffm timhoffm added this to the v3.9.0 milestone Oct 11, 2023
@story645
Copy link
Member

test failures definitely seem related?

@timhoffm
Copy link
Member Author

timhoffm commented Oct 16, 2023

Re: test failures

The semantics of

def _maybe_allow_interrupt(qapp):

is somewhat unclear: What is the qapp parameter?

with _maybe_allow_interrupt(event_loop):

passes in a QEventLoop

with _maybe_allow_interrupt(qapp):

passes in a QApplication

@tacaswell @anntzer do you have a fundamental understanding of the _may_allow_interrupt() context manager? You have written the former/latter calls. Do we really want the context manager take QApplication and QEventLoop respectively? If so, that should be documented.

I've now "fixed" this PR by checking hasattr(qapp, "allWindows") before trying to close any windows, but that feels like fixing symptoms.

@anntzer
Copy link
Contributor

anntzer commented Oct 17, 2023

Do we really want the context manager take QApplication and QEventLoop respectively?

Yes. Really, _maybe_allow_interrupt takes as parameter something on which it can call the quit() method whenever ctrl-c is triggered (perhaps the argument should be quit_func -- the bound methods qapp.quit and event_loop.quit, respectively). This can be the full QApplication, in the usual plt.show() case (QApplication runs its own "main" event loop -- see https://doc.qt.io/qt-6/qapplication.html#exec), but can also be an inner event loop (an actual QEventLoop), which manages the context for plt.pause().

@timhoffm
Copy link
Member Author

Thanks for the clarification. My take on this is:

  • we want this context manager to be general puropse: take something that is quittable.
  • for QApplication, we need to close the windows in the handler

Thus, the hasattr(qapp, "allWindows") is a valid approach and we should adapt the documentation for the intended inputs on _may_allow_interrupt().

@anntzer
Copy link
Contributor

anntzer commented Oct 17, 2023

  1. I think you can just call closeAllWindows(), which does the loop for you?
  2. I'm still a bit confused why in your case, quit() doesn't close all windows -- shouldn't it do so? (I haven't actually tried running anything.)

@tacaswell
Copy link
Member

From the docs https://doc.qt.io/qt-6/qcoreapplication.html#quit it looks like it does not actually close all open windows:

import sys
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QPushButton


app = QApplication(sys.argv)
widget = QWidget()

textLabel = QLabel(widget)
textLabel.setText("Hello World!")
textLabel.move(110, 85)

widget.setWindowTitle("PyQt6 Example")

button1 = QPushButton(widget)
button1.setText("Button1")
button1.move(64, 32)

def test():
    print("pushed the button!")
    app.quit()
    print("tried to quit!")

button1.clicked.connect(test)


widget.show()


input("hit enter: ")

In the case of making an EventLoop object you likely do not want to close all of the windows when you quit the loop (as they tend to be used as "sub" event loops).

None of this works with PySide because they do not install a input hook.

@tacaswell
Copy link
Member

#26970 is related in spirit

Addresses the Qt part of matplotlib#23385.

It appears that `qapp.quit()` does not automatically close the windows
of the app. We therefore do it explicitly.

A unit test for this would be quite complex. Test this
by hand by Ctrl+C in an interactive shell.
@timhoffm
Copy link
Member Author

I now use closeAllWindows(). While it may not be the prettiest solution, I think this is a partial and viable improvement.

@ksunden ksunden merged commit 07c0239 into matplotlib:main Oct 18, 2023
@timhoffm timhoffm deleted the fix-ctrl-c-qt branch October 18, 2023 21:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants