-
-
Notifications
You must be signed in to change notification settings - Fork 8.1k
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Tk backend improvements #17789
Conversation
|
One of the |
|
To check my understanding, in the |
|
On linux the code from #9856 %matplotlib tk
import sys
import matplotlib
import matplotlib as mpl
import pylab as plt
print('OS: %s'%sys.platform)
print('matplotlib version: %s'%mpl.__version__)
print('Backend: %s'%matplotlib.get_backend())
for i in range(3):
plt.figure()
plt.close()
plt.figure()
plt.plot([0,1],[0,1])
plt.show()does not crash, but gives me 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 (see https://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. |
That is correct, to my understanding. for consistency "mainloop" should be everywhere and always the _tkinter.tkapp.mainloop method eventually.
Unfortunately, I don't have a mac to test on. Can we get a test going on CI? I get no messages like that on windows from python repl or ipython. I do see the blinky root windows on ipython though! |
|
I have access to a mac that I can test on, but down a rabbit hole of figuring out why |
|
Fortunately, the bug I thought I found is fixed on both 3.3.x and master as of #17764 ! |
|
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. |
|
@tacaswell, is this still OK? |
These should be exercised with |
|
I don't really understand the |
|
FYI |
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
c1cffde to
880c6c5
Compare
|
@QuLogic I believe I have addressed your comment with this last commit, my rebase on master might have made that unclear! |
QuLogic
left a comment
There was a problem hiding this 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.
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 explore structured concurrency as applied to GUI design. Specifically, I have written a tkinter "app" that hosts trio in guest mode, and then uses callbacks exclusively to launch async functions, which themselves are eventually scheduled to run using
Tk.after_idle.Changes
Without going in to detail, it is easy to wind up in a situation where
FigureCanvasTkAgg.drawis called from an async function in such a way that it deadlocks the event loop. Fortunately, there are absolutely no legitimate reasons to invokeTk.updateortk.update_idletaskswhile 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 call
FigureCanvasTkAgg.drawinFigureCanvasTkAgg.resizeunnecessarily, asFigureCanvasBase.resize_eventcallsFigureCanvasTk.draw_idle. This PR removes that line, so resizes will be drawn eventually, without hogging the event loop.NavigationToolbar2Tkhas a special destroy method to delete it'stkinter.StringVarattribute 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.FigureManagerTkhas 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)FigureCanvasTkis missing methodsstart_event_loopandstop_event_loopand so relies onFigureCanvasBasepure 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 somewhere
Tk.after_idleschedules a callback that containsTk.update_idletasks. Writing this summary, it seems the fix toFigureManagerTkmay actually the most user-noticeable (except most users are probably using Qt.) Not knowing whyTk.update_idletaskswas sprinkled about in the first place, it is hard to say if this is backward-incompatable; it should be fine. A test to assert thatupdateorupdate_idletasksis never called when using the Tk backend would be appropriate, as well as a test of theFigureCanvasTkevent loop methods, but I can't fathom how the backend tests are constructed, so some help would be appreciated.PR Checklist