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
8 signals read/write Dict of Signals
9 graphs read/write List of Graphs
10 layout read/write String representing the layout
16 curgraph: alias to the current graph
23 Figure -- Handle a list of graphs
27 Create a figure and eventually initialize a graphics with a signal list
30 Add a grapth into the figure
35 update(toupdate, todelete)
36 Update the signals of the figure
39 Set the mode of the current graph
41 [get|set]_layout(layout)
42 Set the layout, either horiz, vert or quad
45 Overload of matplotlib.pyplot.figure.draw()
48 Return a list of the signals in all graphs
51 Handle keystrokes during plot
54 from matplotlib
.pyplot
import Figure
as MplFig
, Axes
55 from graphs
import Graph
, LinGraph
57 MAX_GRAPHS_PER_FIGURE
= 4
60 """ Manage figure and its layout
62 def __init__(self
, sigs
={}, fig
=None):
63 """ Instanciate a Figure.
64 If a signal list is provided, add a graph with the signal list
65 By default, create an empty list of graph and set the layout to horiz
70 If provided, the function instanciate the Figure with one Graph and
77 The figure instanciated and initialized
81 self
._layout
= "horiz"
82 self
._MODES
_NAMES
_TO
_OBJ
= {"lin":LinGraph
}
84 # FIXME: Slow way... Surely there exist something faster
85 self
._OBJ
_TO
_MODES
_NAMES
= {}
86 for k
, v
in self
._MODES
_NAMES
_TO
_OBJ
.iteritems():
87 self
._OBJ
_TO
_MODES
_NAMES
[v
] = k
91 elif isinstance(sigs
, dict):
95 assert 0, _("Bad type")
97 def add(self
, sigs
={}):
98 """ Add a graph into the figure and set it as current graph.
99 Up to four graphs can be plotted on the same figure.
100 Additionnal attemps are ignored.
101 By default, do nothing.
105 sigs: dict of Signals
106 The list of Signals to add
112 if len(self
.axes
) > (MAX_GRAPHS_PER_FIGURE
- 1):
113 assert 0, _("Maximum graph number reached")
115 # _graph_position use the current length of self.axes to compute
116 # the graph coordinates. So we add a fake element into the list
117 # and we remove it once the size is computed
119 a
= Axes(self
, (0, 0, 1, 1))
120 self
._axstack
.add(key
, a
)
121 gr
= LinGraph(self
, self
._graph
_position
(len(self
.axes
) - 1),\
122 sigs
, label
=str(len(self
.axes
)))
123 self
._axstack
.remove(a
)
124 # Allocate a free number up to MAX_GRAPH_PER_FIGURE
126 while num
< MAX_GRAPHS_PER_FIGURE
:
127 if self
._axstack
.get(num
) is None:
130 ax
= self
._axstack
.add(num
, gr
)
132 # Force layout refresh
133 self
.set_layout(self
._layout
)
135 def delete(self
, num
=1):
136 """ Delete a graph from the figure
137 By default, delete the first graph.
142 The position of the graph to delete
148 if not isinstance(num
, int):
149 assert 0, _("Bad graph number")
150 if len(self
.axes
) < 1 or num
< 1 or num
> len(self
.axes
):
151 assert 0, _("Bad graph number")
152 self
._axstack
.remove(self
._axstack
[num
- 1][1][1])
153 # Force layout refresh
154 self
.set_layout(self
._layout
)
156 def update(self
, u
, d
):
157 """ Update the graphs
158 FIXME: This function is deprecated by the use of GObject event system
160 For each Graph in the Figure, replace updated signals in u, and remove
166 List of updated Signals to replace in the graphs
169 List of deleted Signals to remove from the graphs
175 if not isinstance(u
, dict):
176 assert 0, _("Bad type")
177 if not isinstance(d
, dict):
178 assert 0, _("Bad type")
182 for sn
in g
.signals():
190 # def get_mode(self):
191 # """ Return the mode of the current graph"""
192 # FIXME: DISABLED AS ONLY ONE MODE CURRENTLY AVAILABLE
193 # return self._OBJ_TO_MODES_NAMES(self._current)
195 def set_mode(self
, args
):
196 """ Set the mode of the current graph
197 Replace the graph provided by a new one of other mode, i.e. copy the
199 The graph is replaced in the Figure internal list of graphs
203 args: tuple of (graph, gmode)
213 # Currently this cannot be tested (only one mode available)
217 assert 0, _("No graph defined")
218 g
= (self
._MODES
_NAMES
_TO
_OBJ
(gmode
))(old_graph
)
219 self
._graphs
[self
._graphs
.index(old_graph
)] = g
221 def get_layout(self
):
222 """ Return the figure layout
231 The name of the current layout
235 def set_layout(self
, layout
="quad"):
236 """ Set the layout of the figure, default is 'quad'
237 'horiz' : graphs are horizontaly aligned
238 'vert' : graphs are verticaly aligned
239 'quad' : graphs are 2 x 2 at maximum
240 Other values are ignored
245 One of ['horiz'|'vert'|'quad']. Default is 'quad'
251 # To change the layout: use ax.set_position
253 if layout
== "horiz" or layout
== "vert" or layout
== "quad":
254 self
._layout
= layout
256 assert 0, _("Bad layout")
258 for gn
, g
in enumerate(self
.axes
):
259 g
.set_position(self
._graph
_position
(gn
))
261 def draw(self
, canvas
):
263 Wrapper to parent class function. Set also the key_press_event callback
268 Canvas to use to draw the figure
272 tmp: Value returned by parent class function call
274 tmp
= MplFig
.draw(self
, canvas
)
276 self
._kid
= self
.canvas
.mpl_connect('key_press_event', self
._key
)
280 def _key(self
, event
):
281 """ Handle key press event
282 1, 2: toggle vertical cursors #0 and #1
283 3, 4: toggle horizontal cursors #0 and #1
287 event: Matplotlib Event
288 The event that triggered the call-back
294 if event
.inaxes
is None:
299 if g
== event
.inaxes
:
305 # Set cursor for graph
307 g
.toggle_cursors("vert", 0, event
.xdata
)
308 elif event
.key
== "2":
309 g
.toggle_cursors("vert", 1, event
.xdata
)
310 elif event
.key
== "3":
311 g
.toggle_cursors("horiz", 0, event
.ydata
)
312 elif event
.key
== "4":
313 g
.toggle_cursors("horiz", 1, event
.ydata
)
318 def _graph_position(self
, num
):
319 """ Compute the position of the graph upon its number and the layout
324 The position of the graph in the list
329 The coordinates of the graph
331 # 1 graph: x:0->1 y:0->1 dx=1 dy=1
332 # 3 graphs horiz: x:0->.333 y:0->1 dx=.333 dy=1
333 # 3 graphs quads: x:0->.5 y:0->.5 dx=.5 dy=.5
335 if self
._layout
== "horiz":
337 dy
= 1.0 / len(self
.axes
)
338 num_to_xy
= [[0, y
] for y
in xrange(len(self
.axes
))]
339 elif self
._layout
== "vert":
340 dx
= 1.0 / len(self
.axes
)
342 num_to_xy
= [[x
, 0] for x
in xrange(len(self
.axes
))]
343 elif self
._layout
== "quad":
346 num_to_xy
= [[x
, y
] for y
in xrange(2) for x
in xrange(2)]
348 assert 0, _("Bad layout")
350 pos_x
= num_to_xy
[num
][0]
351 pos_y
= num_to_xy
[len(self
.axes
) - num
- 1][1]
352 x1
= pos_x
* dx
+ 0.15 * dx
353 y1
= pos_y
* dy
+ 0.15 * dy
354 return [x1
, y1
, dx
* 0.75, dy
* 0.75]
358 """ Return the list of signals in all graphs
367 A list of the signals contained in all graphs
370 for sn
in g
.get_signals():
375 """ Return the graph list
387 layout
= property(get_layout
, set_layout
)
388 # mode = property(get_mode, set_mode)