Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Tk backend improvements#17789

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged

Conversation

richardsheridan
Copy link
Contributor

@richardsheridanrichardsheridan commentedJun 28, 2020
edited
Loading

PR Summary

This PR makes changes to the Tk backend to prevent unnecessary recursion into the Tcl event loop, REPL hangs on figure close, and unnecessary draws on resize plus some extras.

Background

I have a project to explorestructured concurrency as applied to GUI design. Specifically, I have written a tkinter "app" that hoststrio inguest mode, and then uses callbacks exclusively to launch async functions, which themselves are eventually scheduled to run usingTk.after_idle.

Changes

Without going in to detail, it is easy to wind up in a situation whereFigureCanvasTkAgg.draw is called from an async function in such a way that it deadlocks the event loop. Fortunately, there are absolutely no legitimate reasons to invokeTk.update ortk.update_idletasks while an event loop is running, so this PR removes them entirely.

Other changes are less directly related to my primary motivation but became apparent as I was diagnosing the deadlock issue.

Resize events callFigureCanvasTkAgg.draw inFigureCanvasTkAgg.resize unnecessarily, asFigureCanvasBase.resize_event callsFigureCanvasTk.draw_idle. This PR removes that line, so resizes will be drawn eventually, without hogging the event loop.

NavigationToolbar2Tk has a special destroy method to delete it'stkinter.StringVar attribute namedmessage. It will be destroyed via Tk object tree (master=self) and GCd by Python (only referred to by other self attributes) without this, so this PR removes it outright.

FigureManagerTk has a destroy method that is completely ineffective and also fails to stop the Tk mainloop. This PR rewrites the method entirely usingprotocol("WM_DELETE_WINDOW",...) rather thanbind("<Destroy>",...). (Cherry picked and merged to master in#17802)

FigureCanvasTk is missing methodsstart_event_loop andstop_event_loop and so relies onFigureCanvasBase pure Python event loop implementations. This PR reimplements them following the example of the Qt and wx backends. However, I can't find any test code or Matplotlib API functions that actually exercise these methods.

Summary

These changes have minor or no user facing effect unless somewhereTk.after_idle schedules a callback that containsTk.update_idletasks. Writing this summary, it seems the fix toFigureManagerTk may actually the most user-noticeable (except most users are probably using Qt.) Not knowing whyTk.update_idletasks was sprinkled about in the first place, it is hard to say if this is backward-incompatable; it should be fine. A test to assert thatupdate orupdate_idletasks is never called when using the Tk backend would be appropriate, as well as a test of theFigureCanvasTk event loop methods, but I can't fathom how the backend tests are constructed, so some help would be appreciated.

PR Checklist

  • Has Pytest style unit tests
  • Code isFlake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/api_changes.rst if API changed in a backward-incompatible way

@richardsheridan
Copy link
ContributorAuthor

One of theupdate_idletasks calls was added in#9956, fixing a bug without diagnosing the bug origin. The actual issue was failing to make use ofprotocol.git blame puts the other insertions at 15-16 years ago!

@tacaswell
Copy link
Member

To check my understanding, in themainloop function at the module level we are using the c-event loop (because we grabmanagers[0].window.mainloop()?

@tacaswell
Copy link
Member

On linux the code from#9856

%matplotlibtkimportsysimportmatplotlibimportmatplotlibasmplimportpylabaspltprint('OS: %s'%sys.platform)print('matplotlib version: %s'%mpl.__version__)print('Backend: %s'%matplotlib.get_backend())foriinrange(3):plt.figure()plt.close()plt.figure()plt.plot([0,1],[0,1])plt.show()

does not crash, but gives me

OS: linuxmatplotlib version: 3.3.0rc1.post102+g69e5b9e4f8Backend: TkAggIn [3]: can't invoke "event" command: application has been destroyed                                                                                                     while executing"event generate $w <<ThemeChanged>>"    (procedure "ttk::ThemeChanged" line 6)    invoked from within"ttk::ThemeChanged"can't invoke "event" command: application has been destroyed    while executing"event generate $w <<ThemeChanged>>"    (procedure "ttk::ThemeChanged" line 6)    invoked from within"ttk::ThemeChanged"can't invoke "event" command: application has been destroyed    while executing"event generate $w <<ThemeChanged>>"    (procedure "ttk::ThemeChanged" line 6)    invoked from within"ttk::ThemeChanged"

when run from inside of IPython, but no warnings when run as a script which makes me think that the problem is actually coming from the prompt toolkit inputhook (seehttps://github.com/ipython/ipython/blob/master/IPython/terminal/pt_inputhooks/tk.py to send you down all of the tk related rabbit holes...)? Are we sure that this will not bring back that crash on the mac? On v3.2.2 I get no warnings, but see the closed windows flicker in and out of existence.

@richardsheridan
Copy link
ContributorAuthor

To check my understanding, in themainloop function at the module level we are using the c-event loop (because we grabmanagers[0].window.mainloop()?

That is correct, to my understanding. for consistency "mainloop" should be everywhere and always the _tkinter.tkapp.mainloop method eventually.

Are we sure that this will not bring back that crash on the mac?

Unfortunately, I don't have a mac to test on. Can we get a test going on CI?

#9856

I get no messages like that on windows from python repl or ipython. I do see the blinky root windows on ipython though!

@tacaswell
Copy link
Member

I have access to a mac that I can test on, but down a rabbit hole of figuring out whyplt.ion() in the plain-python prompt is broken in 3.3.0rc1 :(

@tacaswell
Copy link
Member

Fortunately, the bug I thought I found is fixed on both 3.3.x and master as of#17764 !

@tacaswell
Copy link
Member

I can run the code and verify that it does not crash but am having issues getting VNC to work so ~95% sure this is OK. If you are feeling motivated, I think another subprocess test would work here, if not I'll try to take care of it.

@jklymakjklymak requested a review fromtacaswellJuly 15, 2020 22:53
@jklymak
Copy link
Member

@tacaswell, is this still OK?

@QuLogic
Copy link
Member

FigureCanvasTk is missing methodsstart_event_loop andstop_event_loop and so relies onFigureCanvasBase pure Python event loop implementations. This PR reimplements them following the example of the Qt and wx backends. However, I can't find any test code or Matplotlib API functions that actually exercise these methods.

These should be exercised withplt.pause orplt.ginput. The former is covered bytest_backends_interactive.py, though the latter you'd have to check yourself interactively, I think.

@QuLogic
Copy link
Member

I don't really understand thedestroy bit, so if tests are working, it's probably fine. Everything else looks reasonable, though.

@richardsheridan
Copy link
ContributorAuthor

FYIplt.ginput checks out afterc1cffde. Also, for more on the effects of fixing FigureManager destruction, seethis comment.

No need to draw if event loop is responsive
message will be destroyed via tk object tree (master=self) and GCd by Python (only referred to by other self attributes)
tkinter has a perfectly good event loop written in C
timeout is given in float seconds according to FigureCanvasBase, but tk needs int milliseconds
start_event_loop semantics are equivalent to update with timeout < 0.001
@richardsheridan
Copy link
ContributorAuthor

@QuLogic I believe I have addressed your comment with this last commit, my rebase on master might have made that unclear!

Copy link
Member

@QuLogicQuLogic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Small test improvements, but LGTM.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@tacaswelltacaswelltacaswell approved these changes

@QuLogicQuLogicQuLogic approved these changes

Assignees
No one assigned
Labels
Projects
None yet
Milestone
v3.4.0
Development

Successfully merging this pull request may close these issues.

6 participants
@richardsheridan@tacaswell@jklymak@QuLogic@charwick@anntzer

[8]ページ先頭

©2009-2025 Movatter.jp