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
14 Figure -- Handle a list of graphs
18 Create a figure and eventually initialize a graphics with a signal list
21 Add a grapth into the figure
26 update(toupdate, todelete)
27 Update the signals of the figure
30 Set the mode of the current graph
32 [get|set]_layout(layout)
33 Set the layout, either horiz, vert or quad
36 Overload of matplotlib.pyplot.figure.draw()
39 Return a list of the signals in all graphs
42 Handle keystrokes during plot
45 import matplotlib
.pyplot
as plt
46 from matplotlib
.pyplot
import Figure
as MplFig
47 from graphs
import Graph
, LinGraph
51 def __init__(self
, sigs
={}, fig
=None):
53 If a signal list is provided, add a graph with the signal list
54 By default, create an empty list of graph and set_ the layout to horiz
58 self
._layout
= "horiz"
59 self
._MODES
_NAMES
_TO
_OBJ
= {"lin":LinGraph
}
61 # Slow way... Surely there exist something faster
62 self
._OBJ
_TO
_MODES
_NAMES
= {}
63 for k
, v
in self
._MODES
_NAMES
_TO
_OBJ
.iteritems():
64 self
._OBJ
_TO
_MODES
_NAMES
[v
] = k
68 elif isinstance(sigs
, dict):
73 def add(self
, sigs
={}):
74 """ Add a graph into the figure and set it as current graph.
75 Up to four graphs can be plotted on the same figure.
76 Additionnal attemps are ignored.
77 By default, do nothing.
79 if len(self
.axes
) > 3:
80 assert 0, "Maximum graph number reached"
82 # _graph_position use the current length of self.axes to compute
83 # the graph coordinates. So we add a fake element into the list
84 # and we remove it once the size is computed
85 self
.axes
.append(None)
86 gr
= LinGraph(self
, self
._graph
_position
(len(self
.axes
) - 1),\
87 sigs
, label
=str(len(self
.axes
)))
88 del self
.axes
[len(self
.axes
) - 1]
89 ax
= self
.add_axes(gr
)
91 # Force layout refresh
92 self
.set_layout(self
._layout
)
94 def delete(self
, num
=1):
95 """ Delete a graph from the figure
96 By default, delete the first graph.
97 Act as a "pop" with curgraph variable.
99 if not isinstance(num
, int):
100 assert 0, "Bad graph number"
101 if len(self
.axes
) < 1 or num
< 1 or num
> len(self
.axes
):
102 assert 0, "Bad graph number"
103 del self
.axes
[num
- 1]
105 def update(self
, u
, d
):
106 """ Update the graphs
108 if not isinstance(u
, dict):
110 if not isinstance(d
, dict):
115 for sn
in g
.signals():
123 # def get_mode(self):
124 # """ Return the mode of the current graph"""
125 # return self._OBJ_TO_MODES_NAMES(self._current)
127 def set_mode(self
, args
):
128 """ Set the mode of the current graph
130 # Currently this cannot be tested (only one mode available)
134 assert 0, "No graph defined"
135 g
= (self
._MODES
_NAMES
_TO
_OBJ
(gmode
))(old_graph
)
136 self
._graphs
[self
._graphs
.index(old_graph
)] = g
138 def get_layout(self
):
139 """ Return the figure layout"""
142 def set_layout(self
, layout
="quad"):
143 """ Set the layout of the figure, default is quad
144 horiz : graphs are horizontaly aligned
145 vert : graphs are verticaly aligned
146 quad : graphs are 2 x 2 at maximum
147 Other values are ignored
149 # To change the layout: use ax.set_position
151 if layout
== "horiz" or layout
== "vert" or layout
== "quad":
152 self
._layout
= layout
154 assert 0, "Bad layout"
156 for gn
, g
in enumerate(self
.axes
):
157 g
.set_position(self
._graph
_position
(gn
))
159 def draw(self
, canvas
):
160 tmp
= MplFig
.draw(self
, canvas
)
162 self
._kid
= self
.canvas
.mpl_connect('key_press_event', self
._key
)
166 def _key(self
, event
):
167 """ Handle key press event
168 1, 2: toggle vertical cursors #0 and #1
169 3, 4: toggle horizontal cursors #0 and #1
171 if event
.inaxes
is None:
176 if g
== event
.inaxes
:
182 # Set cursor for graph
184 g
.toggle_cursors("vert", 0, event
.xdata
)
185 elif event
.key
== "2":
186 g
.toggle_cursors("vert", 1, event
.xdata
)
187 elif event
.key
== "3":
188 g
.toggle_cursors("horiz", 0, event
.ydata
)
189 elif event
.key
== "4":
190 g
.toggle_cursors("horiz", 1, event
.ydata
)
195 def _graph_position(self
, num
):
196 """ Compute the position of the graph upon its number
198 # 1 graph: x:0->1 y:0->1 dx=1 dy=1
199 # 3 graphs horiz: x:0->.333 y:0->1 dx=.333 dy=1
200 # 3 graphs quads: x:0->.5 y:0->.5 dx=.5 dy=.5
202 if self
._layout
== "horiz":
204 dy
= 1.0 / len(self
.axes
)
205 num_to_xy
= [[0, y
] for y
in xrange(len(self
.axes
))]
206 elif self
._layout
== "vert":
207 dx
= 1.0 / len(self
.axes
)
209 num_to_xy
= [[x
, 0] for x
in xrange(len(self
.axes
))]
210 elif self
._layout
== "quad":
213 num_to_xy
= [[x
, y
] for y
in xrange(2) for x
in xrange(2)]
215 assert 0, "Bad layout"
217 pos_x
= num_to_xy
[num
][0]
218 pos_y
= num_to_xy
[len(self
.axes
) - num
- 1][1]
219 x1
= pos_x
* dx
+ 0.15 * dx
220 y1
= pos_y
* dy
+ 0.15 * dy
221 return [x1
, y1
, dx
* 0.75, dy
* 0.75]
225 """ Return the list of signals in all graphs
228 for sn
in g
.get_signals():
233 """ Return the graph list """
236 layout
= property(get_layout
, set_layout
)
237 # mode = property(get_mode, set_mode)