You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Sliders unresponsive when created inside a function #3105
When a Slider is created inside a function, it is typically non-responsive (does not react to the mouse):
from matplotlib import pyplot
from matplotlib.widgets import Slider
sliders = []
def open_fig_with_slider():
pyplot.figure()
slider = Slider(pyplot.axes([0.25, 0.1, 0.65, 0.03]),
'Slider', 0, 1, valinit=0.5)
# sliders.append(slider)
open_fig_with_slider()
pyplot.show() # The slider is unresponsive unless append above is un-commented
The problem appears to be fixed by keeping a reference to the slider. The closest thing I've found in the documentation is that a canvas only keeps weak references to its callbacks (http://matplotlib.org/users/event_handling.html). However, the connection with sliders is not so obvious—if there is any.
If I'm not mistaken, this behavior is not documented, so it would be nice to add it to the documentation, if it is normal.
During the Slider__init__ function one of it's instance methods is registered as a call-back to the canvas so the issue with weak-refs is exactly correct explanation of what is going on.
A quibble over language, I would say that the slider is 'non-responsive', not 'blocking' which is a term I associate with stopping execution of the whole program.
OK, for "non-responsive", I updated the issue. Thanks.
It would be good if the documentation mentioned how to manage Sliders (widgets?) created in functions that make plots (which can be quite common in programs that make multiple figures).
I'm not sure if the method in the example I gave is a good idea (global reference kept), as I'm afraid that it may keep too many Matplotlib objects alive even when the figure is closed.
On the other hand, adding an instance attribute to the canvas, that holds the slider, probably defeats the purpose of using of a weak reference in the first place for one of its instance methods…
In any case, these questions show that documenting how to use sliders when creating plots in functions would be useful!
I am planning to add a line to either the Silder doc or the top-level widget doc saying "In order for the widgets to remain responsive you must maintain a reference to them".
My suggestion on how to do this is to have the function return the figure, axes, and widget objects it creates:
Are you implying that users must keep references to these three objects in order to keep sliders responsive? the smaller the number of objects, the better… (more legible code, less memory consumed by figures that were closed but maintained in memory because of the references created by the caller, etc.).
I like the idea of returning the objects that must be kept in memory, because the caller can handle them locally (no need for a global variable, in general) and release them when/if needed.
The only obligatory reference to keep is the slider, there are referances to the figure in the pyplot state machine and to the axes from the figure (and vice-versa). However to use the figure/axes in the OO style requires having the other two so might as well return them (as if you are writing scripts you should be using the OO style anyway ;) ).
I'm glad that only one reference (to the Slider) has to be kept. (Hopefully this does not lead to the figure being kept around if is closed, though, but that's a different issue.)
I would suggest that you make explicit in the documentation that "the only obligatory reference to keep is the slider", so that the code is "lighter". (I rarely feel the need to use the OO style, mostly because each figure is created in a single function, or in a well defined part of the code, so there is no ambiguity about what the code is doing, and it is also slightly more legible thanks to the use of fewer variables.)
I would argue that using pyplot (for anything but calling plt.subplots) always makes the code less readable as now the behavior is dependent on hidden global state (what the current figure/axes is).
It is clear what is going on, the plotting logic is separate from the science/business logic, there is no ambiguity over which axes the artists will go to, and your my_plotting function is trivially re-usable. It can be directly used in custom mpl embeddings (which, yes, is a corner case but is an awesome corner case). If you want to do multi-axes figures all you have to do is
fig, (ax1, ax2) = plt.subplots(1, 2) # or gridspec, or by hand
my_plotting(ax1, my_data1)
my_plotting(ax2, my_data2)
I see what you mean, and I agree that your example is clear. However, I never personally came across a case where plotting in the current axes was unsafe (as long as it is documented):
def my_plotting(data):
"""
Plot in the current axes…
"""
# do plotting/formatting/whatever
my_data = get_data()
my_data_filtered = filter_function(my_data)
plt.figure()
my_plotting(my_data_filtered)
I agree that knowing in which axes the plotting function does its job generally requires one to check what the current axes are, and that in principle they can been changed as a side effect of some intervening functions. However, in many cases, the state-based approach is pretty clear, and the smaller number of variables arguably makes the code faster to read, and fewer variables need to be kept in working memory, so that's more legible, in my book. Again, though, I agree with you: there is a use for more a more explicit behavior.
There is an entire genre of SO questions that are "how do I merge two figures" and "why are all of my plots going to the wrong axes?" which the answer boils down to "don't use the state machine". I think that we need to shift the way we introduce mpl to be OO first and then, by the way, the state-machine makes your life easy when working interactively.
I see. Then it makes sense to encourage Matplotlib users to use the more explicit approach.
A personal note: I do prefer "explicit is better than implicit"; I somehow managed, over a few years now, to be sheltered from the problems you quote even though I've been using writing less explicit code. :) I guess that being fully aware of the concept of current axes and documenting that some functions plot in them helps.
Activity
tacaswell commentedon Jun 2, 2014
During the
Slider__init__function one of it's instance methods is registered as a call-back to the canvas so the issue with weak-refs is exactly correct explanation of what is going on.A quibble over language, I would say that the slider is 'non-responsive', not 'blocking' which is a term I associate with stopping execution of the whole program.
[-]Sliders blocked when created inside a function[/-][+]Sliders unresponsive when created inside a function[/+]lebigot commentedon Jun 2, 2014
OK, for "non-responsive", I updated the issue. Thanks.
It would be good if the documentation mentioned how to manage Sliders (widgets?) created in functions that make plots (which can be quite common in programs that make multiple figures).
I'm not sure if the method in the example I gave is a good idea (global reference kept), as I'm afraid that it may keep too many Matplotlib objects alive even when the figure is closed.
On the other hand, adding an instance attribute to the canvas, that holds the slider, probably defeats the purpose of using of a weak reference in the first place for one of its instance methods…
In any case, these questions show that documenting how to use sliders when creating plots in functions would be useful!
tacaswell commentedon Jun 2, 2014
I am planning to add a line to either the
Silderdoc or the top-level widget doc saying "In order for the widgets to remain responsive you must maintain a reference to them".My suggestion on how to do this is to have the function return the figure, axes, and widget objects it creates:
lebigot commentedon Jun 3, 2014
Are you implying that users must keep references to these three objects in order to keep sliders responsive? the smaller the number of objects, the better… (more legible code, less memory consumed by figures that were closed but maintained in memory because of the references created by the caller, etc.).
I like the idea of returning the objects that must be kept in memory, because the caller can handle them locally (no need for a global variable, in general) and release them when/if needed.
tacaswell commentedon Jun 3, 2014
The only obligatory reference to keep is the slider, there are referances to the figure in the pyplot state machine and to the axes from the figure (and vice-versa). However to use the figure/axes in the OO style requires having the other two so might as well return them (as if you are writing scripts you should be using the OO style anyway ;) ).
lebigot commentedon Jun 4, 2014
I'm glad that only one reference (to the Slider) has to be kept. (Hopefully this does not lead to the figure being kept around if is closed, though, but that's a different issue.)
I would suggest that you make explicit in the documentation that "the only obligatory reference to keep is the slider", so that the code is "lighter". (I rarely feel the need to use the OO style, mostly because each figure is created in a single function, or in a well defined part of the code, so there is no ambiguity about what the code is doing, and it is also slightly more legible thanks to the use of fewer variables.)
Thank you for your help. :)
tacaswell commentedon Jun 4, 2014
I would argue that using pyplot (for anything but calling
plt.subplots) always makes the code less readable as now the behavior is dependent on hidden global state (what the current figure/axes is).I strongly suggest you write functions like this
It is clear what is going on, the plotting logic is separate from the science/business logic, there is no ambiguity over which axes the artists will go to, and your
my_plottingfunction is trivially re-usable. It can be directly used in custom mpl embeddings (which, yes, is a corner case but is an awesome corner case). If you want to do multi-axes figures all you have to do islebigot commentedon Jun 4, 2014
I see what you mean, and I agree that your example is clear. However, I never personally came across a case where plotting in the current axes was unsafe (as long as it is documented):
I agree that knowing in which axes the plotting function does its job generally requires one to check what the current axes are, and that in principle they can been changed as a side effect of some intervening functions. However, in many cases, the state-based approach is pretty clear, and the smaller number of variables arguably makes the code faster to read, and fewer variables need to be kept in working memory, so that's more legible, in my book. Again, though, I agree with you: there is a use for more a more explicit behavior.
tacaswell commentedon Jun 4, 2014
There is an entire genre of SO questions that are "how do I merge two figures" and "why are all of my plots going to the wrong axes?" which the answer boils down to "don't use the state machine". I think that we need to shift the way we introduce mpl to be OO first and then, by the way, the state-machine makes your life easy when working interactively.
I am also going to fall back to pep-20 (http://legacy.python.org/dev/peps/pep-0020/): "explicit is better than implicit"
lebigot commentedon Jun 5, 2014
I see. Then it makes sense to encourage Matplotlib users to use the more explicit approach.
A personal note: I do prefer "explicit is better than implicit"; I somehow managed, over a few years now, to be sheltered from the problems you quote even though I've been using writing less explicit code. :) I guess that being fully aware of the concept of current axes and documenting that some functions plot in them helps.
DOC : added note about maintain ref to widgets
tacaswell commentedon Jun 10, 2014
@lebigot Please check that PR #3132 makes this clear enough.
lebigot commentedon Jun 10, 2014
Thank you. I left a couple of comments on small typos.
It would be useful to also add a sentence to the online documentation, in the introduction about widgets at http://matplotlib.org/api/widgets_api.html#gui-neutral-widgets. :)