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

Contents of /trunk/pygtk/modelview.py

Parent Directory Parent Directory | Revision Log Revision Log


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

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