FIXED: Do not draw cursor if outside graph limits
[oscopy/ivan.git] / Oscopy / Figure.py
blobf981452352db2c77070b4739b9fdc35466deed1b
1 """ Figure handler
3 A figure consist of a list of up to 4 graphs, with a layout.
4 Signal list are passed directly to each graph.
5 Layout can be either all graphs stacked verticaly, horizontaly or in quad
7 curgraph: alias to the current graph
8 gn: graph number
9 g: graph
10 sn: signal name
12 class:
14 Figure -- Handle a list of graphs
16 methods:
17 __init__(sigs)
18 Create a figure and eventually initialize a graphics with a signal list
20 add(sigs)
21 Add a grapth into the figure
23 delete(num)
24 Delete a graph
26 update(toupdate, todelete)
27 Update the signals of the figure
29 select(graphnum)
30 Select the graph to be the default
32 list()
33 List the graph of the current figure
35 set_mode(mode)
36 Set the mode of the current graph
38 set_layout(layout)
39 Set the layout, either horiz, vert or quad
41 plot()
42 Plot the figure
44 get_sigs()
45 Return a list of the signals in all graphs
47 set_unit()
48 Set the current graph units
50 set_scale()
51 Set the current graph axis scale
53 set_range()
54 Set the current graph axis range
56 key()
57 Handle keystrokes during plot
58 """
60 import types
61 import matplotlib.pyplot as plt
62 from Graphs import Graph
63 from Graphs.LinGraphs import LinGraph
64 from Graphs.FFTGraph import FFTGraph, IFFTGraph
66 class Figure:
68 def __init__(self, sigs = {}):
69 """ Create a Figure.
70 If a signal list is provided, add a graph with the signal list
71 By default, create an empty list of graph and set_ the layout to horiz
72 """
73 self.graphs = []
74 self.layout = "horiz"
75 self.curgraph = None
76 if sigs == {}:
77 return
78 elif type(sigs) == types.DictType:
79 self.add(sigs)
80 else:
81 return
83 def add(self, sigs = {}):
84 """ Add a graph into the figure and set_ it as current graph.
85 Up to four graphs can be plotted on the same figure.
86 Additionnal attemps are ignored.
87 By default, do nothing.
88 """
89 if len(self.graphs) > 3:
90 return
91 gr = LinGraph(sigs)
92 self.graphs.append(gr)
93 self.select(self.graphs.index(gr) + 1)
95 def delete(self, num = 1):
96 """ Delete a graph from the figure
97 By default, delete the first graph.
98 Act as a "pop" with curgraph variable.
99 """
100 if not type(num) == types.IntType:
101 return
102 gn = eval(num) # Graph number
103 if len(self.graphs) < 1 or gn < 1 or gn > len(self.graphs):
104 return
105 if self.curgraph == self.graphs[gn - 1]:
106 if len(self.graphs) == 1:
107 # Only one element in the list
108 self.curgraph = None
109 elif gn == len(self.graphs):
110 # Last element, go to the previous
111 self.curgraph = self.graphs[gn - 2]
112 else:
113 self.curgraph = self.graphs[gn]
114 del self.graphs[gn - 1]
116 def update(self, u, d):
117 """ Update the graphs
119 if type(u) != types.DictType:
120 return
121 if type(d) != types.DictType:
122 return
124 for g in self.graphs:
125 ug = {}
126 dg = {}
127 for sn in g.get_sigs():
128 if sn in u:
129 ug[sn] = u[sn]
130 elif sn in d:
131 dg[sn] = d[sn]
132 g.insert(ug)
133 g.remove(dg)
135 def select(self, gn = 0):
136 """ Select the current graph
138 if gn < 1 or gn > len(self.graphs):
139 return
140 self.curgraph = self.graphs[gn - 1]
142 def list(self):
143 """ List the graphs from the figure
145 for gn, g in enumerate(self.graphs):
146 if g == self.curgraph:
147 print " *",
148 else:
149 print " ",
150 print "Graph", gn + 1, ":", g
152 def set_mode(self, gmode):
153 """ Set the mode of the current graph
155 if self.curgraph == None:
156 return
157 if type(gmode) == types.StringType:
158 if gmode == "lin":
159 g = LinGraph(self.curgraph)
160 elif gmode == "fft":
161 g = FFTGraph(self.curgraph)
162 elif gmode == "ifft":
163 g = IFFTGraph(self.curgraph)
164 else:
165 return
166 self.graphs[self.graphs.index(self.curgraph)] = g
167 self.curgraph = g
169 def set_layout(self, layout = "quad"):
170 """ Set the layout of the figure, default is quad
171 horiz : graphs are horizontaly aligned
172 vert : graphs are verticaly aligned
173 quad : graphs are 2 x 2 at maximum
174 Other values are ignored
176 if layout == "horiz" or layout == "vert" or layout == "quad":
177 self.layout = layout
178 return
179 else:
180 return
182 def plot(self, fig):
183 """ Plot the figure in Matplotlib Figure instance fig
184 First compute the number of subplot and the layout
185 And then really call the plot function of each graph
187 # Set the number of lines and rows
188 if len(self.graphs) < 1:
189 return
190 if self.layout == "horiz":
191 nx = len(self.graphs)
192 ny = 1
193 elif self.layout == "vert":
194 nx = 1
195 ny = len(self.graphs)
196 elif self.layout == "quad":
197 if len(self.graphs) == 1:
198 nx = 1
199 ny = 1
200 elif len(self.graphs) == 2:
201 # For two graphs in quad config, go to horiz
202 nx = 2
203 ny = 1
204 else:
205 nx = 2
206 ny = 2
207 else:
208 nx = 2
209 ny = 2
211 # Plot the whole figure
212 for gn, g in enumerate(self.graphs):
213 ax = fig.add_subplot(nx, ny, gn+1)
214 g.plot(ax)
215 self.kid = fig.canvas.mpl_connect('key_press_event', self.key)
217 def insert(self, sigs):
218 """ Add a signal into the current graph
220 if not self.curgraph == None:
221 self.curgraph.insert(sigs)
223 def remove(self, sigs, where = "current"):
224 """ Delete a signal from the current graph
226 if where == "current":
227 if not self.curgraph == None:
228 self.curgraph.remove(sigs)
229 elif where == "all":
230 for g in self.graphs:
231 g.remove(sigs)
233 def get_sigs(self):
234 """ Return the list of signals in all graphs
236 for g in self.graphs:
237 for sn in g.get_sigs():
238 yield sn
240 def set_unit(self, xu, yu = ""):
241 """ Set the current graph units
243 if not self.curgraph == None:
244 self.curgraph.set_unit(xu, yu)
246 def set_scale(self, a):
247 """ Set the current graph axis scale
249 if not self.curgraph == None:
250 self.curgraph.set_scale(a)
252 def set_range(self, a1 = "reset_", a2 = None, a3 = None, a4 = None):
253 """ Set the axis range of the current graph
255 if not self.curgraph == None:
256 self.curgraph.set_range(a1, a2, a3, a4)
258 def key(self, event):
259 """ Handle key press event
260 1, 2: toggle vertical cursors #0 and #1
261 3, 4: toggle horizontal cursors #0 and #1
263 if event.inaxes == None:
264 return
265 # Find graph
266 g = None
267 for g in self.graphs:
268 if g.ax == event.inaxes:
269 break
270 else:
271 g = None
272 if g == None:
273 return
274 # Set cursor for graph
275 if event.key == "1":
276 g.toggle_cursors("vert", 0, event.xdata)
277 elif event.key == "2":
278 g.toggle_cursors("vert", 1, event.xdata)
279 elif event.key == "3":
280 g.toggle_cursors("horiz", 0, event.ydata)
281 elif event.key == "4":
282 g.toggle_cursors("horiz", 1, event.ydata)
283 else:
284 return
285 event.canvas.draw()