mouse events and plotting many points dynamically

Hi, I am following this mailing list for a while now and

    > considering to convert to matplotlib at some point. Whether
    > this is feasable depends on the following two points:

    > 1) I would like to know if there is an equivalent to the
    > scipy.xplt.mouse command which (in its simplest form) waits
    > for a mouse click and returns its coordinates:

    > Example: #------------------------------------- from
    > scipy.xplt import * x=arange(0.0,1.0,0.1) plg(x*x,x)
    > m=mouse(1,-1,"click with the mouse") print "x,y=",m[0],m[1]
    > #-------------------------------------

    > John's reply
    > http://mail.python.org/pipermail/python-list/2004-April/216550.html
    > shows that it is possible to connect mouse events with
    > functions. However, that code depends on the back-end and
    > is not as short as the above one (though more flexible, of
    > course ;-).

For some time, we've provided basic cross GUI event handling with the
canvas.connect method. Todd Miller had the idea to port the gtk
connect interface to Tk. For example, the demo coords_demo.py runs
unchanged on TkAgg and GTKAgg. This is only a partial implementation,
and in CVS I've extended it a bit more. The new method is called
mpl_connect, and you will be able to do, across GUIs, calls like

def on_move(event):
    # get the x and y coords
    x, y = event.x, event.y
    ...snip...

canvas.mpl_connect('motion_notify_event', on_move)

I plan to provide the motion notify, on click, and key press events,
and provide some extra positional information in the events, namely
canvas coordinate location and axes coordinate location. The backend
will handle things like flipy so the script can ignore it. I'm in the
process of designing a better toolbar and am using this cross-GUI
event handling to minimize the burden of implementing the toolbar in
the various backends.

In order to do something like the

  m = mouse(1,-1,"click with the mouse")
  print "x,y=",m[0],m[1]

example, it would be necessary to implement some cross gui blocking
capability, so that the execution of the script is halted until the
block is removed. I don't see this as a major problem, but will
require some input from people with experience on the not-GTK GUIs.
It's probably not be necessary, though, since it's only a few more
keystrokes to do

def on_click(event): print 'x, y =', event.x, event.y
canvas.mpl_connect(''button_press_event', on_click)

This should be ready by the 0.61 release. BTW, as a wx expert,
perhaps you have a code snippet I can use which calls the event
Connect and Disconnect methods directly. I'm currently trying to
figure this out for the wx implementation of mpl_connect and
mpl_disconnect.

    > 2) For one set of applications I would like to be able to
    > plot several times 1000 points (or more). Optimally would
    > be to plot one point after another to get a dynamical
    > impression. In the application I have in mind there would
    > be no need to store these points (ie zooming is not
    > necessary) which normally degrades performance. Presently
    > for me the solution for this type of things is our
    > PlottingCanvas for wxPython, see
    > http://www.physik.tu-dresden.de/~baecker/python/plot.html
    > and there the StandardMap.py example.

There is a lot of interest in making dynamical plotting more
efficient. Currently, the entire figure is redrawn with each frame
update, which is clearly not ideal for dynamic figures in which only a
portion needs to be redrawn. I'm interested in making some changes to
better support "real time" data acquisition, where the quotes mean
"fast enough to handle most use cases".

My current thought is to support drawing to subsections of the agg
canvas, eg, so that a single axes, line or patch could be updated
without updating the entire figure canvas. To support this, each
object would need to know it's rectangular extent (done), take a
snapshot of the background canvas before drawing (to be done) and know
how to render itself to canvas (done). In addition, agg and the
backends would need to be extended to allow drawing of subregions of
the canvas, which should be fairly easy. By calling the right
combination of store_background, draw and erase, you could get much
faster dynamic plots.

The current implementation (redrawing the entire figure) is pretty
fast on a fast machine, but needs to be faster.

How do you do this in PlottingCanvas? Do simply add objects to the
canvas and redraw the entire canvas if you need to remove an object,
or do you support selective erasing and removal of objects?

JDH

[...]

    > 1) I would like to know if there is an equivalent to the
    > scipy.xplt.mouse command which (in its simplest form) waits
    > for a mouse click and returns its coordinates:

    > Example: #------------------------------------- from
    > scipy.xplt import * x=arange(0.0,1.0,0.1) plg(x*x,x)
    > m=mouse(1,-1,"click with the mouse") print "x,y=",m[0],m[1]
    > #-------------------------------------

[...]

For some time, we've provided basic cross GUI event handling with the
canvas.connect method. Todd Miller had the idea to port the gtk
connect interface to Tk. For example, the demo coords_demo.py runs
unchanged on TkAgg and GTKAgg. This is only a partial implementation,
and in CVS I've extended it a bit more. The new method is called
mpl_connect, and you will be able to do, across GUIs, calls like

def on_move(event):
    # get the x and y coords
    x, y = event.x, event.y
    ...snip...

canvas.mpl_connect('motion_notify_event', on_move)

I plan to provide the motion notify, on click, and key press events,
and provide some extra positional information in the events, namely
canvas coordinate location and axes coordinate location. The backend
will handle things like flipy so the script can ignore it. I'm in the
process of designing a better toolbar and am using this cross-GUI
event handling to minimize the burden of implementing the toolbar in
the various backends.

In order to do something like the

  m = mouse(1,-1,"click with the mouse")
  print "x,y=",m[0],m[1]

example, it would be necessary to implement some cross gui blocking
capability, so that the execution of the script is halted until the
block is removed. I don't see this as a major problem, but will
require some input from people with experience on the not-GTK GUIs.
It's probably not be necessary, though, since it's only a few more
keystrokes to do

def on_click(event): print 'x, y =', event.x, event.y
canvas.mpl_connect(''button_press_event', on_click)

This should be ready by the 0.61 release.

Excellent - that sounds really good!
Still I think that maybe the variant with blocking is
useful as well: I am having our students in mind
which were quite happy and sucessfull with scipy.xplt
(in particular also those who had no previous programming
experience.) Introducing them to event driven ``design''
already in the second excercise might be burdoning them
too much (but maybe I am wrong).

BTW, as a wx expert,
perhaps you have a code snippet I can use which calls the event
Connect and Disconnect methods directly. I'm currently trying to
figure this out for the wx implementation of mpl_connect and
mpl_disconnect.

I am definitively no wx expert - a lot of the stuff I learned
from looking at Chris Barker's and Gordon Williams' code
and the wxPython demo. So I also don't have an example here.
(It only rings a bell that Chris maybe had some discussion
on this on the wxPython mailing list - I think it
was about creating custom events - maybe
there is an example in Chris' FloatCanvas?
Ok, I just had a _quick_ look and at the beginning
of FloatCanvas.py
( http://home.comcast.net/~chrishbarker/FloatCanvas/ )
there are a couple of window.Connect.
Maybe this helps...
Anyway, if you did not succeed by Friday let me know
and I will try to have a look over the week-end.

    > 2) For one set of applications I would like to be able to
    > plot several times 1000 points (or more). Optimally would
    > be to plot one point after another to get a dynamical
    > impression. In the application I have in mind there would
    > be no need to store these points (ie zooming is not
    > necessary) which normally degrades performance. Presently
    > for me the solution for this type of things is our
    > PlottingCanvas for wxPython, see
    > http://www.physik.tu-dresden.de/~baecker/python/plot.html
    > and there the StandardMap.py example.

There is a lot of interest in making dynamical plotting more
efficient. Currently, the entire figure is redrawn with each frame
update, which is clearly not ideal for dynamic figures in which only a
portion needs to be redrawn. I'm interested in making some changes to
better support "real time" data acquisition, where the quotes mean
"fast enough to handle most use cases".

Personally I am a bit sceptic, if it is possible to
cater for all needs (super-fast vs. zooming/redraw of stored data, ...),
sureley not at the same time but maybe a
reasonable comprise is possible ;-).

My current thought is to support drawing to subsections of the agg
canvas, eg, so that a single axes, line or patch could be updated
without updating the entire figure canvas. To support this, each
object would need to know it's rectangular extent (done), take a
snapshot of the background canvas before drawing (to be done) and know
how to render itself to canvas (done). In addition, agg and the
backends would need to be extended to allow drawing of subregions of
the canvas, which should be fairly easy. By calling the right
combination of store_background, draw and erase, you could get much
faster dynamic plots.

Thinking of the example I have in mind, where not untypically
up to 100 x 10000 points are plotted (or even more)
storing each point as an object + further information
might (presumably will) slow things down and cause memory problems
(that's what occurred for me with Tkinter).
Anyway, in September we have a student who will
set up a couple of examples and then we will see
if things are fast enough (for us ;-)...

The current implementation (redrawing the entire figure) is pretty
fast on a fast machine, but needs to be faster.

How do you do this in PlottingCanvas? Do simply add objects to the
canvas

Yes (with the usual double-buffering)
so for example for a circle we just call
  self.ScreenDC.DrawCircle(x, y, pointsize)
  self.BufferDC.DrawCircle(x, y, pointsize)
and the border color and fill color of the circle are
initialized before and only once for a sequence of successive points.

Also (in the case of wx) it helps a lot if the
drawing context (DC) is only initialized once
for a sequence of successive points.
Nikolai (Hlubek) measured the speed increase to be

  factor 2 for wx.wxClientDC(self)
  factor 10 for wx.wxBufferedDC(wx.wxClientDC(self), self._Buffer)

and redraw the entire canvas if you need to remove an object,

Well, in the fastest mode of operation we don't
allow for removal of objects/resizing the canvas and things like that...

or do you support selective erasing and removal of objects?

No. What the PlottingCanvas does provide though
is the possibility to ``move'' an object over
a ``background'' plot - see AnnularBilliard.py
where a ball is moving inside a two-dimensional billiard
(click in the right window to specify the initial condition
and move the slider upwards to increas the speed of
the ball a bit).

In another mode of operation we enable storing
of all data points (with the corresponding speed penalty).
This then allows to zoom in and redraw those data.

Despite some optimizations, plotting with wx
is still not as fast as for example with pgplot
(wherever the bottlenecks are ;-).

Best,

Arnd

···

On Mon, 19 Jul 2004, John Hunter wrote: