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.
laydi/system/plots.py

495 lines
16 KiB
Python

import pygtk
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
from matplotlib.widgets import RectangleSelector
from matplotlib import cm
from system import logger
class MainView (gtk.Notebook):
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()
self.append_page(self.small_view)
self.append_page(self.large_view)
self.set_current_page(0)
def goto_small(self):
if self.get_current_page() == 0:
return None
self.set_current_page(0)
view = self.large_view.remove_child()
self.small_view.return_current(view)
def goto_large(self):
if self.get_current_page() == 1:
return None
self.set_current_page(1)
view = self.small_view.borrow_current()
self.large_view.set_child(view)
def show(self):
gtk.Notebook.show(self)
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
self.selection_listener = None
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 selection_changed(self, selection):
pass
def set_selection_listener(self, listener):
"""Allow project to listen to selections.
The selection will propagate back to all plots through the
selection_changed() method. The listener will be called as
listener(dimension_name, ids).
"""
self.selection_listener = listener
def get_toolbar(self):
return None
class SmallView (gtk.Table):
def __init__(self):
gtk.Table.__init__(self, 2, 2, True)
self.child_views = [[EmptyView(), EmptyView()],
[EmptyView(), EmptyView()]]
self.cols = 2
self.rows = 2
self.active_x = 0
self.active_y = 0
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):
child = self.child_views[x][y]
child.parent_signalling = child.connect('button_press_event', self.__view_button_event__)
self.attach(child, x, x+1, y, y+1)
# drag'n'drop
for views in self.child_views:
for view in views:
view.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("GTK_TREE_MODEL_ROW",gtk.TARGET_SAME_APP,7)],gtk.gdk.ACTION_LINK)
view.connect("drag-data-received",self.drag_received_by_view,view)
def drag_received_by_view(self, widget, drag_context, x, y, selection, info, timestamp, view):
treestore, path = selection.tree_get_row_drag_data()
iter = treestore.get_iter(path)
obj = treestore.get_value(iter,2)
loc = self.find_child(view)
if loc and isinstance(obj, Plot) and (not self.find_child(obj)):
self.set_child(obj,loc[0],loc[1])
def set_view_listener(self, listener):
self._listener = listener
def set_child(self, child, col, row):
# urg, this probably belongs somewhere else
child.drag_dest_set(gtk.DEST_DEFAULT_ALL, [("GTK_TREE_MODEL_ROW",gtk.TARGET_SAME_APP,7)],gtk.gdk.ACTION_LINK)
child.connect("drag-data-received",self.drag_received_by_view,child)
cur_widget = self.child_views[col][row]
cur_widget.disconnect(cur_widget.parent_signalling)
self.remove(cur_widget)
self.attach(child, col, col+1, row, row+1)
child.parent_signalling = child.connect('button_press_event', self.__view_button_event__)
self.child_views[col][row] = child
if cur_widget.active:
child.mark_active(True)
cur_widget.mark_active(False)
child.show()
def borrow_current(self):
self.borrowed = self.child_views[self.active_x][self.active_y]
self.remove_child(self.active_x, self.active_y)
return self.borrowed
def return_current(self, view):
self.set_child(view, self.active_x, self.active_y)
def remove_child(self, col, row):
self.remove(self.child_views[col][row])
self.attach(EmptyView(), col, col+1, row, row+1)
def insert_view(self, child):
if not self.find_child(child):
self.set_child(child, self.active_x, self.active_y)
def show(self):
for x in self.child_views:
for y in x:
y.show()
gtk.Table.show(self)
def hide(self):
for x in self.child_views:
for y in x:
y.hide()
gtk.Table.hide(self)
def set_active(self, x, y):
old_focus = self.child_views[self.active_x][self.active_y]
new_focus = self.child_views[x][y]
old_focus.mark_active(False)
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):
for j, v in enumerate(row):
if v == child:
return (i, j)
return None
def __view_button_event__(self, view, *rest):
loc = self.find_child(view)
if loc:
self.set_active(loc[0], loc[1])
def get_view(self, x, y):
return self.child_views[x][y]
class LargeView (gtk.Frame):
def __init__(self):
gtk.Frame.__init__(self)
self.child_view = EmptyView()
self.add(self.child_view)
def set_child(self, child):
self.remove(self.child_view)
self.child_view.hide()
self.add(child)
self.child_view = child
child.show()
def hide(self):
self.child_view.hide()
gtk.Frame.hide(self)
def show(self):
self.child_view.show()
gtk.Frame.show(self)
def remove_child(self):
child = self.child_view
child.hide()
self.remove(child)
return child
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.connect('button_press_event', self.on_button_press)
self.label = label
self.ebox = ebox
self.show()
def show(self):
self.ebox.show()
self.label.show()
Plot.show(self)
def hide(self):
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):
def __init__(self):
Plot.__init__(self, 'Sine plot')
fig = Figure(figsize=(5,4), dpi=72)
ax = fig.add_subplot(111)
t = arange(0.0,3.0,0.01)
s = sin(2*pi*t)
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 LinePlot(Plot):
def __init__(self, dataset, name="Line plot"):
Plot.__init__(self, name)
fig = Figure(figsize=(5,4), dpi=72)
self.canvas = FigureCanvas(fig)
self.ax = ax = fig.add_subplot(111)
self._dataset = dataset
import rpy
silent_eval = rpy.with_mode(rpy.NO_CONVERSION, rpy.r)
rpy.with_mode(rpy.NO_CONVERSION, rpy.r.assign)("m", dataset.get_matrix())
ymin, ymax = rpy.r("range(m)*1.1")
self._ymin, self._ymax = ymin, ymax
silent_eval("res <- apply(m, 2, density, from=%s, to=%s)" % (ymin, ymax))
silent_eval("k <- sapply(1:length(res), function(id) rev(res[[id]]$y))")
self._bg_matrix = bg_matrix = rpy.r("k")
rpy.r.rm(["k", "res", "m"])
# Hack - we draw plot in selection_changed()
self.selection_changed(None)
self.add(self.canvas)
self.canvas.show()
# We use a regular toolbar as we don't need selections
self._toolbar = NavigationToolbar2(self.canvas, None)
self._toolbar.set_property('show-arrow', False)
def get_toolbar(self):
self.canvas.draw()
return self._toolbar
def selection_changed(self, selection):
self.ax.clear()
rows, cols = self._bg_matrix.shape
self.ax.imshow(self._bg_matrix, cmap=cm.Greys, extent=(0.5, cols+0.5, self._ymin, self._ymax))
dim_2, dim_1 = self._dataset.get_dim_names()
if selection:
ids = selection[dim_2] # current identifiers
index = [ind for id,ind in self._dataset[dim_2].items() if id in ids] #conversion to index
for i in index:
line = self._dataset.get_matrix()[i]
self.ax.plot(range(1, len(line)+1), line)
self.ax.set_xlim((0.5, cols+0.5))
self.ax.set_ylim((self._ymin, self._ymax))
self.canvas.draw()
class ScatterPlot(Plot):
def __init__(self, dataset, id_dim, sel_dim, id_1, id_2, name="Scatter plot"):
Plot.__init__(self, name)
fig = Figure(figsize=(5,4), dpi=72)
self.ax = ax = fig.add_subplot(111)
self.current_dim = id_dim
# testing testing
self.dataset = dataset
x_index = dataset[sel_dim][id_1]
y_index = dataset[sel_dim][id_2]
self.xaxis_data = dataset._array[:,x_index]
self.yaxis_data = dataset._array[:,y_index]
ax.plot(self.xaxis_data,self.yaxis_data,'og')
ax.set_title(self.get_title())
ax.set_xlabel("%s - %s" % (sel_dim, id_1))
ax.set_ylabel("%s - %s" % (sel_dim, id_2))
###
self.canvas = FigureCanvas(fig)
self.add(self.canvas)
self.canvas.show()
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):
ydata = self.yaxis_data
xdata = self.xaxis_data
# find indices of selected area
if x1>x2:
if y1<y2:
index =scipy.nonzero((xdata<x1) & (xdata>x2) & (ydata>y1) & (ydata<y2))
else:
index =scipy.nonzero((xdata<x1) & (xdata>x2) & (ydata<y1) & (ydata>y2))
else:
#logger.log('debug','Selection x_start less than x_end')
if y1<y2:
#logger.log('debug','Selection y_start less than y_end')
index =scipy.nonzero((xdata>x1) & (xdata<x2) & (ydata>y1) & (ydata<y2))
else:
#logger.log('debug','Selection y_start bigger than y_end')
index =scipy.nonzero((xdata>x1) & (xdata<x2) & (ydata<y1) & (ydata>y2))
ids = self.dataset.get_identifiers(self.current_dim, index)
self.selection_listener(self.current_dim, ids)
def selection_changed(self, selection):
ids = selection[self.current_dim] # current identifiers
index = self.dataset.get_indices(self.current_dim, ids)
xdata_new = scipy.take(self.xaxis_data,index) #take data
ydata_new = scipy.take(self.yaxis_data,index)
self.ax.clear()
self.ax.plot(self.xaxis_data,self.yaxis_data,'og')
self.ax.plot(xdata_new,ydata_new,'or')
self.canvas.draw()