/[ascend]/branches/adrian/pygtk/observer.py
ViewVC logotype

Contents of /branches/adrian/pygtk/observer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3076 - (show annotations) (download) (as text)
Wed Aug 19 13:34:55 2015 UTC (6 years, 10 months ago) by adrian
File MIME type: text/x-python
File size: 29407 byte(s)
Fixed observer values displaying issue
1 import gi
2 gi.require_version('Gtk', '3.0')
3 import os.path
4
5 from study import *
6 from unitsdialog import *
7
8 OBSERVER_EDIT_COLOR = "#008800"
9 OBSERVER_NOEDIT_COLOR = "#000088"
10 OBSERVER_NORMAL_COLOR = "black"
11 OBSERVER_TAINTED_COLOR = "#FFBBBB"
12 OBSERVER_DEAD_COLOR = "#ababab"
13 # This code uses the techniques described in
14 # http://www.daa.com.au/pipermail/pygtk/2006-February/011777.html
15 # http://piman.livejournal.com/361173.html
16
17 OBSERVER_NUM=0
18
19 class ClickableTreeColumn(Gtk.TreeViewColumn):
20 def __init__(self, title="", *args, **kwargs):
21 super(ClickableTreeColumn, self).__init__(None, *args, **kwargs)
22 self.label = Gtk.Label(label="%s" % title)
23 self.label.show()
24 self.set_widget(self.label)
25 self.title = title
26 if title != "":
27 self.set_resizable(True)
28 #self.set_sort_column_id(0)
29 #self.set_clickable(True)
30
31 def do_connect(self):
32 """ Connect the defined 'on_click' method. Note: must be called after
33 this object (ClickableTreeColumn) has been added to the TreeView,
34 eg mytreeview.append_column(col). """
35 button = self.label.get_ancestor(Gtk.Button)
36 h = button.connect("clicked",self.on_click)
37 #button.clicked()
38
39 def on_click(self,widget,*args):
40 print "RECEIVED EVENT"
41
42 class ObserverColumn:
43 """
44 A class to identify the instance that relates to a specify column
45 and the units of measurement and column title, etc.
46 """
47 def __init__(self,instance,index,name=None,units=None,browser=None):
48 self.instance = instance
49 self.name = name
50 self.index = index
51
52 if name==None:
53 if browser == None:
54 name = "UNNAMED"
55 else:
56 name = browser.sim.getInstanceName(instance)
57
58 if units is None:
59 units = instance.getType().getPreferredUnits()
60 if units is None:
61 units = instance.getType().getDimensions().getDefaultUnits()
62
63 uname = str(units.getName())
64
65 self.units = units
66 self.uname = uname
67
68 ##### CELSIUS TEMPERATURE WORKAROUND
69 self.instance = instance
70 if instance.getType().isRefinedReal() and str(instance.getType().getDimensions()) == 'TMP':
71 units = Preferences().getPreferredUnitsOrigin(str(instance.getType().getName()))
72 if units == CelsiusUnits.get_celsius_sign():
73 uname = CelsiusUnits.get_celsius_sign()
74 ##### CELSIUS TEMPERATURE WORKAROUND
75
76 if len(uname) or uname.find("/")!=-1:
77 uname = "["+uname+"]"
78
79 if uname == "":
80 _title = "%s" % (name)
81 else:
82 _title = "%s / %s" % (name, uname)
83
84 self.title = _title
85 self.name = name
86
87 def __repr__(self):
88 return "ObserverColumn(name="+self.name+")"
89
90 def cellvalue(self, column, cell, model, row_iter, user_data=None):
91 _rowobject = model.get_value(row_iter,0)
92
93 cell.set_property('editable',False)
94 cell.set_property('weight',400)
95 try:
96 if _rowobject.active or _rowobject.dead:
97 _rawval = self.instance.getRealValue()
98 if self.instance.getType().isRefinedSolverVar():
99 if self.instance.isFixed():
100 cell.set_property('editable',True)
101 cell.set_property('weight',700)
102 cell.set_property('foreground',OBSERVER_EDIT_COLOR)
103 else:
104 cell.set_property('foreground',OBSERVER_NOEDIT_COLOR)
105 _dataval = _rawval / self.units.getConversion()
106 else:
107 cell.set_property('foreground',OBSERVER_NORMAL_COLOR)
108 try :
109 _rawval = _rowobject.values[self.index]
110 _dataval = _rawval / self.units.getConversion()
111 except:
112 _dataval = ""
113 if _rowobject.tainted is True:
114 cell.set_property('background', OBSERVER_TAINTED_COLOR)
115 elif _rowobject.dead or user_data == True:
116 cell.set_property('background', OBSERVER_DEAD_COLOR)
117 cell.set_property('editable', False)
118 else:
119 cell.set_property('background', None)
120
121 ##### CELSIUS TEMPERATURE WORKAROUND
122 _dataval = CelsiusUnits.convert_show(self.instance, str(_dataval), False)
123 ##### CELSIUS TEMPERATURE WORKAROUND
124 except Exception, e:
125 _dataval = ""
126
127 cell.set_property('text', str(_dataval))
128
129 class ObserverRow:
130 """
131 Just a container for a vector of values, but with columns that
132 should correspond to those in the Observer object's vector of
133 ObserverColumn objects.
134 """
135 def __init__(self,values=None,active=True,tainted=False):
136 if values==None:
137 values={}
138 self.tainted = tainted
139 self.values = values
140 self.active = active
141 self.dead = False
142 self.error_msg = None
143
144 def make_static(self, table, values=None):
145 self.active = False
146 #print "TABLE COLS:",table.cols
147 #print "ROW VALUES:",self.values
148 if values is None:
149 _v = {}
150 for col in table.cols.values():
151 _v[col.index] = col.instance.getRealValue()
152 self.values = _v
153 else:
154 self.values = values
155 #print "Made static, values:",self.values
156
157 def get_values(self,table):
158 vv = {}
159 if not self.active:
160 for k,v in table.cols.iteritems():
161 try:
162 vv[k]=(self.values[v.index]/v.units.getConversion())
163 except:
164 vv[k]=""
165 return vv
166 else:
167 for index, col in table.cols.iteritems():
168 vv[index] = float(col.instance.getRealValue())/col.units.getConversion()
169 return vv
170
171 class ObserverTab:
172
173 def __init__(self,browser,tab,name=None,alive=True):
174 global OBSERVER_NUM
175 self.colindex = 0
176 if name==None:
177 OBSERVER_NUM=OBSERVER_NUM+1
178 name = "Observer %d" % OBSERVER_NUM
179 self.name = name
180 self.browser=browser
181 self.browser.builder.connect_signals(self)
182 self.view = self.browser.builder.get_object('observerview')
183 self.view.set_has_tooltip(True)
184 self.addbutton = self.browser.builder.get_object('add')
185 self.tab = tab
186 self.alive=alive
187 self.reloaded = False
188 self.old_path = None
189 self.current_instance = None
190 if self.alive:
191 self.browser.reporter.reportNote("New observer is 'alive'")
192
193 self.keptimg = Gtk.Image()
194 self.activeimg = Gtk.Image()
195 self.errorimg = Gtk.Image()
196 self.activeimg.set_from_file(os.path.join(browser.options.assets_dir,"active.png"))
197 self.errorimg.set_from_file(os.path.join(browser.options.assets_dir,"solveerror.png"))
198 # create PixBuf objects from these?
199 self.rows = []
200 _store = Gtk.TreeStore(object)
201 self.cols = {}
202 self.tvcols = {}
203 self.renderers = {}
204
205 # create the 'active' pixbuf column
206 _renderer = Gtk.CellRendererPixbuf()
207 _col = ClickableTreeColumn("")
208 _col.pack_start(_renderer,False)
209 _col.set_cell_data_func(_renderer, self.activepixbufvalue)
210 self.view.append_column(_col)
211 _col.do_connect()
212
213 # get the context menu
214 self.treecontext=self.browser.builder.get_object("observercontext")
215 self.studycolumnmenuitem=self.browser.builder.get_object("study_column")
216 self.unitsmenuitem=self.browser.builder.get_object("units2")
217 self.deleterowmenuitem=self.browser.builder.get_object("delete_row")
218 self.deletecolumnmenuitem=self.browser.builder.get_object("delete_column")
219 self.plotmenuitem=self.browser.builder.get_object("plotmenuitem")
220 # initially there will not be any other columns
221
222 if self.alive:
223 # for a 'live' Observer, create the 'active' bottom row
224 self.browser.reporter.reportNote("Adding empty row to store")
225 _row = ObserverRow()
226 self.activeiter = _store.append(None, [_row] )
227 self.rows.append(_row)
228 else:
229 self.activeiter = None
230
231 self.view.set_model(_store)
232 self.browser.reporter.reportNote("Created observer '%s'" % self.name)
233
234 _sel = self.view.get_selection()
235 _sel.set_mode(Gtk.SelectionMode.MULTIPLE)
236
237 def activepixbufvalue(self,column,cell,model,iter, dummy):
238 _rowobject = model.get_value(iter,0)
239 if _rowobject.active:
240 cell.set_property('pixbuf',self.activeimg.get_pixbuf())
241 elif _rowobject.tainted:
242 cell.set_property('pixbuf',self.errorimg.get_pixbuf())
243 else:
244 cell.set_property('pixbuf',self.keptimg.get_pixbuf())
245
246 def get_values(self):
247 _v = []
248 for col in self.cols.values():
249 _v.append(col.instance.getRealValue())
250 return _v
251
252 def on_add_clicked(self,*args):
253 self.do_add_row()
254
255 def on_clear_clicked(self,*args):
256 if self.alive is False:
257 self.on_close_observer_clicked()
258 return
259 _store = self.view.get_model()
260 _store.clear();
261 self.rows = []
262 _row = ObserverRow()
263 self.activeiter = _store.append(None, [_row] )
264 self.rows.append(_row)
265
266 def plot(self,x=None,y=None):
267 """create a plot from two/more columns in the ObserverTable"""
268 import matplotlib
269 matplotlib.use('module://backend_gtk3',False)
270 import pylab
271 pylab.ioff()
272
273 # nothing provided: use the first and second columns
274 if x is None or y is None:
275 if len(self.cols)<2:
276 raise Exception("Not enough columns to plot (need 2+)")
277 if x is None:
278 x=self.cols[0]
279 if y is None:
280 y=[self.cols[1]]
281
282 ##### CELSIUS TEMPERATURE WORKAROUND
283 size = len(CelsiusUnits.get_celsius_sign())
284 xtit = x.title.find(CelsiusUnits.get_celsius_sign())
285 if xtit != -1:
286 x.title = x.title[:xtit] + "K" + x.title[xtit + size:]
287 for yy in y:
288 ytit = yy.title.find(CelsiusUnits.get_celsius_sign())
289 if ytit != -1:
290 yy.title = yy.title[:ytit] + "K" + yy.title[ytit + size:]
291 ##### CELSIUS TEMPERATURE WORKAROUND
292
293 # if column indices are provided instead of columns, convert them
294 if x.__class__ is int and x>=0 and x<len(self.cols):
295 x=self.cols[x]
296 if y.__class__ is int and y>=0 and y<len(self.cols):
297 y=[self.cols[y]]
298
299 start = None
300 _p = self.browser.prefs
301 _ignore = _p.getBoolPref("PlotDialog", "ignore_error_points", True)
302 r = {}
303 # FIXME this is not nicely written; we need to collapse the follow if/else
304 # cases into a single bit of code.
305 if _ignore == False:
306 # get all rows, including ones that didn't solve properly
307 for i in range(len(self.rows)):
308 try:
309 r = self.rows[i].get_values(self)
310 # flag if any row are empty -- no data? FIXME why would that happen?
311 flag = True
312 for j in y:
313 if r[j.index]=="":
314 flag = False
315 break
316 if r[x.index]!="" and flag==True:
317 if start == None:
318 start = i
319 break
320 except:
321 continue
322 if start == None:
323 self.browser.reporter.reportError("Can't plot, could not get enough points.")
324 return
325 A = pylab.zeros((len(self.rows)-1-start,len(y)+1),'f')
326 i = 0
327 j = start
328 while j <len(self.rows)-1:
329 r = self.rows[j].get_values(self)
330 A[i,0]=r[x.index]
331 for k in range(len(y)):
332 A[i,k+1]=r[y[k].index]
333 j+=1
334 i+=1
335 else:
336 # ignore error points: get just the non-error rows
337 j = 0
338 k = 0
339 l = 0
340 # count the error-free rows (FIXME: why aren't we just checking the 'tainted' property??)
341 for i in range(len(self.rows)-1):
342 if self.rows[i].tainted is False:
343 try:
344 r = self.rows[i].get_values(self)
345 flag = True
346 for l in y:
347 if r[l.index]=="":
348 flag = False
349 break
350 if r[x.index]!="" and flag == True:
351 if start == None:
352 start = i
353 j=0
354 j+=1
355 except:
356 continue
357 if start == None:
358 self.browser.reporter.reportError("Can't plot, could not get enough points.")
359 return
360 A = pylab.zeros((j,len(y)+1),'f')
361 while start<len(self.rows)-1:
362 if self.rows[start].tainted is True:
363 start+=1
364 continue
365 r = self.rows[start].get_values(self)
366 A[k,0]=r[x.index]
367 for j in range(len(y)):
368 A[k,j+1]=r[y[j].index]
369 k+=1
370 start+=1
371
372 fig = pylab.figure()
373
374 if len(y) == 2:
375 # two y vectors: use two different y axes on one plot
376 ax1 = pylab.subplot(111)
377 # TODO: second y axis label gets cut off?
378 #pylab.axis('auto')
379 ax1.set_xlabel(x.title)
380 ax1.set_ylabel(y[0].title,labelpad=20)
381 l1 = ax1.plot(A[:,0],A[:,1],'-bo',label=y[0].title)
382 ax2 = ax1.twinx()
383 l2 = ax2.plot(A[:,0],A[:,2],'-ro',label=y[1].title)
384 ax2.set_ylabel(y[1].title,labelpad=20)
385 ax2.yaxis.tick_right()
386 l = l1+l2
387 labels = [i.get_label() for i in l]
388 leg = ax1.legend(l,labels,loc='upper left')
389 leg.get_frame().set_alpha(0.3)
390 leg.draggable()
391 else :
392 color_cycle = ['b','r','g','y']
393
394 sharex = None
395 j = 0.83/len(y)
396 for i in range(len(y)):
397 if i == 0:
398 ax = pylab.subplot(len(y),1,i+1)
399 sharex = ax
400 else:
401 ax = pylab.subplot(len(y),1,i+1,sharex=sharex)
402 #ax[i] = fig.add_axes([0.27, 0.08+(i*(j+0.02)), 0.65, j-0.01], **axprops)
403 pylab.plot(A[:,0],A[:,i+1],'-'+color_cycle[i%4]+'o',label=y[i].title)
404
405 # put the x-axis label only on the last plot
406 if i+1 != len(y):
407 pylab.setp(ax.get_xticklabels(),visible=False)
408 else:
409 ax.set_xlabel(x.title)
410
411 # only use a y-axis label if it's a single plot, else put legend on each plot
412 if len(y)==1:
413 pylab.ylabel(y[i].title)
414 else:
415 leg = pylab.legend(loc='upper left')
416 leg.get_frame().set_alpha(0.3)
417
418 # FIXME why can't I drag the legend?
419
420 pylab.ion()
421 pylab.show()
422
423 def on_plot_clicked(self,*args):
424
425 # Disabled plotting for now.
426 #_d = Gtk.MessageDialog(None,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,Gtk.MessageType.ERROR,Gtk.ButtonsType.CLOSE,"Plotting functions are not available unless you have 'matplotlib' installed.\n\nSee http://matplotlib.sf.net/\n\nFailed to load matplotlib" )
427 # _d.run()
428 # _d.destroy()
429 # return
430 try:
431 if len(self.cols)<2:
432 raise Exception("Not enough columns to plot (need 2+)")
433 _plotwin = PlotDialog(self.browser, self)
434 _plot = _plotwin.run()
435 if _plot:
436 self.plot(x=_plotwin.xcol, y=_plotwin.ycol)
437 except Exception,e:
438 self.browser.reporter.reportError(str(e))
439
440 def do_add_row(self,values=None):
441 _store = self.view.get_model()
442 if self.alive:
443 _row = ObserverRow()
444 self.rows.append(_row)
445 if self.activeiter is not None:
446 _oldrow = _store.get_value(self.activeiter, 0)
447 _oldrow.make_static(self, values)
448 self.activeiter = _store.append(None,[_row])
449 _path = _store.get_path(self.activeiter)
450 _oldpath,_oldcol = self.view.get_cursor()
451 self.view.set_cursor(_path, _oldcol)
452 else:
453 _row = ObserverRow(values=values,active=False,tainted=False)
454 self.rows.append(_row)
455 _store.append(None,[_row])
456 #self.browser.reporter.reportNote("Added data row")
457
458 def on_view_cell_edited(self, renderer, path, newtext, col):
459 # we can assume it's always the self.activeiter that is edited...
460 ##### CELSIUS TEMPERATURE WORKAROUND
461 newtext = CelsiusUnits.convert_edit(col.instance, newtext, False)
462 ##### CELSIUS TEMPERATURE WORKAROUND
463 if col.instance.isFixed():
464 val = float(newtext) * col.units.getConversion()
465 col.instance.setRealValue( val )
466 self.browser.reporter.reportNote("Updated value to %f" % float(newtext))
467 else:
468 self.browser.reporter.reportError("Can't set a FREE variable from the Observer")
469 return
470 self.browser.do_solve_if_auto()
471
472 def sync(self):
473 self.view.queue_draw()
474 #self.browser.reporter.reportNote("SYNC performed")
475
476 def add_instance(self,instance):
477 _col = ObserverColumn(instance,self.colindex,browser=self.browser)
478
479 # the loop is to ensure that we dont add multiple columns for the same variable
480 for _cols in self.cols:
481 if self.cols[_cols].title == _col.title:
482 del(_col)
483 return
484 self.cols[self.colindex] = _col
485 self.colindex = self.colindex + 1
486
487 # create a new column
488 _renderer = Gtk.CellRendererText()
489 _renderer.connect('edited',self.on_view_cell_edited, _col)
490 _tvcol = ClickableTreeColumn(_col.title)
491 _tvcol.pack_start(_renderer,False)
492 self.tvcols[self.colindex-1] = _tvcol
493 self.renderers[self.colindex-1] = _renderer
494 _tvcol.set_cell_data_func(_renderer, _col.cellvalue)
495 self.view.append_column(_tvcol);
496 _tvcol.do_connect()
497 #self.browser.reporter.reportError("cols = "+str(self.cols))
498
499 def copy_to_clipboard(self,clip):
500 _s = []
501 _s.append('\t'.join([_v.title for _k,_v in self.cols.iteritems()]))
502 #_cf = [_v.units.getConversion() for _k,_v in self.cols.iteritems()]
503 print "COPYING %d ROWS" % len(self.rows)
504 #print "CONVERSIONS:",_cf
505 for _r in self.rows:
506 _s.append("\t".join(["%s" % _v for _k, _v in _r.get_values(self).iteritems()]))
507
508 clip.set_text('\n'.join(_s),-1)
509
510 self.browser.reporter.reportNote("Observer '%s' data copied to clipboard" % self.name)
511
512 def on_observerview_query_tooltip(self, widget, _x, _y, keyboard_mode, _tooltip):
513 _store = self.view.get_model()
514 _pthinfo = self.view.get_path_at_pos(_x, _y)
515
516 if _pthinfo is None:
517 return False
518 _path, _col, _cellx, _celly = _pthinfo
519 _temp, = _path
520 if(_temp==0):
521 return False
522 _path = _temp-1,
523
524 # the folowing is to ensure that the tooltip
525 # gets refreshed for each row
526 if self.old_path == _path:
527 _iter = _store.get_iter(_path)
528 _rowobject = _store.get_value(_iter,0)
529 if _rowobject.error_msg is None:
530 return False
531 _tooltip.set_text(_rowobject.error_msg)
532 return True
533 else:
534 self.old_path = _path
535 return False
536
537 def set_all_menu_items_sensitive(self):
538 self.unitsmenuitem.set_sensitive(True)
539 self.studycolumnmenuitem.set_sensitive(True)
540 self.plotmenuitem.set_sensitive(True)
541 self.deletecolumnmenuitem.set_sensitive(True)
542
543 def on_treeview_event(self,widget,event):
544
545 _path = None
546 _delete_row = False
547 _contextmenu = False
548
549 _sel = self.view.get_selection()
550 _model, _rowlist = _sel.get_selected_rows()
551 if event.type==Gdk.EventType.KEY_PRESS:
552 _keyval = Gdk.keyval_name(event.keyval)
553 _path, _col = self.view.get_cursor()
554 if _path is not None:
555 if _keyval=='Menu':
556 self.set_all_menu_items_sensitive()
557 _contextmenu = True
558 _button = 3
559 elif _keyval=='Delete' or _keyval=='BackSpace':
560 _delete_row = True
561
562 elif event.type==Gdk.EventType.BUTTON_PRESS:
563 _x = int(event.x)
564 _y = int(event.y)
565 _button = event.button
566 _pthinfo = self.view.get_path_at_pos(_x, _y)
567 if _pthinfo is not None:
568 _path, _col, _cellx, _celly = _pthinfo
569 if event.button == 3:
570 self.set_all_menu_items_sensitive()
571 _contextmenu = True
572
573 if not (_contextmenu or _delete_row):
574 #print "NOT DOING ANYTHING ABOUT %s" % Gdk.keyval_name(event.keyval)
575 return
576
577 if len(_rowlist)>1:
578 self.unitsmenuitem.set_sensitive(False)
579 self.studycolumnmenuitem.set_sensitive(False)
580 self.plotmenuitem.set_sensitive(False)
581 self.deletecolumnmenuitem.set_sensitive(False)
582 if _delete_row:
583 self.on_delete_row()
584 return True
585 self.treecontext.popup( None, None, None, _button, event.time)
586 return
587
588 self.view.grab_focus()
589 self.view.set_cursor( _path, _col, 0)
590
591 self.current_path = _path
592 self.current_col = _col
593 self.current_instance = None
594 if _delete_row:
595 self.on_delete_row()
596 elif self.alive is False:
597 self.unitsmenuitem.set_sensitive(False)
598 self.studycolumnmenuitem.set_sensitive(False)
599 self.treecontext.popup( None, None, None, _button, event.time)
600 else:
601 # Since we have the instance data in self.cols and treeview points us to the
602 # ClickableTreeColumn, we need to match the two.
603 for _cols in self.cols:
604 if self.cols[_cols].title == self.current_col.title:
605 self.current_instance = self.cols[_cols].instance
606 self.current_col_key = _cols
607 break
608 if self.current_instance is None:
609 return 0
610 if self.current_instance.isFixed() == False:
611 self.studycolumnmenuitem.set_sensitive(False)
612 self.treecontext.popup(None, None,lambda _menu,data: (event.get_root_coords()[0],event.get_root_coords()[1], True), None,_button, event.time)
613 return 1
614
615 def on_study_column_activate(self, *args):
616 if self.current_instance is not None:
617 _dia = StudyWin(self.browser,self.current_instance)
618 _dia.run()
619
620 def on_delete_row(self, *args):
621 # We need to remove the row from two places,
622 # the treestore, and our own list of ObserverRows
623
624 _sel = self.view.get_selection()
625 _store, _rowlist = _sel.get_selected_rows()
626 i = 0
627 (x,) = _rowlist[0]
628 for _path in _rowlist:
629 if len(self.rows) == x+1:
630 self.browser.reporter.reportWarning("Can't delete the active row")
631 return
632 self.rows.pop(x)
633 _store.remove(_store.get_iter((x,)))
634
635 # now that we have deleted the row, it is
636 # time to move the cursor to the next available element
637
638 self.view.grab_focus()
639 self.view.set_cursor((x,), self.current_col, 0)
640
641 def taint_row(self, msg, _rowobject = None):
642 # to set the solver error message as the row's tooltip
643 _store = self.view.get_model()
644 if _rowobject is None:
645 _rowobject = _store.get_value(self.activeiter,0)
646 _rowobject.error_msg = msg
647 _rowobject.tainted = True
648
649 def on_delete_column(self, *args):
650 # To delete columns
651 self.cols.pop(self.current_col_key)
652 self.view.remove_column(self.current_col)
653
654 def on_plotmenuitem_activate(self, *args):
655 # To preselect the column as y axis
656 # Disabled plotting for now.
657 _d = Gtk.MessageDialog(None,Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,Gtk.MessageType.ERROR,Gtk.ButtonsType.CLOSE,"Plotting functions are not available unless you have 'matplotlib' installed.\n\nSee http://matplotlib.sf.net/\n\nFailed to load matplotlib" )
658 _d.run()
659 _d.destroy()
660 return
661 try:
662 if len(self.cols)<2:
663 raise Exception("Not enough columns to plot (need 2+)")
664 _plotwin = PlotDialog(self.browser, self)
665 _plotwin.select_ycol(self.cols[self.current_col_key], self)
666 _plot = _plotwin.run()
667 if _plot:
668 self.plot(x=_plotwin.xcol, y=_plotwin.ycol)
669 except Exception,e:
670 self.browser.reporter.reportError(str(e))
671
672 def on_close_observer_clicked(self, *args):
673 # First, the dialog box is brought up, and if the user clicks on yes,
674 # the observer tab is closed. Need to make sure that this instance is
675 # removed from the list of observers maintained in the browser
676
677 _alertwin = CloseDialog(self.browser)
678 destroy = _alertwin.run()
679 if destroy:
680 self.view.destroy()
681 if self.browser.currentobservertab == self.browser.currentpage:
682 self.browser.currentobservertab = None
683 self.browser.maintabs.remove_page(self.browser.currentpage)
684 self.rows = []
685 self.browser.observers.remove(self)
686
687 def on_units_activate(self, *args):
688 if self.current_instance is not None:
689 T = self.current_instance.getType()
690 _un = UnitsDialog(self.browser,T)
691 _un.run()
692
693 def units_refresh(self, instance_type):
694 for _col in self.cols.values():
695 _units = None
696 _units = instance_type.getPreferredUnits()
697 if _units is None:
698 _units = instance_type.getDimensions().getDefaultUnits()
699 _uname = str(_units.getName())
700
701 _col_type = _col.instance.getType()
702 _col_units = _col_type.getPreferredUnits()
703 if _col_units is None:
704 _col_units = _col_type.getDimensions().getDefaultUnits()
705 _col_uname = str(_col_units.getName())
706
707 if _col_uname == _uname:
708 if self.browser == None:
709 name = "UNNAMED"
710 else:
711 name = self.browser.sim.getInstanceName(_col.instance)
712
713 _uname = str(_units.getName())
714 ##### CELSIUS TEMPERATURE WORKAROUND
715 if _col.instance.getType().isRefinedReal() and str(_col.instance.getType().getDimensions()) == 'TMP':
716 units = Preferences().getPreferredUnitsOrigin(str(_col.instance.getType().getName()))
717 if units == CelsiusUnits.get_celsius_sign():
718 _uname = CelsiusUnits.get_celsius_sign()
719 ##### CELSIUS TEMPERATURE WORKAROUND
720 if len(_uname) or _uname.find("/")!=-1:
721 _uname = "["+_uname+"]"
722
723 if _uname == "":
724 _title = "%s" % (name)
725 else:
726 _title = "%s / %s" % (name, _uname)
727 for _tvcol in self.view.get_columns():
728 if _tvcol.title == _col.title:
729 _tvcol.label.set_text(str(_title))
730 _tvcol.title = _title
731 _tvcol.set_title(_title)
732 _col.title = _title
733 _col.units = _units
734 _col.uname = _uname
735 _col.name = name
736 def set_dead(self):
737 if self.alive == False and self.reloaded == True:
738 for i in range(self.colindex):
739 col = self.cols[i]
740 renderer = self.renderers[i]
741 tvcol = self.tvcols[i]
742 tvcol.set_cell_data_func(renderer, None)
743 tvcol.set_cell_data_func(renderer, col.cellvalue, True)
744 self.alive = False
745 self.addbutton.set_sensitive(False)
746 _selection = self.view.get_selection()
747 _selection.unselect_all()
748 _store = self.view.get_model()
749 if self.activeiter is not None:
750 _rowobject = _store.get_value(self.activeiter,0)
751 _rowobject.active = False
752 _rowobject.dead = True
753 self.activeiter = None
754
755 class CloseDialog:
756
757 # Just a dialog to confirm that the user REALLY wants to close
758 # the observer
759
760 def __init__(self, browser):
761 browser.builder.add_objects_from_file(browser.glade_file, ["closeobserverdialog"])
762 self.alertwin = browser.builder.get_object("closeobserverdialog")
763 browser.builder.connect_signals(self)
764
765 def on_closeobserverdialog_close(self,*args):
766 self.alertwin.response(Gtk.ResponseType.CLOSE)
767
768 def run(self):
769 _continue = True
770 while _continue:
771 _res = self.alertwin.run()
772 if _res == Gtk.ResponseType.YES:
773 self.alertwin.destroy()
774 return True
775 else:
776 self.alertwin.destroy()
777 return False
778
779 class PlotDialog:
780
781 # a dialog where the user can select which columns to plot
782
783 def __init__(self, browser, tab):
784 self.browser = browser
785 self.browser.builder.add_objects_from_file(browser.glade_file, ["plotdialog"])
786 self.plotwin = self.browser.builder.get_object("plotdialog")
787 self.plotwin.set_transient_for(browser.window)
788 self.plotbutton = self.browser.builder.get_object("plotbutton")
789 self.xview = self.browser.builder.get_object("treeview1")
790 self.yview = self.browser.builder.get_object("treeview2")
791 self.yview.get_selection().set_mode(Gtk.SelectionMode.MULTIPLE)
792 self.ignorepoints = self.browser.builder.get_object("ignorepoints")
793
794 _p = self.browser.prefs
795 _ignore = _p.getBoolPref("PlotDialog", "ignore_error_points", True)
796 self.ignorepoints.set_active(_ignore)
797
798 _xstore = Gtk.TreeStore(object)
799 self.xview.set_model(_xstore)
800 _xrenderer = Gtk.CellRendererText()
801 _xtvcol = Gtk.TreeViewColumn("X axis")
802 _xtvcol.pack_start(_xrenderer,False)
803 _xtvcol.set_cell_data_func(_xrenderer, self.varlist)
804 self.xview.append_column(_xtvcol)
805
806 _ystore = Gtk.TreeStore(object)
807 self.yview.set_model(_ystore)
808 _yrenderer = Gtk.CellRendererText()
809 _ytvcol = Gtk.TreeViewColumn("Y axis")
810 _ytvcol.pack_start(_yrenderer,False)
811 _ytvcol.set_cell_data_func(_yrenderer, self.varlist)
812 self.yview.append_column(_ytvcol)
813
814 self.xcol = None
815 self.ycol = None
816 for _cols in tab.cols:
817 _xtemp = _xstore.append(None, [tab.cols[_cols]])
818 _ytemp = _ystore.append(None, [tab.cols[_cols]])
819 if self.xcol is None:
820 self.xcol = tab.cols[_cols]
821 _xiter = _xtemp
822 continue
823 if self.ycol is None:
824 self.ycol = tab.cols[_cols]
825 _yiter = _ytemp
826 self.plotbutton.set_sensitive(True)
827
828 _selx = self.xview.get_selection()
829 _selx.select_iter(_xiter)
830
831 _sely = self.yview.get_selection()
832 _sely.select_iter(_yiter)
833
834 self.browser.builder.connect_signals(self)
835
836 def on_plotdialog_close(self,*args):
837 self.plotwin.response(Gtk.ResponseType.CANCEL)
838
839 def varlist(self,column,cell,model,iter, dummy):
840 _value = model.get_value(iter,0)
841 cell.set_property('text', _value.title)
842
843 def on_treeview_event(self,widget,event):
844
845 _path = None
846 _col = None
847 self.plotbutton.set_sensitive(False)
848 if event.type==Gdk.EventType.KEY_RELEASE:
849 _keyval = Gdk.keyval_name(event.keyval)
850 if _keyval == "Escape":
851 self.plotwin.response(Gtk.ResponseType.CANCEL)
852 return
853 _path, _tvcol = widget.get_cursor()
854 if _path is None:
855 return
856
857 elif event.type==Gdk.EventType.BUTTON_RELEASE:
858 _x = int(event.x)
859 _y = int(event.y)
860 _button = event.button
861 _pthinfo = widget.get_path_at_pos(_x, _y)
862 if _pthinfo is None:
863 return
864 _path, _tvcol, _cellx, _celly = _pthinfo
865 if _path is None:
866 return
867
868 _sel = widget.get_selection()
869 _view, path_list = _sel.get_selected_rows()
870 if path_list is None:
871 return
872 #_path = _view.get_path(_iter)
873 #_path = _iter[0]
874 #if _path is None:
875 # return
876 widget.grab_focus()
877 _store = widget.get_model()
878 #_selx = self.xview.get_selection()
879 #_sely = self.yview.get_selection()
880
881 #widget.set_cursor( _path, None, 0)
882 flag=True
883 for _path in path_list:
884 if _path is None:
885 return
886 _iter = _store.get_iter(_path)
887 _col = _store.get_value(_iter,0)
888 if widget is self.xview:
889 self.xcol = _col
890 #_selx.select_iter(_iter)
891 break
892 else:
893 if flag==True:
894 self.ycol = []
895 flag=False
896 self.ycol.append(_col)
897 #_sely.select_iter(_iter)
898
899 if self.ycol is not None and type(self.ycol)!=type([]) :
900 self.ycol = [self.ycol]
901
902 flag=True
903 if self.xcol is not None and self.ycol is not None:
904 for yitem in self.ycol:
905 if self.xcol.title == yitem.title:
906 flag=False
907 if widget is self.xview:
908 self.yview.get_selection().unselect_all()
909 self.ycol = None
910 else:
911 self.xview.get_selection().unselect_all()
912 self.xcol = None
913 break
914 if flag==True:
915 self.plotbutton.set_sensitive(True)
916
917 def select_ycol(self, _col, _tab):
918 _ystore = self.yview.get_model()
919 _iter = _ystore.get_iter_first()
920 for i in _tab.cols:
921 _item = _ystore.get_value(_iter, 0)
922 if _item.title == _col.title:
923 self.ycol = _col
924 _sel = self.yview.get_selection()
925 _sel.unselect_all()
926 _sel.select_iter(_iter)
927 self.xcol = None
928 self.xview.get_selection().unselect_all()
929 self.plotbutton.set_sensitive(False)
930 _iter = _ystore.iter_next(_iter)
931
932 def run(self):
933 _continue = True
934 while _continue:
935 _res = self.plotwin.run()
936 if _res == Gtk.ResponseType.YES:
937 _p = self.browser.prefs
938 _p.setBoolPref("PlotDialog", "ignore_error_points", self.ignorepoints.get_active())
939 self.plotwin.destroy()
940 if self.ycol is not None and type(self.ycol)!=type([]) :
941 self.ycol = [self.ycol]
942 return True
943 else:
944 self.plotwin.destroy()
945 return False

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22