Update Makefile.am for recent changes
[reinteract/rox.git] / lib / replot.py
blobda89add70accf41e614e5412eca8227bf39c705e
1 import gtk
2 from matplotlib.figure import Figure
3 from matplotlib.backends.backend_cairo import RendererCairo, FigureCanvasCairo
4 import numpy
6 import reinteract.custom_result as custom_result
8 class _PlotResultCanvas(FigureCanvasCairo):
9 def draw_event(*args):
10 # Since we never change anything about the figure, the only time we
11 # need to redraw is in response to an expose event, which we handle
12 # ourselves
13 pass
15 class PlotResult(custom_result.CustomResult):
16 def __init__(self, *args, **kwargs):
17 self.__args = args
18 self.__kwargs = kwargs
20 def create_widget(self):
21 widget = PlotWidget(self)
22 widget.axes.plot(*self.__args, **self.__kwargs)
24 return widget
26 class ImshowResult(custom_result.CustomResult):
27 def __init__(self, *args, **kwargs):
28 self.__args = args
29 self.__kwargs = kwargs
31 def create_widget(self):
32 widget = PlotWidget(self)
33 widget.axes.imshow(*self.__args, **self.__kwargs)
35 return widget
37 class PlotWidget(gtk.DrawingArea):
38 __gsignals__ = {
39 'button-press-event': 'override',
40 'button-release-event': 'override',
41 'expose-event': 'override'
44 def __init__(self, result):
45 gtk.DrawingArea.__init__(self)
46 self.figure = Figure(facecolor='white', figsize=(6,4.5))
47 self.canvas = _PlotResultCanvas(self.figure)
49 self.axes = self.figure.add_axes((0.05,0.05,0.9,0.9))
50 self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE)
52 def do_expose_event(self, event):
53 cr = self.window.cairo_create()
55 renderer = RendererCairo(self.figure.dpi)
56 renderer.set_width_height(self.allocation.width, self.allocation.height)
57 renderer.set_ctx_from_surface(cr.get_target())
59 # event.region is not bound: http://bugzilla.gnome.org/show_bug.cgi?id=487158
60 # gdk_context = gtk.gdk.CairoContext(renderer.ctx)
61 # gdk_context.region(event.region)
62 # gdk_context.clip()
64 self.figure.draw(renderer)
66 def do_button_press_event(self, event):
67 if event.button == 3:
68 custom_result.show_menu(self, event, save_callback=self.__save)
69 return True
70 else:
71 return True
73 def do_button_release_event(self, event):
74 return True
76 def do_realize(self):
77 gtk.DrawingArea.do_realize(self)
78 cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR)
79 self.window.set_cursor(cursor)
81 def do_size_request(self, requisition):
82 requisition.width = self.figure.bbox.width()
83 requisition.height = self.figure.bbox.height()
85 def __save(self, filename):
86 # The save/restore here was added to matplotlib's after 0.90. We duplicate
87 # it for compatibility with older versions.
89 orig_dpi = self.figure.dpi.get()
90 orig_facecolor = self.figure.get_facecolor()
91 orig_edgecolor = self.figure.get_edgecolor()
93 try:
94 self.canvas.print_figure(filename)
95 finally:
96 self.figure.dpi.set(orig_dpi)
97 self.figure.set_facecolor(orig_facecolor)
98 self.figure.set_edgecolor(orig_edgecolor)
99 self.figure.set_canvas(self.canvas)
101 # def do_size_allocate(self, allocation):
102 # gtk.DrawingArea.do_size_allocate(self, allocation)
104 # dpi = self.figure.dpi.get()
105 # self.figure.set_size_inches (allocation.width / dpi, allocation.height / dpi)
107 def _validate_args(args):
109 # The matplotlib argument parsing is a little wonky
111 # plot(x, y, 'fmt', y2)
112 # plot(x1, y2, x2, y2, 'fmt', y3)
114 # Are valid, but
116 # plot(x, y, y2)
118 # is not. We just duplicate the algorithm here
120 l = len(args)
121 i = 0
122 while True:
123 xi = None
124 yi = None
125 formati = None
127 remaining = l - i
128 if remaining == 0:
129 break
130 elif remaining == 1:
131 yi = i
132 i += 1
133 # The 'remaining != 3 and' encapsulates the wonkyness referred to above
134 elif remaining == 2 or (remaining != 3 and not isinstance(args[i + 2], basestring)):
135 # plot(...., x, y [, ....])
136 xi = i
137 yi = i + 1
138 i += 2
139 else:
140 # plot(....., x, y, format [, ...])
141 xi = i
142 yi = i + 1
143 formati = i + 2
144 i += 3
146 if xi != None:
147 arg = args[xi]
148 if isinstance(arg, numpy.ndarray):
149 xshape = arg.shape
150 elif isinstance(arg, list):
151 # Not supporting nested python lists here
152 xshape = (len(arg),)
153 else:
154 raise TypeError("Expected numpy array or list for argument %d" % (xi + 1))
155 else:
156 xshape = None
158 # y isn't optional, pretend it is to preserve code symmetry
160 if yi != None:
161 arg = args[yi]
162 if isinstance(arg, numpy.ndarray):
163 yshape = arg.shape
164 elif isinstance(arg, list):
165 # Not supporting nested python lists here
166 yshape = (len(arg),)
167 else:
168 raise TypeError("Expected numpy array or list for argument %d" % (yi + 1))
169 else:
170 yshape = None
172 if xshape != None and yshape != None and xshape != yshape:
173 raise TypeError("Shapes of arguments %d and %d aren't compatible" % ((xi + 1), (yi + 1)))
175 if formati != None and not isinstance(args[formati], basestring):
176 raise TypeError("Expected format string for argument %d" % (formati + 1))
179 def plot(*args, **kwargs):
180 _validate_args(args)
181 return PlotResult(*args, **kwargs)
183 def imshow(*args, **kwargs):
184 return ImshowResult(*args, **kwargs)