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

Contents of /trunk/pygtk/modelview.py

Parent Directory Parent Directory | Revision Log Revision Log


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

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