Add toolbar support to plots, with home made selection support <phew>.
This commit is contained in:
parent
490038d9bf
commit
21edeecdc3
|
@ -24,6 +24,8 @@ class FluentApp:
|
||||||
# Application variables
|
# Application variables
|
||||||
self.project = None
|
self.project = None
|
||||||
self.current_data = None
|
self.current_data = None
|
||||||
|
self._last_view = None
|
||||||
|
self._plot_toolbar = None
|
||||||
|
|
||||||
gtk.glade.set_custom_handler(self.custom_object_factory)
|
gtk.glade.set_custom_handler(self.custom_object_factory)
|
||||||
self.widget_tree = gtk.glade.XML(GLADEFILENAME, 'appwindow')
|
self.widget_tree = gtk.glade.XML(GLADEFILENAME, 'appwindow')
|
||||||
|
@ -52,10 +54,29 @@ class FluentApp:
|
||||||
return self.log_view
|
return self.log_view
|
||||||
|
|
||||||
def create_main_view(self, str1, str2, int1, int2):
|
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()
|
self.main_view.show()
|
||||||
return self.main_view
|
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):
|
def create_small_view(self, str1, str2, int1, int2):
|
||||||
self.small_view = plots.SmallView()
|
self.small_view = plots.SmallView()
|
||||||
self.small_view.show()
|
self.small_view.show()
|
||||||
|
|
195
system/plots.py
195
system/plots.py
|
@ -4,6 +4,7 @@ import gtk
|
||||||
import matplotlib
|
import matplotlib
|
||||||
import scipy
|
import scipy
|
||||||
from matplotlib.backends.backend_gtkagg import FigureCanvasGTKAgg as FigureCanvas
|
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.axes import Subplot
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
from matplotlib.numerix import arange, sin, pi
|
from matplotlib.numerix import arange, sin, pi
|
||||||
|
@ -12,13 +13,14 @@ from system import logger
|
||||||
|
|
||||||
|
|
||||||
class MainView (gtk.Notebook):
|
class MainView (gtk.Notebook):
|
||||||
def __init__(self):
|
def __init__(self, view_listener):
|
||||||
gtk.Notebook.__init__(self)
|
gtk.Notebook.__init__(self)
|
||||||
self.set_show_tabs(False)
|
self.set_show_tabs(False)
|
||||||
self.set_show_border(False)
|
self.set_show_border(False)
|
||||||
|
|
||||||
# Add a multiple pane view and a single pane view.
|
# Add a multiple pane view and a single pane view.
|
||||||
self.small_view = SmallView()
|
self.small_view = SmallView()
|
||||||
|
self.small_view.set_view_listener(view_listener)
|
||||||
self.small_view.show()
|
self.small_view.show()
|
||||||
self.large_view = LargeView()
|
self.large_view = LargeView()
|
||||||
self.large_view.show()
|
self.large_view.show()
|
||||||
|
@ -47,7 +49,43 @@ class MainView (gtk.Notebook):
|
||||||
def insert_view(self, view):
|
def insert_view(self, view):
|
||||||
self.small_view.insert_view(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):
|
class SmallView (gtk.Table):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
gtk.Table.__init__(self, 2, 2, True)
|
gtk.Table.__init__(self, 2, 2, True)
|
||||||
|
|
||||||
|
@ -61,6 +99,7 @@ class SmallView (gtk.Table):
|
||||||
|
|
||||||
self.set_row_spacings(3)
|
self.set_row_spacings(3)
|
||||||
self.set_col_spacings(3)
|
self.set_col_spacings(3)
|
||||||
|
self._listener = None
|
||||||
|
|
||||||
for x in range(self.cols):
|
for x in range(self.cols):
|
||||||
for y in range(self.rows):
|
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__)
|
child.parent_signalling = child.connect('button_press_event', self.__view_button_event__)
|
||||||
self.attach(child, x, x+1, y, y+1)
|
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):
|
def set_child(self, child, col, row):
|
||||||
cur_widget = self.child_views[col][row]
|
cur_widget = self.child_views[col][row]
|
||||||
cur_widget.disconnect(cur_widget.parent_signalling)
|
cur_widget.disconnect(cur_widget.parent_signalling)
|
||||||
|
@ -115,6 +157,9 @@ class SmallView (gtk.Table):
|
||||||
new_focus.mark_active(True)
|
new_focus.mark_active(True)
|
||||||
self.active_x = x
|
self.active_x = x
|
||||||
self.active_y = y
|
self.active_y = y
|
||||||
|
if self._listener:
|
||||||
|
logger.log("debug", "emitting")
|
||||||
|
self._listener(new_focus)
|
||||||
|
|
||||||
def find_child(self, child):
|
def find_child(self, child):
|
||||||
for i, row in enumerate(self.child_views):
|
for i, row in enumerate(self.child_views):
|
||||||
|
@ -158,36 +203,6 @@ class LargeView (gtk.Frame):
|
||||||
self.remove(child)
|
self.remove(child)
|
||||||
return 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):
|
class EmptyView (Plot):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -214,6 +229,101 @@ class EmptyView (Plot):
|
||||||
Plot.hide(self)
|
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):
|
class SinePlot(Plot):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Plot.__init__(self, 'Sine plot')
|
Plot.__init__(self, 'Sine plot')
|
||||||
|
@ -225,9 +335,14 @@ class SinePlot(Plot):
|
||||||
ax.plot(t,s)
|
ax.plot(t,s)
|
||||||
|
|
||||||
self.canvas = FigureCanvas(fig)
|
self.canvas = FigureCanvas(fig)
|
||||||
|
self._toolbar = NavToolbar(self.canvas, None)
|
||||||
|
self._toolbar.set_property('show-arrow', False)
|
||||||
self.add(self.canvas)
|
self.add(self.canvas)
|
||||||
self.canvas.show()
|
self.canvas.show()
|
||||||
|
|
||||||
|
def get_toolbar(self):
|
||||||
|
return self._toolbar
|
||||||
|
|
||||||
|
|
||||||
class ScatterPlot(Plot):
|
class ScatterPlot(Plot):
|
||||||
def __init__(self, dataset,id_dim, sel_dim,id_1,id_2):
|
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.xaxis_data = dataset._array[:,x_index]
|
||||||
self.yaxis_data = dataset._array[:,y_index]
|
self.yaxis_data = dataset._array[:,y_index]
|
||||||
ax.plot(self.xaxis_data,self.yaxis_data,'og')
|
ax.plot(self.xaxis_data,self.yaxis_data,'og')
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|
||||||
self.canvas = FigureCanvas(fig)
|
self.canvas = FigureCanvas(fig)
|
||||||
self.add(self.canvas)
|
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()
|
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'
|
'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
|
ydata = self.yaxis_data
|
||||||
xdata = self.xaxis_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(self.xaxis_data,self.yaxis_data,'og')
|
||||||
self.ax.plot(xdata_new,ydata_new,'or')
|
self.ax.plot(xdata_new,ydata_new,'or')
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
|
|
||||||
|
|
Reference in New Issue