/[ascend]/trunk/pygtk/modelview.py
ViewVC logotype

Contents of /trunk/pygtk/modelview.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2684 - (show annotations) (download) (as text)
Tue Feb 26 01:56:46 2013 UTC (10 years, 7 months ago) by sreenatha
File MIME type: text/x-python
File size: 17196 byte(s)
Merging GSOC2012 contributions from Sreenatha, after some modifications made to fix minor bugs -- jpye.
1 import gtk
2 import pango
3 import ascpy
4
5 from varentry import *
6 from properties import *
7 from unitsdialog import *
8 from study import *
9
10 BROWSER_FIXED_COLOR = "#008800"
11 BROWSER_FREE_COLOR = "#000088"
12 BROWSER_SETTING_COLOR = "#4444AA"
13
14 BROWSER_INCLUDED_COLOR = "black"
15 BROWSER_UNINCLUDED_COLOR = "#888888"
16
17 class ModelView:
18 def __init__(self,browser,builder):
19 self.browser = browser # the parent object: the entire ASCEND browser
20
21 self.builder = builder
22 self.notes = browser.library.getAnnotationDatabase()
23
24 self.modelview = builder.get_object("browserview")
25
26 # name, type, value, foreground, weight, editable, status-icon
27 columns = [str,str,str,str,int,bool,gtk.gdk.Pixbuf]
28
29 self.otank = {}
30
31 # name, type, value, foreground, weight, editable, status-icon
32 columns = [str,str,str,str,int,bool,gtk.gdk.Pixbuf]
33 self.modelstore = gtk.TreeStore(*columns)
34 titles = ["Name","Type","Value"];
35 self.modelview.set_model(self.modelstore)
36 self.tvcolumns = [ gtk.TreeViewColumn() for _type in columns[:len(titles)] ]
37
38 self.modelview.connect("row-expanded", self.row_expanded )
39 self.modelview.connect("button-press-event", self.on_treeview_event )
40 self.modelview.connect("key-release-event",self.on_treeview_event )
41
42 # data columns are: name type value colour weight editable
43
44 i = 0
45 for tvcolumn in self.tvcolumns[:len(titles)]:
46 tvcolumn.set_title(titles[i])
47 self.modelview.append_column(tvcolumn)
48
49 if(i==2):
50 # add status icon
51 renderer1 = gtk.CellRendererPixbuf()
52 tvcolumn.pack_start(renderer1, False)
53 tvcolumn.add_attribute(renderer1, 'pixbuf', 6)
54
55 renderer = gtk.CellRendererText()
56 tvcolumn.pack_start(renderer, True)
57 tvcolumn.add_attribute(renderer, 'text', i)
58 tvcolumn.add_attribute(renderer, 'foreground', 3)
59 tvcolumn.add_attribute(renderer, 'weight', 4)
60 if(i==2):
61 tvcolumn.add_attribute(renderer, 'editable', 5)
62 renderer.connect('edited',self.cell_edited_callback)
63 i = i + 1
64
65 #--------------------
66 # get all menu icons and set up the context menu for fixing/freeing vars
67 _imagelist = []
68 for i in range(6):
69 _imagelist.append("image%s" % (i+1))
70 self.browser.builder.add_objects_from_file(self.browser.glade_file, _imagelist)
71 self.browser.builder.add_objects_from_file(self.browser.glade_file, ["treecontext"])
72
73 self.treecontext = self.browser.builder.get_object("treecontext")
74 self.fixmenuitem = self.browser.builder.get_object("fix1")
75 self.freemenuitem = self.browser.builder.get_object("free1")
76 self.propsmenuitem = self.browser.builder.get_object("properties1")
77 self.observemenuitem = self.browser.builder.get_object("observe1")
78 self.studymenuitem = self.browser.builder.get_object("study1")
79 self.unitsmenuitem = self.browser.builder.get_object("units1")
80
81 self.fixmenuitem.connect("activate",self.fix_activate)
82 self.freemenuitem.connect("activate",self.free_activate)
83 self.propsmenuitem.connect("activate",self.props_activate)
84 self.observemenuitem.connect("activate",self.observe_activate)
85 self.studymenuitem.connect("activate", self.study_activate)
86 self.unitsmenuitem.connect("activate",self.units_activate)
87
88 if not self.treecontext:
89 raise RuntimeError("Couldn't create browsercontext")
90
91 def setSimulation(self,sim):
92 # instance hierarchy
93 self.sim = sim
94 self.modelstore.clear()
95 self.otank = {} # map path -> (name,value)
96 self.browser.disable_menu()
97 try:
98 self.make( self.sim.getName(),self.sim.getModel() )
99 self.browser.enable_on_model_tree_build()
100 except Exception,e:
101 self.browser.reporter.reportError("Error building tree: %s" % e);
102 self.browser.maintabs.set_current_page(1);
103
104 def clear(self):
105 self.modelstore.clear()
106 self.otank = {}
107
108 # --------------------------------------------
109 # INSTANCE TREE
110
111 def get_tree_row_data(self,instance): # for instance browser
112 _value = str(instance.getValue())
113 _type = str(instance.getType())
114 _name = str(instance.getName())
115 _fgcolor = BROWSER_INCLUDED_COLOR
116 _fontweight = pango.WEIGHT_NORMAL
117 _editable = False
118 _statusicon = None
119 if instance.getType().isRefinedSolverVar():
120 _editable = True
121 _fontweight = pango.WEIGHT_BOLD
122 if instance.isFixed():
123 _fgcolor = BROWSER_FIXED_COLOR
124 else:
125 _fgcolor = BROWSER_FREE_COLOR
126 _fontweight = pango.WEIGHT_BOLD
127 _status = instance.getStatus();
128 _statusicon = self.browser.statusicons[_status]
129 elif instance.isRelation():
130 _status = instance.getStatus();
131 _statusicon = self.browser.statusicons[_status]
132 if not instance.isIncluded():
133 _fgcolor = BROWSER_UNINCLUDED_COLOR
134 elif instance.isBool() or instance.isReal() or instance.isInt():
135 # TODO can't edit constants that have already been refined
136 _editable = True
137 _fgcolor = BROWSER_SETTING_COLOR
138 _fontweight = pango.WEIGHT_BOLD
139 elif instance.isSymbol() and not instance.isConst():
140 _editable = True
141 _fgcolor = BROWSER_SETTING_COLOR
142 _fontweight = pango.WEIGHT_BOLD
143
144 #if(len(_value) > 80):
145 # _value = _value[:80] + "..."
146
147 return [_name, _type, _value, _fgcolor, _fontweight, _editable, _statusicon]
148
149 def make_row( self, piter, name, value ): # for instance browser
150 assert(value)
151 _piter = self.modelstore.append( piter, self.get_tree_row_data(value) )
152 return _piter
153
154 def refreshtree(self):
155 # @TODO FIXME use a better system than colour literals!
156 for _path in self.otank: # { path : (name,value) }
157 _iter = self.modelstore.get_iter(_path)
158 _name, _instance = self.otank[_path]
159 self.modelstore.set_value(_iter, 2, _instance.getValue())
160 if _instance.getType().isRefinedSolverVar():
161 if _instance.isFixed() and self.modelstore.get_value(_iter,3)==BROWSER_FREE_COLOR:
162 self.modelstore.set_value(_iter,3,BROWSER_FIXED_COLOR)
163 elif not _instance.isFixed() and self.modelstore.get_value(_iter,3)==BROWSER_FIXED_COLOR:
164 self.modelstore.set_value(_iter,3,BROWSER_FREE_COLOR)
165 self.modelstore.set_value(_iter, 6, self.browser.statusicons[_instance.getStatus()])
166 elif _instance.isRelation():
167 self.modelstore.set_value(_iter, 6, self.browser.statusicons[_instance.getStatus()])
168 if _instance.isIncluded():
169 self.modelstore.set_value(_iter,3,BROWSER_INCLUDED_COLOR)
170 else:
171 self.modelstore.set_value(_iter,3,BROWSER_UNINCLUDED_COLOR)
172
173 def get_selected_type(self):
174 return self.get_selected_instance().getType()
175
176 def get_selected_instance(self):
177 model,iter = self.modelview.get_selection().get_selected()
178 if iter is None:
179 return None
180 path = model.get_path(iter)
181 name,instance = self.otank[path]
182 return instance
183
184 def cell_edited_callback(self, renderer, path, newtext, **kwargs):
185 # get back the Instance object we just edited (having to use this seems like a bug)
186 path = tuple( map(int,path.split(":")) )
187
188 if not self.otank.has_key(path):
189 raise RuntimeError("cell_edited_callback: invalid path '%s'" % path)
190 return
191
192 _name, _instance = self.otank[path]
193
194 if _instance.isReal():
195 if _instance.getValue() == newtext:
196 return
197 # only real-valued things can have units
198
199 _e = RealAtomEntry(_instance,newtext);
200 try:
201 _e.checkEntry()
202 _e.setValue()
203 _e.exportPreferredUnits(self.browser.prefs)
204 except InputError, e:
205 self.browser.reporter.reportError(str(e))
206 return;
207
208 else:
209 if _instance.isBool():
210 _lower = newtext.lower();
211 if _lower.startswith("t") or _lower.startswith("y") or _lower.strip()=="1":
212 newtext = 1
213 elif _lower.startswith("f") or _lower.startswith("n") or _lower.strip()=="0":
214 newtext = 0
215 else:
216 self.browser.reporter.reportError("Invalid entry for a boolean variable: '%s'" % newtext)
217 return
218 _val = bool(newtext);
219 if _val == _instance.getValue():
220 self.browser.reporter.reportNote("Boolean atom '%s' was not altered" % _instance.getName())
221 return
222 _instance.setBoolValue(_val)
223
224 elif _instance.isInt():
225 _val = int(newtext)
226 if _val == _instance.getValue():
227 self.browser.reporter.reportNote("Integer atom '%s' was not altered" % _instance.getName())
228 return
229 _instance.setIntValue(_val)
230 elif _instance.isSymbol():
231 _val = str(newtext)
232 if _val == _instance.getValue():
233 self.browser.reporter.reportNote("Symbol atom '%s' was not altered" % _instance.getName())
234 return
235 _instance.setSymbolValue(ascpy.SymChar(_val))
236
237 else:
238 self.browser.reporter.reportError("Attempt to set a non-real, non-boolean, non-integer value!")
239 return
240
241 # now that the variable is set, update the GUI and re-solve if desired
242 _iter = self.modelstore.get_iter(path)
243 self.modelstore.set_value(_iter,2,_instance.getValue())
244
245 if _instance.getType().isRefinedSolverVar():
246 self.modelstore.set_value(_iter,3,BROWSER_FIXED_COLOR) # set the row green as fixed
247
248 self.browser.do_solve_if_auto()
249
250 def make_children(self, value, piter ):
251 assert(value)
252 if value.isCompound():
253 children=value.getChildren();
254 for child in children:
255 try:
256 _name = child.getName();
257 _piter = self.make_row(piter,_name,child)
258 _path = self.modelstore.get_path(_piter)
259 self.otank[_path]=(_name,child)
260 #self.browser.reporter.reportError("2 Added %s at path %s" % (_name,repr(_path)))
261 except Exception,e:
262 self.browser.reporter.reportError("%s: %s" % (_name,e))
263
264 def make(self, name=None, value=None, path=None, depth=1):
265 if path is None:
266 # make root node
267 piter = self.make_row( None, name, value )
268 path = self.modelstore.get_path( piter )
269 self.otank[ path ] = (name, value)
270 #self.browser.reporter.reportError("4 Added %s at path %s" % (name, path))
271 else:
272 name, value = self.otank[ path ]
273
274 assert(value)
275
276 piter = self.modelstore.get_iter( path )
277 if not self.modelstore.iter_has_child( piter ):
278 #self.browser.reporter.reportNote( "name=%s has CHILDREN..." % name )
279 self.make_children(value,piter)
280
281 if depth:
282 for i in range( self.modelstore.iter_n_children( piter ) ):
283 self.make( path = path+(i,), depth = depth - 1 )
284 else:
285 self.modelview.expand_row("0",False)
286
287 def row_expanded( self, modelview, piter, path ):
288 self.make( path = path )
289
290
291 # ------------------------------
292 # CONTEXT MENU
293
294 def on_treeview_event(self,widget,event):
295 _path = None
296 _contextmenu = False
297 if event.type==gtk.gdk.KEY_RELEASE:
298 _keyval = gtk.gdk.keyval_name(event.keyval)
299 _path, _col = self.modelview.get_cursor()
300 if _keyval=='Menu':
301 _contextmenu = True
302 _button = 3
303 elif _keyval == 'F2':
304 print "F2 pressed"
305 self.modelview.set_cursor(_path,self.tvcolumns[2],1)
306
307 return
308 elif event.type==gtk.gdk.BUTTON_PRESS:
309 _x = int(event.x)
310 _y = int(event.y)
311 _button = event.button
312 _pthinfo = self.modelview.get_path_at_pos(_x, _y)
313 if _pthinfo is not None:
314 _path, _col, _cellx, _celly = _pthinfo
315 if event.button == 3:
316 _contextmenu = True
317
318 if _path:
319 _name,_instance = self.otank[_path]
320 # set the statusbar
321 nn = self.notes.getNotes(self.sim.getModel().getType(),ascpy.SymChar("inline"),_name)
322 for n in nn:
323 print "%s: (%s) %s" % (n.getId(),str(n.getLanguage()),n.getText())
324
325 self.builder.get_object("free_variable").set_sensitive(False)
326 self.builder.get_object("fix_variable").set_sensitive(False)
327 self.builder.get_object("propsmenuitem").set_sensitive(False)
328 if _instance.isReal():
329 self.builder.get_object("units").set_sensitive(True)
330 if _instance.getType().isRefinedSolverVar():
331 self.builder.get_object("propsmenuitem").set_sensitive(True)
332 if _instance.isFixed():
333 self.builder.get_object("free_variable").set_sensitive(True)
334 else:
335 self.builder.get_object("fix_variable").set_sensitive(True)
336 elif _instance.isRelation():
337 self.builder.get_object("propsmenuitem").set_sensitive(True)
338
339 if not _contextmenu:
340 #print "NOT DOING ANYTHING ABOUT %s" % gtk.gdk.keyval_name(event.keyval)
341 return
342
343 _canpop = False;
344 # self.browser.reporter.reportError("Right click on %s" % self.otank[_path][0])
345
346 self.unitsmenuitem.set_sensitive(False)
347 self.fixmenuitem.set_sensitive(False)
348 self.freemenuitem.set_sensitive(False)
349 self.observemenuitem.set_sensitive(False)
350 self.studymenuitem.set_sensitive(False)
351 self.propsmenuitem.set_sensitive(False)
352
353 if _instance.isReal():
354 print "CAN POP: real atom"
355 _canpop = True
356 self.unitsmenuitem.set_sensitive(True)
357
358 if _instance.getType().isRefinedSolverVar():
359 _canpop = True
360 self.propsmenuitem.set_sensitive(True)
361 self.observemenuitem.set_sensitive(True)
362 if _instance.isFixed():
363 self.freemenuitem.set_sensitive(True)
364 if len(self.browser.observers) > 0:
365 self.studymenuitem.set_sensitive(True)
366 else:
367 self.fixmenuitem.set_sensitive(True)
368 elif _instance.isRelation():
369 _canpop = True
370 self.propsmenuitem.set_sensitive(True)
371 elif _instance.isModel():
372 # MODEL instances have a special context menu:
373 _menu = self.get_model_context_menu(_instance)
374 self.modelview.grab_focus()
375 self.modelview.set_cursor(_path,_col,0)
376 print "RUNNING POPUP MENU"
377 _menu.popup(None,None,None,_button,event.time)
378 return
379
380 if not _canpop:
381 return
382
383 self.modelview.grab_focus()
384 self.modelview.set_cursor( _path, _col, 0)
385 self.treecontext.popup( None, None, None, _button, event.time)
386 return 1
387
388 def get_model_context_menu(self,instance):
389 menu = gtk.Menu()
390
391 if instance.isPlottable():
392 print "PLOTTABLE"
393 mi = gtk.ImageMenuItem("P_lot",True);
394 img = gtk.Image()
395 img.set_from_file(self.browser.options.assets_dir+'/plot.png')
396 mi.set_image(img)
397 mi.show()
398 mi.connect("activate",self.plot_activate)
399 menu.append(mi);
400 sep = gtk.SeparatorMenuItem(); sep.show()
401 menu.append(sep)
402
403 mi = gtk.ImageMenuItem("Run method...",False)
404 mi.set_sensitive(False)
405 img = gtk.Image()
406 img.set_from_stock(gtk.STOCK_EXECUTE,gtk.ICON_SIZE_MENU)
407 mi.set_image(img)
408 mi.show()
409 menu.append(mi)
410
411 sep = gtk.SeparatorMenuItem(); sep.show()
412 menu.append(sep)
413
414 t = instance.getType()
415 ml = t.getMethods()
416 if len(ml):
417 for m in ml:
418 mi = gtk.MenuItem(m.getName(),False)
419 mi.show()
420 mi.connect("activate",self.run_activate,instance,m)
421 menu.append(mi)
422
423 return menu
424
425 def run_activate(self,widget,instance,method):
426 print "RUNNING %s" % method.getName()
427 try:
428 self.browser.sim.run(method,instance)
429 except Exception,e:
430 self.browser.reporter.reportError(str(e))
431 self.refreshtree()
432
433 def fix_activate(self,widget):
434 _path,_col = self.modelview.get_cursor()
435 _name, _instance = self.otank[_path]
436 self.set_fixed(_instance,True);
437 _instance.setFixed(True)
438 return 1
439
440 def free_activate(self,widget):
441 _path,_col = self.modelview.get_cursor()
442 _instance = self.otank[_path][1]
443 self.set_fixed(_instance,False)
444 return 1
445
446 def plot_activate(self,widget):
447
448 self.browser.reporter.reportNote("plot_activate...");
449 _path,_col = self.modelview.get_cursor()
450 _instance = self.otank[_path][1]
451 if not _instance.isPlottable():
452 self.browser.reporter.reportError("Can't plot instance %s" % _instance.getName().toString())
453 return
454 else:
455 self.browser.reporter.reportNote("Instance %s about to be plotted..." % _instance.getName().toString())
456
457 print("Plotting instance '%s'..." % _instance.getName().toString())
458
459 _plot = _instance.getPlot()
460
461 print "Title: ", _plot.getTitle()
462 _plot.show(True)
463
464 return 1
465
466 def props_activate(self,widget,*args):
467 if not hasattr(self,'sim'):
468 self.browser.reporter.reportError("Can't show properties until a simulation has been created.");
469 return
470 _path,_col = self.modelview.get_cursor()
471 _instance = self.otank[_path][1]
472 if _instance.isRelation():
473 print "Relation '"+_instance.getName().toString()+"':", \
474 _instance.getRelationAsString(self.sim.getModel())
475 _dia = RelPropsWin(self.browser,_instance);
476 _dia.run();
477 elif _instance.getType().isRefinedSolverVar():
478 _dia = VarPropsWin(self.browser,_instance);
479 _dia.run();
480 else:
481 self.browser.reporter.reportWarning("Select a variable or relation first...")
482
483 def observe_activate(self,widget,*args):
484 _path,_col = self.modelview.get_cursor()
485 _instance = self.otank[_path][1]
486 if _instance.getType().isRefinedSolverVar():
487 print "OBSERVING",_instance.getName().toString()
488 self.browser.observe(_instance)
489
490 def on_fix_variable_activate(self,*args):
491 _path,_col = self.modelview.get_cursor()
492 _instance = self.otank[_path][1]
493 self.set_fixed(_instance,True)
494
495 def on_free_variable_activate(self,*args):
496 _path,_col = self.modelview.get_cursor()
497 _instance = self.otank[_path][1]
498 self.set_fixed(_instance,False)
499
500 def set_fixed(self,instance,val):
501 if instance.getType().isRefinedSolverVar():
502 f = instance.isFixed();
503 if (f and not val) or (not f and val):
504 instance.setFixed(val)
505 self.browser.do_solve_if_auto()
506
507
508 def study_activate(self, *args):
509 _path,_col = self.modelview.get_cursor()
510 _instance = self.otank[_path][1]
511 self.browser.observe(_instance)
512 _dia = StudyWin(self.browser,_instance);
513 _dia.run()
514
515 def units_activate(self,*args):
516 T = self.get_selected_type()
517 try:
518 _un = UnitsDialog(self.browser,T)
519 _un.run()
520 except:
521 self.browser.reporter.reportError("Unable to display units dialog.")
522

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