-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
ENH : add function to add displayhook #4091
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
Conversation
@tacaswell, Thanks, now I understand. It works on the normal ipython console, but not with the ipython qtconsole or the notebook. In the qtconsole:
If the present IPython API allows us to tap into that, then this seems like an elegant way to go. |
With which backend? I would not expect this to work with inline as you don't have a live figure. |
@tacaswell, I don't think I understand your last question. Running in a notebook or qtconsole with %matplotlib, one does have a live figure. Using qtconsole in particular is just like using a normal console. Somehow, however, the attempt to replace the ZMQShellDisplayHook is doing nothing:
|
Ah, i now understand. I assume this is something funny going on with where things get executed? I threw some print statements in and Python 3.4.2 (default, Jan 20 2015, 14:24:24)
Type "copyright", "credits" or "license" for more information.
IPython 2.3.1 -- An enhanced Interactive Python.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://binstar.org
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
%guiref -> A brief reference about the graphical user interface.
Using matplotlib backend: Qt4Agg
In [1]: import matplotlib.pyplot as plt
In [3]: import sys
In [4]: sys.displayhook
Out[4]: <IPython.kernel.zmq.displayhook.ZMQShellDisplayHook at 0x7ff1d17e8be0>
In [5]: plt.install_repl_displayhook
Out[5]: <function matplotlib.pyplot.install_repl_displayhook>
In [6]: plt.install_repl_displayhook()
<IPython.kernel.zmq.displayhook.ZMQShellDisplayHook object at 0x7ff1d17e8be0>
<function install_repl_displayhook.<locals>.displayhook at 0x7ff1c804c6a8>
In [8]: sys.displayhook
Out[8]: <IPython.kernel.zmq.displayhook.ZMQShellDisplayHook at 0x7ff1d17e8be0> (missing input/output is where I removed errors because I can't type) I was skimming the IPython code last night and kept seeing calls to update the user namespace which I suspect is related to this. I'll see if reviving the monkey-patching version of this work... |
@minrk Thoughts on this? What are we doing wrong and how bad of an idea is the monkey patching? |
@tacaswell IPython's display machinery is pluggable, it's just not on the DisplayHook object. If you want to call from IPython import get_ipython
ip = get_ipython()
# IPython >= 2
try:
ip.events.register('post_execute', draw_all)
except AttributeError:
# IPython 1.x
ip.register_post_execute(draw_all) This is what the * There are actually two events - |
last commit from right before running out of the office, not tested yet. |
It is missing something. With plain ipython and %matplotlib, the drawing is not triggered until the window is resized, or a zoom/pan is executed. |
@efiring I can't reproduce this: (dd_py3k)[tcaswell@arya ~]$ ipython
Python 3.4.2 |Continuum Analytics, Inc.| (default, Oct 21 2014, 17:16:37)
Type "copyright", "credits" or "license" for more information.
IPython 2.3.1 -- An enhanced Interactive Python.
Anaconda is brought to you by Continuum Analytics.
Please check out: http://continuum.io/thanks and https://binstar.org
? -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help -> Python's own help system.
object? -> Details about 'object', use 'object??' for extra details.
In [1]: import matplotlib.pyplot
In [2]: matplotlib.pyplot.install_repl_displayhook()
In [3]: %matplotlib
Using matplotlib backend: TkAgg
In [4]: import matplotlib.pyplot as plt
In [5]: ax = plt.gca()
In [6]: ax.plot(range(3))
Out[6]: [<matplotlib.lines.Line2D at 0x7f13740f0710>] I will add a commit installing the hook by default as you seem happy with this approach. |
This will work well for simple plots, but I think will fall on it's face for people with complex expensive (like seconds) draw time plots. We either need to add a way to control this (rc param that We probably should do both but do the switch first as it is easier/faster so we can get this merged and get people to play with it. |
current failure is related to sphinx 1.3 issues |
As an example of the problem with redrawing a complex plot when it shouldn't, try this in ipython, %matplotlib
import matplotlib.pyplot as plt
fig, axs = plt.subplots(20, 20)
5*10 First, creating the 400 empty subplots is horribly slow; this has been pointed out before, and remains an area where we need some streamlining. Redrawing is not as slow, but it still leads to a very noticeable lag in getting the prompt back after the trivial operation on the last line. |
@efiring I think I have (to first order) fixed that problem and started to make the %matplotlib
import matplotlib.pyplot as plt
fig, axs = plt.subplots(2, 2)
ln, = axs[0, 0].plot(range(3))
ln.set_color('r')
ln.set_linewidth(5) line-by-line. Each of the 'set_*' calls should auto-magically redraw. |
The exact plumbing of the call-backs probably needs some work. It may be a better idea to allow the artists to mark them selves as not-dirty in their This is where something like traitlets comes in super handy as we could stick the 'mark as dirty' logic it to some meta-class/class level feature and have it centralized (instead of spread out across all of the |
Each draw method does seem to be a logical place to put the marking. (Maybe we could call things "stale" rather than "dirty"--somehow it sounds better, and seems a little more descriptive of the actual state.) |
I like stale better too, dirty feels too negative. There are 74 draw methods, doing it this way wad more expedient to make On Mon, Mar 16, 2015, 03:33 Eric Firing notifications@github.com wrote:
|
@efiring Any thoughts on how this is coming along? I regretting not sorting out how to test this ahead of time. |
9f8eeab
to
03669ce
Compare
Hide the python2 only code in a conditional
With the repl callback the pyplot function do not need to call `draw_if_interactive`.
There is still one `draw_if_interactive` left in the `rcdefaults` call as the current scheme does not track when rcparams change.
Re-based this again. @efiring @WeatherGod @jenshnielsen I think I have addressed the documentation issues. I would like to get this merged as soon as possible to avoid excessive re-bases and to get all the brave souls who run master testing this 😈 . |
ENH : add function to add displayhook
Looks good; any additional wrinkles can be ironed out in subsequent PRs. This is a huge step forward. |
@tacaswell , @efiring A few comments:
I think this is a misunderstanding. Calling canvas.draw_idle doesn't actually draw anything; it just tells the canvas to redraw itself on the next pass through the event loop. This is standard practice in GUI design and does not break the separation between the frontend and the backend.
Something like that, yes, but it can probably be simplified, and the call to
Plain python also has a post-execute hook, which is PyOS_InputHook; this is what the GUI backends use to be interactive. The call to |
On 2015/07/11 7:33 PM, mdehoon wrote:
Right, but PyOS_InputHook is at the C level; the only hook at the Python The Artist picker already accesses figure.canvas, so I thought maybe the |
@tacaswell, in the process of unsuccessfully working on this, I put in a counter to see how many times the |
@tacaswell, Now I see your comment from 4 hours ago--I missed it originally. And I forgot that there was still one pending PR in this series, but had only the vague recollection that everything had been worked out. Good! |
Sure, but (using tkagg as an example), the point is that Tkinter calls PyOS_InputHook for you, and by using
which is the behavior we are trying to implement here.
I don't see why you would need a separate |
I think having Doesn't it also help prevent unnecessary redraws of elements in the artist On Sun, Jul 12, 2015 at 4:23 AM, mdehoon notifications@github.com wrote:
|
Apologies for the late response on this, I didn't pay it any attention as I have never got interactive mode to work (still haven't, but that's another story). My two cents on this, I have no opinion on I mean with this solution: ln.set_color('r')
ln.set_linewidth(5) only works in interactive mode... try: import matplotlib
matplotlib.use('GTK3Agg')
from matplotlib import pyplot as plt
from gi.repository import Gtk, Gdk, GObject, GLib
fig, ax = plt.subplots()
ln, = plt.plot(range(5))
def change_props(self):
print 'changing props'
ln.set_lw(5)
ln.set_color('purple')
if True:
tbar = fig.canvas.manager.toolbar
tbutton = Gtk.ToolButton()
tbutton.set_label('change props')
tbar.insert(tbutton, 0)
tbutton.connect('clicked', change_props)
tbar.show_all()
plt.show() This works for me neither on 1.4.2 and on master. Note I didn't use MEP22 (new toolbar) here because I wanted to test on 1.4.x. With MEP22, this would become a more common occurrence with the ease of creating new tools. This becomes even nicer as and when MEP26 (Artist Style Sheets) ever get off the ground, and perhaps we should include this as part of the MEP26 refactor as if I understand correctly that deals with precisely this problem (see https://github.com/matplotlib/matplotlib/blob/master/doc/devel/MEP/MEP26.rst). |
@OceanWolf Try #4506. With that, there will be no use of displayhook. |
@efiring just tried and it doesn't work, of course if I add And about interactive mode, I think I now understand, I have to either explicitly set it as an rcParam or run |
This is still a prototype, but it seems to work.
Artist
properties change (seeArtist.{add,remove}_callback
andArtist.pchanged
) which we can tap into to set axes/figure trees as 'dirty'. This will useful if we ever want to start caching rasters of each artist (does that even make sense? I think it does and is an obvious place to get major gains if we do not re-render the text each time)calling
will automatically re-draw the figure as expected.
If we are happy with this, running the installer should probably be part of the pyplot import.
attn @efiring this is the alternative to the approach in #4082 that I was talking about in MD.