I just committed code which adds a new kwarg to the
> slider to allow for live updates while dragging. It is
> off by default since the gtk backend seems to queue
> events. Wx and tk work in a natural way (they don't
> generate mouse events while busy). Who is the resident
> gtk expert, and do you think you know how to fix this?
> It would be nice to have the live slider be on by
> default.
The backend_bases.FigureCanvas defines a base method draw_idle which
calls draw by default
def draw_idle(self, *args, **kwargs):
"""
draw only if idle; defaults to draw but backends can overrride
"""
self.draw(*args, **kwargs)
backend_gtk subclasses this method to do proper idle drawing. So call
idle_draw from Slider.
Note that there are two updates to consider, the update of the slider
and the update of the figure it is manipulating. There is no reason
not to continuously update the slider once you have this idle business
sorted out. But you probably do want to continuously update the
figure it is modifying in every case, because the figure could be very
complicated to draw. Instead, you might want to expose this live
update property as a checkbox in the slider, since if the figure is
expensive (see examples/widgets/check_buttons.py and note you will
have to update from CVS because I just committed a patch to make check
buttons work for single buttons)
I've been working on how to draw individual artists w/o updating the
entire figure. This is mostly working with a few bugs in gtkagg.
Basically you have to manage the background region as a memory chunk
and then reblit the background before redrawing the artist. This was
designed to make animations faster and to support better GUI widgets.
The first example below shows how to draw just a rectangle and move it
around the screen w/o updating the entire figure. The second example
is more complex, and makes a rectangle widget which you can move
around the canvas or resize using a standard click on edge drag
operation. In this one, you create new rectangles by pressing "n"
(only works on gtkagg). Only two methods are required to support this
on other backends
region = canvas.copy_from_bbox(bbox)
canvas.restore_region(region)
canvas.blit(bbox)
This is all highly experimental and subject to change, but it maybe
something you want to incorporate into the slider for better realtime
updating. Note that you can easily add copy_from_bbox and
restore_region to cocoaagg by simply forwarding the calls onto the agg
renderer, as gtkagg does. For the blit method, you have to figure out
how to make cocoa blit a region -- shouldn't be too hard.
JDH
######## Example 1 #############
from pylab import *
ion()
ax = subplot(111)
plot(rand(100), rand(100), 'o')
r = Rectangle((.5, .5), width=.1, height=.1)
ax.add_patch(r)
r.region = ax.figure.canvas.copy_from_bbox(r.get_window_extent())
draw()
def move(event):
if not move.idle or event.inaxes != ax: return
move.idle = 0
ax.figure.canvas.restore_region(r.region)
r.xy = event.xdata, event.ydata
r.region = ax.figure.canvas.copy_from_bbox(r.get_window_extent())
ax.draw_artist(r)
ax.figure.canvas.blit(ax.bbox)
move.idle = 1
move.idle = 1
connect('motion_notify_event', move)
show()
######## Example 2 #############
from pylab import *
from matplotlib.transforms import lbwh_to_bbox
from matplotlib.mlab import dist_point_to_segment
class RectangleWidget(Rectangle):
def __init__(self, figure, *args, **kwargs):
Rectangle.__init__(self, *args, **kwargs)
self.figure = figure
self.idle = True
self.oldbox = self.get_window_extent()
self.newbox = self.get_window_extent()
self.region = self.figure.canvas.copy_from_bbox(self.oldbox)
self.figure.canvas.mpl_connect('motion_notify_event', self.move)
self.figure.canvas.mpl_connect('button_press_event', self.press)
self.figure.canvas.mpl_connect('button_release_event', self.release)
self.figure.draw_artist(self)
self._resize = False
self._drag = False
def redraw(self):
canvas = self.figure.canvas
canvas.restore_region(self.region)
self.region = canvas.copy_from_bbox(self.newbox)
self.figure.draw_artist(self)
canvas.blit(self.oldbox)
canvas.blit(self.newbox)
def press(self, event):
if event.button!=1: return
x, y = self.xy
w, h = self.width, self.height
# the four line segments of the rectangle
lb = x, y
lt = x, y+h
rb = x+w, y
rt = x+w, y+h
segments = {
'left' : (lb, lt),
'right' : (rb, rt),
'top' : (lt, rt),
'bottom' : (lb, rb),
}
p = event.x, event.y
near =
for name, segment in segments.items():
s0, s1 = segment
d = dist_point_to_segment(p, s0, s1)
if d<5: near.append(name)
info = event.x, event.y, self.xy[0], self.xy[1], self.width, self.height, near
if len(near):
self._resize = info
self.set_edgecolor('red')
self.set_linewidth(2)
else:
self._resize = False
if self.get_window_extent().contains(event.x, event.y):
self._drag = info
self.redraw()
def release(self, event):
if event.button!=1: return
self._resize = False
self._drag = False
self.idle = True
self.set_edgecolor('black')
self.set_linewidth(0.5)
self.redraw()
def move(self, event):
if not (self.idle and (self._resize or self._drag)): return
self.idle = False
canvas = self.figure.canvas
if self._resize:
exo, eyo, xo, yo, wo, ho, near = self._resize
else:
exo, eyo, xo, yo, wo, ho, near = self._drag
scalex=0; scaley=0; scalew=0; scaleh=0
if 'left' in near:
scalex = 1
scalew = -1
if 'right' in near:
scalew = 1
if 'bottom' in near:
scaley = 1
scaleh = -1
if 'top' in near:
scaleh = 1
if self._drag:
scalex = 1
scaley = 1
dx = event.x - exo
dy = event.y - eyo
if event.button ==1:
self.oldbox = self.get_padded_box()
newx = xo + scalex*dx
if scalew==0 or newx<xo+wo:
self.xy[0] = newx
newy = yo + scaley*dy
if scaleh==0 or newy<yo+ho:
self.xy[1] = newy
neww = wo + scalew*dx
if neww>0: self.width = neww
newh = ho + scaleh*dy
if newh>0: self.height = newh
#self.xy = event.x, event.y
self.newbox = self.get_padded_box()
#print event.x, event.y, self.get_padded_box().get_bounds()
self.redraw()
self.idle = True
def get_padded_box(self, pad=5):
xmin = self.xy[0]-pad
ymin = self.xy[1]-pad
return lbwh_to_bbox(xmin, ymin, self.width+2*pad, self.height+2*pad)
ion()
fig = figure()
#ax = subplot(111)
def keypress(event):
if event.key=='n':
widget = RectangleWidget( fig, (100,100), 20, 30)
fig.canvas.blit()
fig.canvas.mpl_connect('key_press_event', keypress)
show()