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
Interactivity is fragile #4732
Comments
Continuing the discussion on pull request #4506 ,
I would suggest to make use of inheritance here. Something along these lines: # In class Artist:
def stale(self):
self.axes.stale = True
# In class Axes:
def stale(self):
self.figure.stale = True
# In class Figure:
def stale(self):
self.canvas.draw_idle() |
It looks nice from the standpoint of simplicity and consistency, but I don't understand all the implications. |
It turns out alot, poor chaining with the python repl hook installed is what is stalling out the tests. It causes the system to choke on the system monitor example because it is calling |
I thought the python repl hook was not being used any more. |
Sorry, poor wording on my choice. I meant the callback we are using in vinilla python repl to trigger the auto-redraw. The behavior reported the the OP makes sense, there is an exception during the draw which means it never gets to the line that marks the figure as not-stale so the next time you try to mark it as stale it does not trigger the 'the stale state has been flipped, redraw' logic (because in plain python we are hooking in via a call back on the stale state of the figure). In IPython this would not be the case, it would try to re-draw the figure after every code execution. Probably the figure's draw logic should be wrapped in a try-finally so it gets marked as not-stale so long as drawing it is attempted which will fix this problem and mean IPython won't spam you with exceptions. |
Then override draw_idle in the noninteractive backend. Or better yet, make draw_idle a no-op in FigureCanvasBase. The noninteractive backends don't need to call draw until the figure is saved. And for interactive backends, calling draw from draw_idle breaks the separation between the frontend and the backend.
I have not yet seen a good reason of why the stale property is needed at all; it duplicates the efforts of FigureCanvasTkAgg._idle. The only reason I can imagine is to optimize the code, but there are simpler ways to achieve that. Anyway we should have the design right first before optimizing. |
I do not understand how |
As far as I can tell from the current code in master, the stale flag is only used to prevent multiple calls to draw_idle (to prevent idle_draw getting scheduled more than once between draws). But because of the _idle flag, idle_draw won't get scheduled more than once anyway, so multiple calls to draw_idle are harmless. If there is no other purpose of the stale flag, we may as well get rid of it. |
The design philosophy is that OO calls should never any sort of draw, they should only mutate the state of the object which will be reflected the next time that the plot is redrawn. I do not have the history of this, but that seems to be consistent with all of the existing code. This is part of the separation between the mpl/artist and the canvas/backend sides of the library. At it's core MPL is a framework for describing what should be in the plot and providing hooks to, if given a backend, draw a pretty picture. Under this model it is the job of the code embedding to decide if/when to trigger a redraw, the fact that the the gui backends have a notion of It happens that This will also be important going forward with the new |
"should never perform any sort of draw"? or "should never schedule any sort of draw"? |
Either. |
If so, then the current master breaks this philosophy. import matplotlib
matplotlib.use("tkagg")
from pylab import *
figure()
l, = plot([4,1,5])
l.set_linewidth(10) then the call to l.set_linewidth causes a call to draw_idle: l.set_linewidth sets l.stale to True with all of this happening before l.set_linewidth returns. |
That is showing that this is working as intended. I assume you have What I want to think of This is much simpler with IPython which has a 'post_code_execute` hook. |
@mdehoon I am a bit surprised by your resistance to this because as I recall the idea for artists being able to know if they are stale and triggering re-draws based on that was put in my head by a conversation with you. |
Yes I think that that is the right way of doing this; in the example above, l.set_linewidth should call draw_idle. My objection is against artists having 'stale' as a property; I think it is sufficient for 'stale' to be a function that calls draw_idle. The artist does not need to remember if it's stale or not. |
I had a conversation with @pzwang this evening and he endorsed this pattern of marking artists as invalid. I still do not understand any down sides to the artists knowing if they are stale/invalid, I can see many upsides to them knowing (offers more flexibility to embedding programs/user control of re-draw cadenc, will work nicely with exporters, will work with out figures having a canvas) and many down sides of the OO layer directly calling draw_idle. |
With the current master, I am finding that interactivity can break in case of errors.
This is one example:
Resizing the figure restores interactivity.
The text was updated successfully, but these errors were encountered: