Fix Graph range return type
[oscopy.git] / oscopy / context.py
blob53383d78e5328f43bdf7e032e8b66b3e92b9da89
1 from readers.detect_reader import DetectReader
2 from readers.reader import ReadError
3 from writers.detect_writer import DetectWriter
4 from writers.writer import WriteError
5 from figure import Figure
6 import gobject
8 class Context(gobject.GObject):
9 """ Class Context -- Interface between signals, files and figures
11 This object is the interface between the signals, the files and the figures.
12 It gathers operations on figures, signals, readers and writers and thus
13 can be used for a program such ioscopy.
14 It maintain a list of figures, a dict of reader and a dict of signals.
16 Signals are stored in a dict according to their user name (Signal.name)
17 Files are stored in a dict according to their name
19 Properties
20 signals read/write Dict of Signals read
21 readers read/write Dict of Readers used
22 figures read/write Dict of Figures instanciated
24 Signals
25 'begin-transaction' is received to notify that Dependancy Signal data is
26 being changed. Event re-emitted toward Listeners
27 'end-transaction' is emitted once the Result Signal data is recomputed
28 to notify Listeners that they can recompute their own data
30 Abbreviations
31 sigs: dict of sigs
32 sns : signal names
33 opts: options
34 fn : filename
35 """
36 __gsignals__ = {
37 'begin-transaction': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
38 'end-transaction': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ())
41 def __init__(self):
42 """ Create the instance variables
44 Parameters
45 ----------
46 None
48 Returns
49 -------
50 Object instanciated
51 """
52 gobject.GObject.__init__(self)
53 self._readers = {}
54 self._figures = []
55 self._signals = {}
56 self._update_num = -1
57 self._signal_name_to_reader = {}
59 def create(self, sigs):
60 """ Instanciate a new figure
61 No error reported if parameter type mismatches, silently does nothing
63 Parameters
64 ----------
65 sigs: string or list of signals
66 Contains the list of signals for the Figure instanciation
67 See also Figure.Figure()
69 Returns
70 -------
71 Nothing
72 """
73 if isinstance(sigs, list):
74 # Called from commandline,
75 # Get the signal list from args
76 if not sigs == "":
77 sigs = self.names_to_signals(sigs)
78 else:
79 # No signal list provided
80 sigs = {}
81 elif not isinstance(sigs, dict):
82 return
83 f = Figure(sigs)
84 self._figures.append(f)
86 def destroy(self, num):
87 """ Delete a figure
88 If num is out of range issue an assertion.
89 Figure is replaced by None in the list to keep code simple
91 Parameters
92 ----------
93 num: integer
94 The number of the figure to destroy
96 Returns
97 -------
98 Nothing
99 """
100 if num > len(self._figures) or num < 1:
101 assert 0, _("Out of range figure number")
102 self._figures[num - 1] = None
104 def read(self, fn):
105 """ Read signals from file
106 Overwrite signals in case of Signal name conflict.
107 On success, Reader and Signals are added in the lists.
109 Parameters
110 ----------
111 fn: string
112 The file name
114 Returns
115 -------
116 sigs: Dict of Signals
117 List of Signals read from the file
119 Raises
120 ------
121 string
122 Do not read the same file twice
124 NotImplementedError
125 When the file type is not managed by any Reader
127 # File already loaded ?
128 if fn in self._readers.keys():
129 raise _("File already loaded, use update to read it again")
131 r = DetectReader(fn)
132 if r is None:
133 raise NotImplementedError()
134 sigs = r.read(fn)
135 self.connect('begin-transaction', r.on_begin_transaction)
136 self.connect('end-transaction', r.on_end_transaction)
138 # Insert signals into the dict
139 for sn in sigs.keys():
140 self._signals[sn] = sigs[sn]
141 self._signal_name_to_reader[sn] = r
142 self._readers[fn] = r
143 return sigs
145 def write(self, fn, fmt, sns, opts):
146 """ Write signals to file
148 Parameters
149 ----------
150 fn: string
151 The file name
153 fmt: string
154 The file format
156 sns: dict of Signals
157 The Signals to record
159 opts: string
160 List of options to pass to the Writer
162 Returns
163 -------
164 Nothing
166 Raises
167 ------
168 NotImplementedError
169 When the file type is not managed by any Writer
171 # Create the object
172 sigs = self.names_to_signals(sns)
173 if not sigs:
174 return
175 w = DetectWriter(fmt, fn, True)
176 if w is not None:
177 w.write(fn, sigs, opts)
178 else:
179 raise NotImplementedError()
181 def update(self, r=None, upn=-1):
182 """ Reread signal from files.
183 For each file, reread it, and for updated, new and deleted signal,
184 update the Signal dict accordingly.
185 Act recursively.
187 Note: recursion is now deprecated by using GObject event system by
188 Signals, it will be removed in a future release
190 Parameters
191 ----------
192 r: Reader
193 Update Signals from this Reader only
195 upn: integer
196 Reserved for internal use (recursion) should always be the default value
198 Returns
199 -------
200 n: new Signals
201 """ ## SUPPORT FOR UPDATE SINGLE READER
202 n = {} # New signals
203 if upn == -1:
204 self.emit('begin-transaction')
205 # Normal call create the new list etc etc
206 self._update_num += 1
207 if r is None:
208 for reader in self._readers.itervalues():
209 # print "Updating signals from", reader
210 n.update(self.update(reader, self._update_num))
211 else:
212 n.update(self.update(r, self._update_num))
213 self.emit('end-transaction')
214 else:
215 # First look at its dependencies
216 if hasattr(r, "get_depends") and callable(r.get_depends):
217 for sn in r.get_depends():
218 # print " Updating signals from", self._signal_name_to_reader[sn]
219 n.update(self.update(self._signal_name_to_reader[sn], self._update_num))
220 # TODO: Update depencies: what happens to vo when
221 # vout is deleted ? It seems it is not deleted: it should!
222 # Update the reader
223 n.update(r.update(self._update_num, keep=False))
224 return n
226 # Find deleted signals
227 d = []
228 for sn, s in self._signals.iteritems():
229 #n.update(s.update(self._update_num, False))
230 if s.data is None:
231 d.append(sn)
232 # Insert new signals
233 self._signals.update(n)
234 # Delete signals from all graphs of all figures
235 for f in self._figures:
236 for g in f.graphs:
237 g.remove(self.names_to_signals(d))
238 g.update_signals()
239 if f.canvas is not None:
240 f.canvas.draw()
241 for sn in d:
242 del self._signals[sn]
244 def freeze(self, sns):
245 """ Set the freeze flag of signals, i.e. disable updates
247 Parameters
248 ----------
249 sns: dict of Signals
250 Signals list to be frozen
252 Returns
253 -------
254 Nothing
256 sigs = self.names_to_signals(sns)
257 for s in sigs.itervalues():
258 s.freeze = True
260 def unfreeze(self, sns):
261 """ Unset the freeze flag of signals, i.e. enable updates
263 Parameters
264 ----------
265 sns: dict of Signals
266 Signals list to be unfrozen
268 Returns
269 -------
270 Nothing
272 sigs = self.names_to_signals(sns)
273 for s in sigs.itervalues():
274 s.freeze = False
276 @property
277 def signals(self):
278 """ Dict of loaded signals
280 Parameters
281 ----------
282 Nothing
284 Returns
285 -------
286 dict of Signals
287 List of Signals currently loaded in this Context
289 return self._signals
291 def import_signal(self, sig, name):
292 """ Import a signal from outside this Context
294 Parameters
295 ----------
296 sig: Signal
297 Signal to be imported
299 name: string
300 Name to be assigned to the Signal in this Context
302 Returns
303 -------
304 ss: dict of Signals
305 Contains only one element, the Signal imported
307 Example
308 -------
309 >>> ctxt=oscopy.Context()
310 >>> ctxt.read('demo/tran.dat')
311 >>> ctxt.signals
312 >>> v1=oscopy.Signal('v1', 'V')
313 >>> ctxt.import_signal(v1, 'v1imported')
314 >>> ctxt.signals
316 r = DetectReader(sig)
317 ss = r.read((sig, name))
318 for sn, s in ss.iteritems():
319 self._signals[sn] = s
320 self._signal_name_to_reader[sn] = r
321 # FIXME: Probleme, comment gerer les dependances ?
323 rname = "%s=%s" % (name, sig.name)
324 self.readers[rname] = r
325 return ss
327 def names_to_signals(self, sns):
328 """ Return a signal dict from the signal names list provided
329 If no signals are found, return {}
330 Use this function is reserved, will be made private in a future release
332 Parameters
333 ----------
334 sns: list of string
335 The list of name of Signals to retrieve
337 Returns
338 -------
339 sigs: dict of Signals
340 The list of Signals found in this Context
342 if not sns or not self._signals:
343 return {}
344 sigs = {}
346 # Prepare the signal list
347 for sn in sns:
348 if sn in self._signals.keys():
349 sigs[sn] = self._signals[sn]
350 else:
351 # print sn + ": Not here"
352 pass
354 # No signals found
355 # if not sigs:
356 # print "No signals found"
357 return sigs
359 @property
360 def figures(self):
361 """ Return the figure list
363 Parameters
364 ----------
365 Nothing
367 Returns
368 -------
369 List of Figures managed in this Context
371 return self._figures
373 @property
374 def readers(self):
375 """ Return the reader list
377 Parameters
378 ----------
379 Nothing
381 Returns
382 -------
383 List of Readers managed in this Context
385 return self._readers