Add toolbar support to plots, with home made selection support <phew>.

This commit is contained in:
Truls Alexander Tangstad 2006-04-26 12:11:23 +00:00
parent 490038d9bf
commit 21edeecdc3
2 changed files with 179 additions and 41 deletions

View File

@ -24,6 +24,8 @@ class FluentApp:
# Application variables
self.project = None
self.current_data = None
self._last_view = None
self._plot_toolbar = None
gtk.glade.set_custom_handler(self.custom_object_factory)
self.widget_tree = gtk.glade.XML(GLADEFILENAME, 'appwindow')
@ -52,10 +54,29 @@ class FluentApp:
return self.log_view
def create_main_view(self, str1, str2, int1, int2):
self.main_view = plots.MainView()
self.main_view = plots.MainView(self._update_toolbar)
self.main_view.show()
return self.main_view
def _update_toolbar(self, view):
logger.log("debug", "view changed to %s" % view)
# don't do anything on no change
if self._last_view == view:
return
self._last_view = view
window = self.widget_tree.get_widget('plot_toolbar_dock')
if self._plot_toolbar:
print "removing", self._plot_toolbar
window.remove(self._plot_toolbar)
self._plot_toolbar = view.get_toolbar()
if self._plot_toolbar:
print "adding", self._plot_toolbar
window.add(self._plot_toolbar)
print "window contents:", window.get_children()
def create_small_view(self, str1, str2, int1, int2):
self.small_view = plots.SmallView()
self.small_view.show()

View File

@ -4,6 +4,7 @@ import gtk
import matplotlib
import scipy
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTK as NavigationToolbar2
from matplotlib.axes import Subplot
from matplotlib.figure import Figure
from matplotlib.numerix import arange, sin, pi
@ -12,13 +13,14 @@ from system import logger
class MainView (gtk.Notebook):
def __init__(self):
def __init__(self, view_listener):
gtk.Notebook.__init__(self)
self.set_show_tabs(False)
self.set_show_border(False)
# Add a multiple pane view and a single pane view.
self.small_view = SmallView()
self.small_view.set_view_listener(view_listener)
self.small_view.show()
self.large_view = LargeView()
self.large_view.show()
@ -47,7 +49,43 @@ class MainView (gtk.Notebook):
def insert_view(self, view):
self.small_view.insert_view(view)
class Plot (gtk.Frame):
def __init__(self, title):
gtk.Frame.__init__(self)
self.mark_active(False)
self.connect('button_press_event', self.on_button_press)
self.sel_obj = None
self.active = False
self.title = title
def get_title(self):
return self.title
def on_button_press(self, *rest):
# logger.log('debug', 'button pressed in plot')
self.mark_active(True)
def mark_active(self, active):
if active:
self.set_shadow_type(gtk.SHADOW_IN)
else:
self.set_shadow_type(gtk.SHADOW_OUT)
self.active = active
def update(self, key):
pass
def set_project(self, project):
self.project = project
def get_toolbar(self):
return None
class SmallView (gtk.Table):
def __init__(self):
gtk.Table.__init__(self, 2, 2, True)
@ -61,6 +99,7 @@ class SmallView (gtk.Table):
self.set_row_spacings(3)
self.set_col_spacings(3)
self._listener = None
for x in range(self.cols):
for y in range(self.rows):
@ -68,6 +107,9 @@ class SmallView (gtk.Table):
child.parent_signalling = child.connect('button_press_event', self.__view_button_event__)
self.attach(child, x, x+1, y, y+1)
def set_view_listener(self, listener):
self._listener = listener
def set_child(self, child, col, row):
cur_widget = self.child_views[col][row]
cur_widget.disconnect(cur_widget.parent_signalling)
@ -115,6 +157,9 @@ class SmallView (gtk.Table):
new_focus.mark_active(True)
self.active_x = x
self.active_y = y
if self._listener:
logger.log("debug", "emitting")
self._listener(new_focus)
def find_child(self, child):
for i, row in enumerate(self.child_views):
@ -157,37 +202,7 @@ class LargeView (gtk.Frame):
child.hide()
self.remove(child)
return child
class Plot (gtk.Frame):
def __init__(self, title):
gtk.Frame.__init__(self)
self.mark_active(False)
self.connect('button_press_event', self.on_button_press)
self.sel_obj = None
self.active = False
self.title = title
def get_title(self):
return self.title
def on_button_press(self, *rest):
# logger.log('debug', 'button pressed in plot')
self.mark_active(True)
def mark_active(self, active):
if active:
self.set_shadow_type(gtk.SHADOW_IN)
else:
self.set_shadow_type(gtk.SHADOW_OUT)
self.active = active
def update(self, key):
pass
def set_project(self, project):
self.project = project
class EmptyView (Plot):
def __init__(self):
@ -212,6 +227,101 @@ class EmptyView (Plot):
self.label.hide()
self.ebox.hide()
Plot.hide(self)
class NavToolbar(NavigationToolbar2):
toolitems = (('Select', 'Select within rectangle', 'zoom_to_rect.png',
'select'),) + NavigationToolbar2.toolitems
def __init__(self, *args):
NavigationToolbar2.__init__(self, *args)
self._select_callback = None
def select(self, *args):
"""Selection mode selected handler."""
if self._active == 'SELECT':
self._active = None
else:
self._active = 'SELECT'
if self._idPress is not None:
self._idPress = self.canvas.mpl_disconnect(self._idPress)
self.mode = ''
if self._idRelease is not None:
self._idRelease = self.canvas.mpl_disconnect(self._idRelease)
self.mode = ''
if self._active:
self._idPress = self.canvas.mpl_connect('button_press_event', self.press_select)
self._idRelease = self.canvas.mpl_connect('button_release_event', self.release_select)
self.mode = 'Select rectangle mode'
self.set_message(self.mode)
def set_message(self, s):
"""Set status in toolbar to string s.
Overrided to make sure message can be updated even when
drawing rubberband.
"""
self.message.set_label(s)
def press_select(self, event):
"""Mouse button pressed handler for selection mode."""
x, y = event.x, event.y
for i, a in enumerate(self.canvas.figure.get_axes()):
if event.inaxes==a and event.inaxes.get_navigate():
xmin, xmax = a.get_xlim()
ymin, ymax = a.get_ylim()
lim = xmin, xmax, ymin, ymax
self._xypress = x, y, a, i, lim, a.transData.deepcopy()
break
self.press(event)
def release_select(self, event):
"""Mouse button released handler for selection mode."""
# only release if button was pressed inside first?
if self._xypress:
x, y = event.x, event.y
lastx, lasty, a, ind, lim, trans = self._xypress
lastx, lasty = a.transData.inverse_xy_tup( (lastx, lasty) )
x, y = a.transData.inverse_xy_tup( (x, y) )
if self._select_callback:
self._select_callback(lastx, lasty, x, y)
self._xypress = None
self.draw()
self.release(event)
def mouse_move(self, event):
"""Extend NavigationToolbar2.mouse_move to provide selection support."""
from matplotlib.backend_bases import cursors
# 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
class SinePlot(Plot):
@ -225,9 +335,14 @@ class SinePlot(Plot):
ax.plot(t,s)
self.canvas = FigureCanvas(fig)
self._toolbar = NavToolbar(self.canvas, None)
self._toolbar.set_property('show-arrow', False)
self.add(self.canvas)
self.canvas.show()
def get_toolbar(self):
return self._toolbar
class ScatterPlot(Plot):
def __init__(self, dataset,id_dim, sel_dim,id_1,id_2):
@ -243,21 +358,22 @@ class ScatterPlot(Plot):
self.xaxis_data = dataset._array[:,x_index]
self.yaxis_data = dataset._array[:,y_index]
ax.plot(self.xaxis_data,self.yaxis_data,'og')
###
self.canvas = FigureCanvas(fig)
self.add(self.canvas)
rectprops = dict(facecolor='gray', edgecolor = 'black',
alpha=0.2, fill=True) #cool
self.sel = RectangleSelector(ax, self.rectangle_select_callback,
drawtype='box',useblit=True,rectprops=rectprops)
self.canvas.show()
def rectangle_select_callback(self,event1, event2):
self._toolbar = NavToolbar(self.canvas, None)
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):
'event1 and event2 are the press and release events'
x1, y1 = event1.xdata, event1.ydata
x2, y2 = event2.xdata, event2.ydata
ydata = self.yaxis_data
xdata = self.xaxis_data
@ -297,3 +413,4 @@ class ScatterPlot(Plot):
self.ax.plot(self.xaxis_data,self.yaxis_data,'og')
self.ax.plot(xdata_new,ydata_new,'or')
self.canvas.draw()