Compare commits

...
This repository has been archived on 2024-07-04. You can view files and clone it, but cannot push or open issues or pull requests.

11 Commits

Author SHA1 Message Date
ded2faaf2c Removed the title of the plot from the canvas, as it is now shown above
the canvas as well. Updated the matplotlib axis limits to reflect this and
allow more space for the plot.
2006-10-12 14:56:53 +00:00
92a8a819e8 Plots restore current selection on set_frozen(False) 2006-10-12 12:58:48 +00:00
3a62580125 Plots now show names outside the canvas. 2006-10-12 12:46:34 +00:00
31527e51fd Added freeze icon. 2006-10-12 12:20:19 +00:00
28cab35cd5 The toolbar functionality is almost back to normal. 2006-10-12 11:44:49 +00:00
f435b37046 Rectangle modes work, pan does not. 2006-10-11 12:05:13 +00:00
a6571d8c6b Cleaned up toolbar code, lots of work still to be done.
Does not work.
2006-10-10 21:15:13 +00:00
0ed180d6ab move icon
AM   move.png
2006-10-10 21:09:12 +00:00
5e2d1a4ae6 toolbar icons 2006-10-10 15:36:19 +00:00
502cd22dc8 2006-10-10 15:23:35 +00:00
958c6dbfdf testing new toolbar 2006-10-10 15:19:12 +00:00
13 changed files with 446 additions and 358 deletions

BIN
icons/cursor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

BIN
icons/filesave.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

BIN
icons/freeze.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

BIN
icons/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
icons/move.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

BIN
icons/select.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 659 B

BIN
icons/zoom_to_rect.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -139,7 +139,7 @@ axes.grid : True # display grid or not
axes.titlesize : 12 # fontsize of the axes title axes.titlesize : 12 # fontsize of the axes title
axes.labelsize : 10 # fontsize of the x any y labels axes.labelsize : 10 # fontsize of the x any y labels
axes.labelcolor : black axes.labelcolor : black
axes.axisbelow : True # whether axis gridlines and ticks are below axes.axisbelow : True # whether axis gridlines and ticks are below
# the axes elements (lines, text, etc) # the axes elements (lines, text, etc)
@ -191,11 +191,11 @@ figure.edgecolor : white # figure edgecolor
# The figure subplot parameters. All dimensions are fraction of the # The figure subplot parameters. All dimensions are fraction of the
# figure width or height # figure width or height
figure.subplot.left : 0.1 # the left side of the subplots of the figure figure.subplot.left : 0.04 # the left side of the subplots of the figure
figure.subplot.right : 0.975 # the right side of the subplots of the figure figure.subplot.right : 0.999 # the right side of the subplots of the figure
figure.subplot.bottom : 0.1 # the bottom of the subplots of the figure figure.subplot.bottom : 0.04 # the bottom of the subplots of the figure
figure.subplot.top : 0.90 # the top of the subplots of the figure figure.subplot.top : 0.999 # the top of the subplots of the figure
figure.subplot.wspace : 0.1 # the amount of width reserved for blank space between subplots figure.subplot.wspace : 0.1 # the amount of width reserved for blank space between subplots
figure.subplot.hspace : 0.1 # the amount of height reserved for white space between subplots figure.subplot.hspace : 0.1 # the amount of height reserved for white space between subplots

View File

@ -263,6 +263,7 @@ class GraphDataset(Dataset):
Dataset.__init__(self,array=array,identifiers=identifiers,name='A') Dataset.__init__(self,array=array,identifiers=identifiers,name='A')
self._graph = None self._graph = None
self._type = 'g' self._type = 'g'
self._pos = None
def asnetworkx(self,nx_type='graph'): def asnetworkx(self,nx_type='graph'):
dim = self.get_dim_name()[0] dim = self.get_dim_name()[0]

View File

@ -20,7 +20,7 @@ import networkx
import scipy import scipy
# global active mode. Used by toolbars to communicate correct mode # global active mode. Used by toolbars to communicate correct mode
active_mode = 'DEFAULT' active_mode = 'default'
class ObjectTable: class ObjectTable:
"""A 2D table of elements.""" """A 2D table of elements."""
@ -245,35 +245,78 @@ class MainView (gtk.Notebook):
self.emit('view-changed', vf) self.emit('view-changed', vf)
class Plot (gtk.Frame): class View (gtk.Frame):
"""The base class of everything that is shown in the center view of fluents.
Most views should rather subclass Plot, which automatically handles freezing and
toolbars, and sets up matplotlib Figure and Canvas objects.
"""
def __init__(self, title): def __init__(self, title):
gtk.Frame.__init__(self) gtk.Frame.__init__(self)
self.title = title self.title = title
self.sel_obj = None self.set_shadow_type(gtk.SHADOW_NONE)
self.set_label(title)
def get_toolbar(self):
return None
class EmptyView (View):
"""EmptyView is shown in ViewFrames that are unused."""
def __init__(self):
View.__init__(self, 'Empty view')
self.set_label(None)
label = gtk.Label('No view')
ebox = gtk.EventBox()
ebox.add(label)
self.add(ebox)
label.show()
ebox.show()
self.show()
class Plot (View):
def __init__(self, title):
View.__init__(self, title)
self.selection_listener = None self.selection_listener = None
self.fig = Figure() self.fig = Figure()
self.canvas = FigureCanvas(self.fig) self.canvas = FigureCanvas(self.fig)
self.set_shadow_type(gtk.SHADOW_NONE)
self._background = None self._background = None
self._sel_sensitive = True self._frozen = False
self._toolbar = PlotToolbar(self)
self.canvas.add_events(gtk.gdk.ENTER_NOTIFY_MASK) self.canvas.add_events(gtk.gdk.ENTER_NOTIFY_MASK)
self.current_dim = None
def set_selection_sensitive(self,event): def set_frozen(self, frozen):
if event: """A frozen plot will not be updated when the current selection is changed."""
if event.get_active(): self._frozen = frozen
logger.log('debug','Selection freezed') if not frozen:
self._sel_sensitive = False self.set_current_selection(self._current_selection)
else:
logger.log('debug','Selections active')
self._sel_sensitive = True
def get_title(self): def get_title(self):
return self.title return self.title
def selection_changed(self, selection): def get_toolbar(self):
if not self._sel_sensitive or not self.get_property('visible'): return self._toolbar
def selection_changed(self, dim_name, selection):
""" Selection observer handle.
A selection change in a plot is only drawn if:
1.) plot is sensitive to selections (not freezed)
2.) plot is visible (has a view)
3.) the selections dim_name is the plot's dimension.
"""
if self._frozen \
or not self.get_property('visible') \
or self.current_dim != dim_name:
return return
else:
self._current_selection = selection
self.set_current_selection(selection) self.set_current_selection(selection)
def set_selection_listener(self, listener): def set_selection_listener(self, listener):
@ -285,21 +328,7 @@ class Plot (gtk.Frame):
""" """
self.selection_listener = listener self.selection_listener = listener
def get_toolbar(self):
return None
class EmptyView (Plot):
def __init__(self):
Plot.__init__(self, 'Empty view')
label = gtk.Label('No view')
ebox = gtk.EventBox()
ebox.add(label)
self.add(ebox)
label.show()
ebox.show()
self.show()
class LineViewPlot(Plot): class LineViewPlot(Plot):
"""Line view of current selection, no interaction """Line view of current selection, no interaction
Only works on 2d-arrays Only works on 2d-arrays
@ -315,7 +344,7 @@ class LineViewPlot(Plot):
self.dataset = dataset self.dataset = dataset
Plot.__init__(self, name) Plot.__init__(self, name)
self.ax = self.fig.add_subplot(111) self.ax = self.fig.add_subplot(111)
self.ax.set_title(self.get_title()) #self.ax.set_title(self.get_title())
self.current_dim = self.dataset.get_dim_name(major_axis) self.current_dim = self.dataset.get_dim_name(major_axis)
if len(self._data.shape)==2 and not minor_axis: if len(self._data.shape)==2 and not minor_axis:
minor_axis = major_axis-1 minor_axis = major_axis-1
@ -359,14 +388,9 @@ class LineViewPlot(Plot):
self.canvas.show() self.canvas.show()
#FIXME: Lineview plot cannot do selections -> disable in toolbar #FIXME: Lineview plot cannot do selections -> disable in toolbar
self._toolbar = PlotToolbar(self.canvas,self) self._toolbar = PlotToolbar(self)
self._toolbar.chk.connect ('toggled' , self.set_selection_sensitive)
self.canvas.mpl_connect('resize_event', self.clear_background) self.canvas.mpl_connect('resize_event', self.clear_background)
def get_toolbar(self):
return self._toolbar
def clear_background(self, event): def clear_background(self, event):
self._background = None self._background = None
@ -386,27 +410,23 @@ class LineViewPlot(Plot):
line_coll = LineCollection(segs, colors=(1,0,0,1)) line_coll = LineCollection(segs, colors=(1,0,0,1))
line_coll.set_clip_box(self.ax.bbox) line_coll.set_clip_box(self.ax.bbox)
self.ax.update_datalim(line_coll.get_verts(self.ax.transData)) self.ax.update_datalim(line_coll.get_verts(self.ax.transData))
self._toolbar.forward()
if self.use_blit: if self.use_blit:
self.ax.draw_artist(line_coll) self.ax.draw_artist(line_coll)
#print "\nLine collection clip box:"
line_coll.get_clip_box().get_bounds() line_coll.get_clip_box().get_bounds()
#print "\nLine collection bbox:"
#print self.ax.bbox.get_bounds()
#print "Background bbox:"
#print self._bbox.get_bounds()
#self.canvas.blit(self._bbox)
self.canvas.blit() self.canvas.blit()
#self.ax.draw_artist(line_coll)
else: else:
self.ax.add_collection(line_coll) self.ax.add_collection(line_coll)
self.canvas.draw() self.canvas.draw()
class ScatterMarkerPlot(Plot): class ScatterMarkerPlot(Plot):
"""The ScatterMarkerPlot is faster than regular scatterplot, but """The ScatterMarkerPlot is faster than regular scatterplot, but
has no color and size options.""" has no color and size options."""
def __init__(self, dataset_1, dataset_2, id_dim, sel_dim, id_1, id_2,s=6, name="Scatter plot"):
def __init__(self, dataset_1, dataset_2, id_dim, sel_dim,
id_1, id_2, s=6, name="Scatter plot"):
Plot.__init__(self, name) Plot.__init__(self, name)
self.use_blit = False self.use_blit = False
self._background = None self._background = None
@ -422,19 +442,10 @@ has no color and size options."""
self.xaxis_data = dataset_1._array[:, x_index] self.xaxis_data = dataset_1._array[:, x_index]
self.yaxis_data = dataset_2._array[:, y_index] self.yaxis_data = dataset_2._array[:, y_index]
self.ax.plot(self.xaxis_data, self.yaxis_data, 'o', markeredgewidth=0, markersize=s) self.ax.plot(self.xaxis_data, self.yaxis_data, 'o', markeredgewidth=0, markersize=s)
self.ax.set_title(self.get_title()) #self.ax.set_title(self.get_title())
self.add(self.canvas) self.add(self.canvas)
self.canvas.show() self.canvas.show()
# add toolbar
self._toolbar = PlotToolbar(self.canvas, self)
self._toolbar.chk.connect ('toggled', self.set_selection_sensitive)
self._toolbar.set_property('show-arrow', False)
self._toolbar.set_select_callback(self.rectangle_select_callback)
def get_toolbar(self):
return self._toolbar
def rectangle_select_callback(self, x1, y1, x2, y2): def rectangle_select_callback(self, x1, y1, x2, y2):
ydata = self.yaxis_data ydata = self.yaxis_data
xdata = self.xaxis_data xdata = self.xaxis_data
@ -467,7 +478,7 @@ has no color and size options."""
self._selection_line, = self.ax.plot(xdata_new, ydata_new,marker='o', markersize=self.ms, linestyle=None, markerfacecolor='r') self._selection_line, = self.ax.plot(xdata_new, ydata_new,marker='o', markersize=self.ms, linestyle=None, markerfacecolor='r')
self._toolbar.forward() #update data lims before draw # self._toolbar.forward() #update data lims before draw
if self.use_blit: if self.use_blit:
self.ax.draw_artist(self._selection_line) self.ax.draw_artist(self._selection_line)
self.canvas.blit() self.canvas.blit()
@ -496,7 +507,7 @@ class ScatterPlot(Plot):
self.fig.colorbar(sc,ticks=[], fraction=.05) self.fig.colorbar(sc,ticks=[], fraction=.05)
self.ax.axhline(0, color='k', lw=1., zorder=1) self.ax.axhline(0, color='k', lw=1., zorder=1)
self.ax.axvline(0, color='k', lw=1., zorder=1) self.ax.axvline(0, color='k', lw=1., zorder=1)
self.ax.set_title(self.get_title()) #self.ax.set_title(self.get_title())
# collection # collection
self.coll = self.ax.collections[0] self.coll = self.ax.collections[0]
@ -504,15 +515,6 @@ class ScatterPlot(Plot):
self.add(self.canvas) self.add(self.canvas)
self.canvas.show() self.canvas.show()
# create toolbar
self._toolbar = PlotToolbar(self.canvas, self)
self._toolbar.chk.connect ('toggled' , self.set_selection_sensitive)
self._toolbar.set_property('show-arrow', False)
self._toolbar.set_select_callback(self.rectangle_select_callback)
def get_toolbar(self):
return self._toolbar
def rectangle_select_callback(self, x1, y1, x2, y2): def rectangle_select_callback(self, x1, y1, x2, y2):
ydata = self.yaxis_data ydata = self.yaxis_data
xdata = self.xaxis_data xdata = self.xaxis_data
@ -532,7 +534,9 @@ class ScatterPlot(Plot):
def set_current_selection(self, selection): def set_current_selection(self, selection):
ids = selection[self.current_dim] # current identifiers ids = selection[self.current_dim] # current identifiers
if len(ids)==0: if len(ids)==0:
print "nothing selected"
return return
#self._toolbar.forward() #update data lims before draw
index = self.dataset_1.get_indices(self.current_dim, ids) index = self.dataset_1.get_indices(self.current_dim, ids)
if self.use_blit: if self.use_blit:
if self._background is None: if self._background is None:
@ -542,8 +546,7 @@ class ScatterPlot(Plot):
if len(index)>0: if len(index)>0:
lw.put(2.,index) lw.put(2.,index)
self.coll.set_linewidth(lw) self.coll.set_linewidth(lw)
self._toolbar.forward() #update data lims before draw
if self.use_blit: if self.use_blit:
self.canvas.blit() self.canvas.blit()
self.ax.draw_artist(self.coll) self.ax.draw_artist(self.coll)
@ -558,6 +561,7 @@ class NetworkPlot(Plot):
self.dataset = dataset self.dataset = dataset
self.keywords = kw self.keywords = kw
self.dim_name = self.dataset.get_dim_name(0) self.dim_name = self.dataset.get_dim_name(0)
self.current_dim = self.dim_name
if not kw.has_key('name'): if not kw.has_key('name'):
kw['name'] = self.dataset.get_name() kw['name'] = self.dataset.get_name()
if not kw.has_key('prog'): if not kw.has_key('prog'):
@ -604,17 +608,10 @@ class NetworkPlot(Plot):
# Initial draw # Initial draw
networkx.draw_networkx(self.graph, ax=self.ax, **kw) networkx.draw_networkx(self.graph, ax=self.ax, **kw)
# Setup toolbar
self._toolbar = PlotToolbar(self.canvas, self)
self._toolbar.chk.connect ('toggled' , self.set_selection_sensitive)
self._toolbar.set_property('show-arrow', False)
self._toolbar.set_select_callback(self.rectangle_select_callback)
def get_toolbar(self): def get_toolbar(self):
return self._toolbar return self._toolbar
def rectangle_select_callback(self, x1, y1, x2, y2): def rectangle_select_callback(self, x1, y1, x2, y2):
'event1 and event2 are the press and release events'
pos = self.keywords['pos'] pos = self.keywords['pos']
ydata = scipy.zeros((len(pos),), 'l') ydata = scipy.zeros((len(pos),), 'l')
xdata = scipy.zeros((len(pos),), 'l') xdata = scipy.zeros((len(pos),), 'l')
@ -635,10 +632,10 @@ class NetworkPlot(Plot):
ids = [node_ids[i] for i in index] ids = [node_ids[i] for i in index]
self.selection_listener(self.dataset.get_dim_name(0), ids) self.selection_listener(self.current_dim, ids)
def set_current_selection(self, selection): def set_current_selection(self, selection):
ids = selection[self.dataset.get_dim_name(0)] # current identifiers ids = selection[self.current_dim] # current identifiers
node_set = set(self.graph.nodes()) node_set = set(self.graph.nodes())
selected_nodes = list(ids.intersection(node_set)) selected_nodes = list(ids.intersection(node_set))
@ -668,293 +665,383 @@ class NetworkPlot(Plot):
self.canvas.draw() self.canvas.draw()
class PlotToolbar(NavigationToolbar2,gtk.Toolbar): class PlotMode:
# list of toolitems to add to the toolbar, format is: """A PlotMode object corresponds to a mouse mode in a plot.
# text, tooltip_text, image_file, callback(str)
toolitems = (
('Home', 'Reset original view', 'home.png', 'home'),
#('Back', 'Back to previous view','back.png', 'back'),
#('Forward', 'Forward to next view','forward.png', 'forward'),
#('Subplots', 'Configure subplots','subplots.png', 'configure_subplots'),
('Save', 'Save the figure','filesave.png', 'save_figure'),
)
radiobuttons = ( When a mode is selected in the toolbar, the PlotMode corresponding
('DEFAULT', 'Default mode', 'cursor.png', 'default'), to the toolbar button is activated by calling setup(ax) for the axis
('PAN', 'Pan axes with left mouse, zoom with right', 'move.png', 'pan'), system ax.
('ZOOM', 'Zoom to rectangle','zoom_to_rect.png', 'zoom'), """
('SELECT', 'Select within rectangle', 'select.png', 'select'), def __init__(self, plot, name, tooltip, image_file):
) self.name = name
self.tooltip = tooltip
self.image_file = image_file
self.plot = plot
self.canvas = plot.canvas
def __init__(self, canvas, plot): def get_icon(self):
self.win = None """Returns the icon for the PlotMode"""
fname = os.path.join(fluents.ICONDIR, self.image_file)
image = gtk.Image()
image.set_from_file(fname)
return image
def activate(self):
"""Subclasses of PlotMode should do their initialization here.
The activate method is called when a mode is activated, and is
used primarily to set up callback functions on events in the
canvas.
"""
pass
def deactivate(self):
"""Subclasses of PlotMode should do their cleanup here.
The deactivate method is primarily by subclasses of PlotMode to
remove any callbacks they might have on the matplotlib canvas.
"""
pass
def _mpl_disconnect_all(self):
"""Disconnects all matplotlib callbacks defined on the canvas.
This is a hack because the RectangleSelector in matplotlib does
not store its callbacks, so we need a workaround to remove them.
"""
callbacks = self.plot.canvas.callbacks
for callbackd in callbacks.values():
for c in callbackd.keys():
del callbackd[c]
class DefaultPlotMode (PlotMode):
def __init__(self, plot):
PlotMode.__init__(self, plot, 'default', 'Default mode', 'cursor.png')
def activate(self):
for k, v in self.canvas.callbacks.items():
print k, v
class PanPlotMode (PlotMode):
def __init__(self, plot):
PlotMode.__init__(self, plot, 'pan',
'Pan axes with left mouse, zoom with right',
'move.png')
# Holds handler IDs for callbacks.
self._button_press = None
self._button_release = None
self._motion_notify = None
self._button_pressed = None
def activate(self):
self._button_press = self.canvas.mpl_connect(
'button_press_event', self._on_button_press)
self._button_relese = self.canvas.mpl_connect(
'button_release_event', self._on_button_release)
#self._drag = self.canvas.mpl_connect(
# 'mouse_drag_event', self._on_drag)
def deactivate(self):
if self._button_press:
self.canvas.mpl_disconnect(self._button_press)
if self._button_release:
self.canvas.mpl_disconnect(self._button_release)
def _on_button_press(self, event):
if event.button == 1:
self._button_pressed = 1
elif event.button == 3:
self._button_pressed = 3
else:
self._button_pressed=None
return
x, y = event.x, event.y
# push the current view to define home if stack is empty
# if self._views.empty(): self.push_current()
self._xypress=[]
for i, a in enumerate(self.canvas.figure.get_axes()):
if x is not None and y is not None and a.in_axes(x, y) \
and a.get_navigate():
xmin, xmax = a.get_xlim()
ymin, ymax = a.get_ylim()
lim = xmin, xmax, ymin, ymax
self._xypress.append((x, y, a, i, lim,a.transData.deepcopy()))
self.canvas.mpl_disconnect(self._motion_notify)
cid = self.canvas.mpl_connect('motion_notify_event',
self._on_motion_notify)
self._motion_notify = cid
def _on_motion_notify(self, event):
"""The drag callback in pan/zoom mode"""
def format_deltas(event, dx, dy):
"""Returns the correct dx and dy based on the modifier keys"""
if event.key=='control':
if(abs(dx)>abs(dy)):
dy = dx
else:
dx = dy
elif event.key=='x':
dy = 0
elif event.key=='y':
dx = 0
elif event.key=='shift':
if 2*abs(dx) < abs(dy):
dx=0
elif 2*abs(dy) < abs(dx):
dy=0
elif(abs(dx)>abs(dy)):
dy=dy/abs(dy)*abs(dx)
else:
dx=dx/abs(dx)*abs(dy)
return (dx,dy)
for cur_xypress in self._xypress:
lastx, lasty, a, ind, lim, trans = cur_xypress
xmin, xmax, ymin, ymax = lim
#safer to use the recorded button at the press than current button:
#multiple button can get pressed during motion...
if self._button_pressed==1:
lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )
x, y = trans.inverse_xy_tup( (event.x, event.y) )
if a.get_xscale()=='log':
dx=1-lastx/x
else:
dx=x-lastx
if a.get_yscale()=='log':
dy=1-lasty/y
else:
dy=y-lasty
dx,dy=format_deltas(event,dx,dy)
if a.get_xscale()=='log':
xmin *= 1-dx
xmax *= 1-dx
else:
xmin -= dx
xmax -= dx
if a.get_yscale()=='log':
ymin *= 1-dy
ymax *= 1-dy
else:
ymin -= dy
ymax -= dy
elif self._button_pressed==3:
try:
dx=(lastx-event.x)/float(a.bbox.width())
dy=(lasty-event.y)/float(a.bbox.height())
dx,dy=format_deltas(event,dx,dy)
if a.get_aspect() != 'auto':
dx = 0.5*(dx + dy)
dy = dx
alphax = pow(10.0,dx)
alphay = pow(10.0,dy)
lastx, lasty = trans.inverse_xy_tup( (lastx, lasty) )
if a.get_xscale()=='log':
xmin = lastx*(xmin/lastx)**alphax
xmax = lastx*(xmax/lastx)**alphax
else:
xmin = lastx+alphax*(xmin-lastx)
xmax = lastx+alphax*(xmax-lastx)
if a.get_yscale()=='log':
ymin = lasty*(ymin/lasty)**alphay
ymax = lasty*(ymax/lasty)**alphay
else:
ymin = lasty+alphay*(ymin-lasty)
ymax = lasty+alphay*(ymax-lasty)
except OverflowError:
warnings.warn('Overflow while panning')
return
a.set_xlim(xmin, xmax)
a.set_ylim(ymin, ymax)
self.canvas.draw()
def _on_button_release(self, event):
'the release mouse button callback in pan/zoom mode'
self.canvas.mpl_disconnect(self._motion_notify)
if not self._xypress: return
self._xypress = None
self._button_pressed=None
self.canvas.draw()
class ZoomPlotMode (PlotMode):
def __init__(self, plot):
PlotMode.__init__(self, plot, 'zoom',
'Zoom to rectangle','zoom_to_rect.png')
self._selectors = {}
def activate(self):
for ax in self.canvas.figure.get_axes():
props = dict(facecolor = 'blue',
edgecolor = 'black',
alpha = 0.3,
fill = True)
rs = RectangleSelector(ax, self._on_select, drawtype='box',
useblit=True, rectprops = props)
self.canvas.draw()
self._selectors[rs] = ax
def deactivate(self):
self._mpl_disconnect_all()
self._selectors = {}
def _on_select(self, start, end):
ax = start.inaxes
ax.set_xlim((min(start.xdata, end.xdata), max(start.xdata, end.xdata)))
ax.set_ylim((min(start.ydata, end.ydata), max(start.ydata, end.ydata)))
self.canvas.draw()
class SelectPlotMode (PlotMode):
def __init__(self, plot):
PlotMode.__init__(self, plot, 'select',
'Select within rectangle', 'select.png')
self._selectors = {}
def activate(self):
for ax in self.canvas.figure.get_axes():
props = dict(facecolor = 'blue',
edgecolor = 'black',
alpha = 0.3,
fill = True)
rs = RectangleSelector(ax, self._on_select, drawtype='box',
useblit=True, rectprops = props)
self.canvas.draw()
self._selectors[rs] = ax
def deactivate(self):
self._mpl_disconnect_all()
self._selectors = {}
def _on_select(self, start, end):
self.plot.rectangle_select_callback(start.xdata, start.ydata,
end.xdata, end.ydata)
class PlotToolbar(gtk.Toolbar):
def __init__(self, plot):
gtk.Toolbar.__init__(self) gtk.Toolbar.__init__(self)
NavigationToolbar2.__init__(self, canvas) self.plot = plot
self._idleId = 0 self.canvas = plot.canvas
self._select_callback = None self._current_mode = None
canvas.connect('enter-notify-event', self.on_enter_notify)
def _init_toolbar(self):
self.set_style(gtk.TOOLBAR_ICONS)
self.tooltips = gtk.Tooltips() self.tooltips = gtk.Tooltips()
self._states = {}
self._selector = None ## Maps toolbar buttons to PlotMode objects.
self._mode_buttons = {}
self.set_property('show-arrow', False) self.set_property('show-arrow', False)
basedir = fluents.ICONDIR
#canvas.connect('enter-notify-event', self.on_enter_notify)
# setup base buttons self.show()
for text, tooltip_text, image_file, callback in self.toolitems: self.add_mode(DefaultPlotMode(self.plot))
if text is None: self.add_mode(PanPlotMode(self.plot))
self.insert(gtk.SeparatorToolItem(), -1 ) self.add_mode(ZoomPlotMode(self.plot))
continue self.add_mode(SelectPlotMode(self.plot))
fname = os.path.join(basedir, image_file)
image = gtk.Image()
image.set_from_file(fname)
tbutton = gtk.ToolButton(image, text)
self.insert(tbutton, -1)
tbutton.connect('clicked', getattr(self, callback))
tbutton.set_tooltip(self.tooltips, tooltip_text, 'Private')
self.insert(gtk.SeparatorToolItem(), -1) self.insert(gtk.SeparatorToolItem(), -1)
# mode/state buttons # Set up freeze button
rbutton = None btn = gtk.ToggleToolButton()
for text,tooltip_text,image_file,callback in self.radiobuttons:
if text is None:
self.insert(gtk.SeparatorToolItem(), -1 )
continue
fname = os.path.join(basedir, image_file)
image = gtk.Image()
image.set_from_file(fname)
rbutton = gtk.RadioToolButton(rbutton)
rbutton.set_icon_widget(image)
rbutton.connect('toggled', getattr(self, callback))
rbutton.set_tooltip(self.tooltips, tooltip_text, 'Private')
self._states[text] = rbutton
self.insert(rbutton, -1)
self.insert(gtk.SeparatorToolItem(), -1) fname = os.path.join(fluents.ICONDIR, "freeze.png")
image = gtk.Image()
toolitem = gtk.ToolItem() image.set_from_file(fname)
self.insert(toolitem, -1)
self.message = gtk.Label()
toolitem.add(self.message)
self.tb_freeze = gtk.ToolItem() btn.set_icon_widget(image)
self.chk = gtk.CheckButton () btn.connect('toggled', self._on_freeze_toggle)
self.chk.set_label ('Freeze') self.insert(btn, -1)
self.tb_freeze.add (self.chk)
self.tb_freeze.set_tooltip(self.tooltips, 'Freeze current selection')
self.insert(self.tb_freeze,-1)
toolitem = gtk.SeparatorToolItem()
self.insert(toolitem, -1)
self.show_all() self.show_all()
self.fileselect = FileChooserDialog(title='Save the figure', parent=self.win,) def add_mode(self, mode):
"""Adds a new mode to the toolbar."""
def on_enter_notify(self, widget, event): if len(self._mode_buttons) > 0:
if self._active != active_mode: other = self._mode_buttons.keys()[0]
self.set_mode(active_mode)
def set_mode(self, active):
# if state is unkown or not set, set to default
if active == None or active not in self._states.keys():
active = 'DEFAULT'
# remove current Selector:
if self._selector:
print "Removing selector"
self._selector = None
# remove current button bindings
if self._idPress != None:
self._idPress = self.canvas.mpl_disconnect(self._idPress)
self.mode = ''
if self._idRelease != None:
self._idRelease = self.canvas.mpl_disconnect(self._idRelease)
self.mode = ''
for state, button in self._states.items():
if state != active:
continue
if state == 'SELECT':
ax, = self.canvas.figure.get_axes()
props = dict(facecolor='blue', edgecolor = 'black',
alpha=0.3, fill=True)
self._selector = RectangleSelector(ax, self.onselect,
drawtype='box', useblit=True,
rectprops=props)
self.mode = 'Select rectangle mode'
print self.mode
elif state == 'PAN':
self._idPress = self.canvas.mpl_connect(
'button_press_event', self.press_pan)
self._idRelease = self.canvas.mpl_connect(
'button_release_event', self.release_pan)
self.mode = 'pan/zoom mode'
elif state == 'ZOOM':
self._idPress = self.canvas.mpl_connect('button_press_event', self.press_zoom)
self._idRelease = self.canvas.mpl_connect('button_release_event', self.release_zoom)
self.mode = 'Zoom to rect mode'
elif state == 'DEFAULT':
pass
else:
pass
if not button.get_active():
button.set_active(True)
for a in self.canvas.figure.get_axes():
a.set_navigate_mode(self._active)
self.set_message(self.mode)
self._active = active
# update global active_mode (for all toolbar)
globals()['active_mode'] = active
def get_mode(self):
if self._active == None:
return 'DEFAULT'
return self._active
def default(self, button):
"""Activates default mode"""
if not button.get_active():
return
self.set_mode('DEFAULT')
def select(self, button):
"""Activate select mode"""
if not button.get_active():
return
self.set_mode('SELECT')
def onselect(self,eclick, erelease):
'eclick and erelease are matplotlib events at press and release'
if self._select_callback:
self._select_callback(eclick.xdata, eclick.ydata, erelease.xdata, erelease.ydata)
def pan(self, button):
"""Activate the pan/zoom tool. pan with left button, zoom with right"""
if not button.get_active():
return
self.set_mode('PAN')
def zoom(self, button):
"""Activate zoom to rect mode"""
if not button.get_active():
return
self.set_mode('ZOOM')
def mouse_move(self, event):
"""Extend NavigationToolbar2.mouse_move to provide selection support."""
# Only update the rubberband for selection mode when mouse is
# within the plotting area.
#if event.inaxes and self._active=='SELECT':
# if self._lastCursor != cursors.SELECT_REGION:
# self.set_cursor(cursors.SELECT_REGION)
# self._lastCursor = cursors.SELECT_REGION
# if self._xypress is not None:
# x, y = event.x, event.y
# lastx, lasty, a, ind, lim, trans= self._xypress
# self.draw_rubberband(event, x, y, lastx, lasty)
NavigationToolbar2.mouse_move(self, event)
def set_select_callback(self, listener):
"""Allow plots to register a callback for selection events.
The callback will be called as listener(x1, y1, x2, y2). All
coordinates are in the plot coordinate system, not pixels or
widget coordinates.
"""
self._select_callback = listener
def set_cursor(self, cursor):
self.canvas.window.set_cursor(cursord[cursor])
def dynamic_update(self):
# legacy method; new method is canvas.draw_idle
self.canvas.draw_idle()
def mpl_draw_rubberband(self,event):
"""Use RectangleSelector for rubberband drawing"""
def draw_rubberband(self, event, x0, y0, x1, y1):
'adapted from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744'
drawable = self.canvas.window
if drawable is None:
return
gc = drawable.new_gc()
height = self.canvas.figure.bbox.height()
y1 = height - y1
y0 = height - y0
w = abs(x1 - x0)
h = abs(y1 - y0)
rect = [int(val)for val in min(x0,x1), min(y0, y1), w, h]
try: lastrect, imageBack = self._imageBack
except AttributeError:
#snap image back
if event.inaxes is None:
return
ax = event.inaxes
l,b,w,h = [int(val) for val in ax.bbox.get_bounds()]
b = int(height)-(b+h)
axrect = l,b,w,h
self._imageBack = axrect, drawable.get_image(*axrect)
drawable.draw_rectangle(gc, False, *rect)
self._idleId = 0
else: else:
def idle_draw(*args): other = None
drawable.draw_image(gc, imageBack, 0, 0, *lastrect)
drawable.draw_rectangle(gc, False, *rect)
self._idleId = 0
return False
if self._idleId == 0:
self._idleId = gobject.idle_add(idle_draw)
def save_figure(self, button): btn = gtk.RadioToolButton(other)
fname = self.fileselect.get_filename_from_user() btn.set_icon_widget(mode.get_icon())
if fname: btn.set_tooltip(self.tooltips, mode.tooltip, 'Private')
self.canvas.print_figure(fname) btn.connect('toggled', self._on_mode_toggle)
def configure_subplots(self, button): self._mode_buttons[btn] = mode
toolfig = Figure(figsize=(6,3)) self.insert(btn, -1)
canvas = self._get_canvas(toolfig)
toolfig.subplots_adjust(top=0.9)
tool = SubplotTool(self.canvas.figure, toolfig)
w = int (toolfig.bbox.width()) if self._current_mode == None:
h = int (toolfig.bbox.height()) self._current_mode = mode
def get_mode(self):
window = gtk.Window() """Returns the active mode name."""
window.set_title("Subplot Configuration Tool") if self._current_mode:
window.set_default_size(w, h) return self._current_mode.name
vbox = gtk.VBox() return None
window.add(vbox)
vbox.show()
canvas.show()
vbox.pack_start(canvas, True, True)
window.show()
def _get_canvas(self, fig):
return FigureCanvas(fig)
def get_mode_by_name(self, mode_name):
"""Returns the mode with the given name or None."""
for m in self._mode_buttons.values():
if m.name == mode_name:
return m
return None
def get_button(self, mode_name):
"""Returns the button that corresponds to a mode name."""
for b, m in self._mode_buttons.items():
if m.name == mode_name:
return b
return None
def set_mode(self, mode_name):
"""Sets a mode by name. Returns the mode or None"""
if mode_name == self._current_mode.name:
return None
if self._current_mode:
self._current_mode.deactivate()
new_mode = self.get_mode_by_name(mode_name)
if new_mode:
new_mode.activate()
self._current_mode = self.get_mode_by_name(mode_name)
else:
logger.log('warning', 'No such mode: %s' % mode_name)
if self.get_button(mode_name) and \
not self.get_button(mode_name).get_active():
self.get_button(mode_name).set_active(True)
return self._current_mode
def _on_mode_toggle(self, button):
if button.get_active():
self.set_mode(self._mode_buttons[button].name)
def _on_freeze_toggle(self, button):
self.plot.set_frozen(button.get_active())
# Create a view-changed signal that should be emitted every time # Create a view-changed signal that should be emitted every time
# the active view changes. # the active view changes.

View File

@ -25,12 +25,12 @@ class Project:
def add_selection_observer(self, observer): def add_selection_observer(self, observer):
self._selection_observers.append(observer) self._selection_observers.append(observer)
observer.selection_changed(self.get_selection()) #observer.selection_changed(self.get_selection())
def notify_selection_listeners(self, dim_name): def notify_selection_listeners(self, dim_name):
"""Notifies observers""" """Notifies observers"""
for observer in self._selection_observers: for observer in self._selection_observers:
observer.selection_changed(self.get_selection()) observer.selection_changed(dim_name, self.get_selection())
def add_dataset_observer(self, observer): def add_dataset_observer(self, observer):
self._dataset_observers.append(observer) self._dataset_observers.append(observer)
@ -43,11 +43,11 @@ class Project:
def set_selection(self, dim_name, selection): def set_selection(self, dim_name, selection):
"""Sets a current selection and notify observers""" """Sets a current selection and notify observers"""
if self._last_selection != selection: if self._last_selection != selection:
self.sel_obj[dim_name] = set(selection) self.sel_obj[dim_name] = set(selection)
self.notify_selection_listeners(dim_name) self.notify_selection_listeners(dim_name)
self._last_selection = selection self._last_selection = selection
def get_selection(self): def get_selection(self):
"""Returns the current selection object""" """Returns the current selection object"""
return self.sel_obj return self.sel_obj

View File

@ -139,7 +139,7 @@ class DimListController:
values = (selection.title, selection, dataset.get_dim_name(0)) values = (selection.title, selection, dataset.get_dim_name(0))
self.selstore.insert_after(i, None, values) self.selstore.insert_after(i, None, values)
def selection_changed(self, selection): def selection_changed(self, dim_name, selection):
"""Callback function from Project.""" """Callback function from Project."""
for dim in selection.dims(): for dim in selection.dims():

View File

@ -103,7 +103,7 @@ class TestDataFunction(workflow.Function):
def run(self): def run(self):
logger.log('notice', 'Injecting foo test data') logger.log('notice', 'Injecting foo test data')
x = randn(5000,4) x = randn(500,4)
X = dataset.Dataset(x) X = dataset.Dataset(x)
p = plots.ScatterPlot(X, X, 'rows', 'rows', '0_1', '0_2',name='scatter') p = plots.ScatterPlot(X, X, 'rows', 'rows', '0_1', '0_2',name='scatter')
p2 = plots.ScatterMarkerPlot(X, X, 'rows', 'rows', '0_1', '0_2',name='marker') p2 = plots.ScatterMarkerPlot(X, X, 'rows', 'rows', '0_1', '0_2',name='marker')