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

Contents of /trunk/pygtk/gtkbrowser.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 227 - (show annotations) (download) (as text)
Sat Jan 28 04:44:15 2006 UTC (14 years, 8 months ago) by johnpye
Original Path: trunk/pygtk/interface/gtkbrowser.py
File MIME type: text/x-python
File size: 33711 byte(s)
Added grouping of solver parameters into pages
1 #!/usr/bin/env python
2
3 import pygtk
4 pygtk.require('2.0')
5 import gtk
6 import gtk.glade
7 import pango
8 import re
9 import preferences
10 import urlparse
11 import optparse
12
13 import sys, dl
14 # This sets the flags for dlopen used by python so that the symbols in the
15 # ascend library are made available to libraries dlopened within ASCEND:
16 sys.setdlopenflags(dl.RTLD_GLOBAL|dl.RTLD_NOW)
17 import ascend
18
19 # This is my first ever GUI code so please be nice :)
20
21 # The fancy tree-view gizmo is the GtkTreeView object. See the article
22 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/300304
23 # for the original source code on which my implementation was based.
24
25 GLADE_FILE = "/home/john/src/ascend/trunk/pygtk/interface/ascend.glade"
26
27 CHANGED_COLOR = "#FFFF88"
28 SOLVERPARAM_BOOL_TRUE = "Yes"
29 SOLVERPARAM_BOOL_FALSE = "No"
30
31 ESCAPE_KEY = 65307
32
33 #======================================
34 # Browser is the main ASCEND library/model browser window
35
36 class Browser:
37
38 # ---------------------------------
39 # SETUP
40
41 def __init__(self):
42 #--------
43 # load the file referenced in the command line, if any
44
45 parser = optparse.OptionParser(usage="%prog [[-m typename] file]", version="gtkbrowser $rev$" )
46 # add options here if we want
47
48 parser.add_option("-m", "--model"
49 ,action="store", type="string", dest="model"
50 ,help="specify the model to instantiate upon loading modules")
51 (options, args) = parser.parse_args()
52
53 #print "OPTIONS_______________:",options
54
55 #--------
56 # load up the preferences ini file
57
58 self.prefs = preferences.Preferences()
59
60 #--------
61 # initialise ASCEND
62
63 self.library = ascend.Library()
64
65 self.sim = None
66
67 #-------------------
68 # Set up the window and main widget actions
69
70 glade = gtk.glade.XML(GLADE_FILE,"browserwin")
71
72 self.window = glade.get_widget("browserwin")
73
74 if not self.window:
75 raise RuntimeError("Couldn't load window from glade file")
76
77 _display = self.window.get_screen().get_display().get_name();
78 _geom=self.prefs.getGeometrySizePosition(_display,"browserwin")
79 if _geom:
80 self.window.resize(_geom[0],_geom[1]);
81 self.window.move(_geom[2],_geom[3]);
82
83 self.window.connect("delete_event", self.delete_event)
84
85 self.browserpaned=glade.get_widget("browserpaned");
86 _geom2=self.prefs.getGeometryValue(_display,"browserpaned");
87 if _geom2:
88 self.browserpaned.set_position(_geom2);
89
90 self.openbutton=glade.get_widget("openbutton")
91 self.openbutton.connect("clicked",self.open_click)
92
93 self.reloadbutton=glade.get_widget("reloadbutton")
94 self.reloadbutton.connect("clicked",self.reload_click)
95
96 self.solvebutton=glade.get_widget("solvebutton")
97 self.solvebutton.connect("clicked",self.solve_click)
98
99 self.checkbutton=glade.get_widget("checkbutton")
100 self.checkbutton.connect("clicked",self.check_click)
101
102 self.autotoggle=glade.get_widget("autotoggle")
103 self.autotoggle.connect("toggled",self.auto_toggle)
104
105 self.methodrunbutton=glade.get_widget("methodrunbutton")
106 self.methodrunbutton.connect("clicked",self.methodrun_click)
107
108 self.methodsel=glade.get_widget("methodsel")
109
110 self.maintabs = glade.get_widget("maintabs")
111
112 self.statusbar = glade.get_widget("statusbar")
113
114 self.menu = glade.get_widget("browsermenu")
115 glade.signal_autoconnect(self)
116
117 #-------------------
118 # waitwin
119
120 self.waitwin = gtk.gdk.Window(self.window.window,
121 gtk.gdk.screen_width(),
122 gtk.gdk.screen_height(),
123 gtk.gdk.WINDOW_CHILD,
124 0,
125 gtk.gdk.INPUT_ONLY)
126
127 _cursor = gtk.gdk.Cursor(gtk.gdk.WATCH)
128 self.waitwin.set_cursor(_cursor)
129
130 #-------------------
131 # pixbufs to be used in the error listing
132
133 self.iconok = self.window.render_icon(gtk.STOCK_YES,gtk.ICON_SIZE_MENU)
134 self.iconinfo = self.window.render_icon(gtk.STOCK_DIALOG_INFO,gtk.ICON_SIZE_MENU)
135 self.iconwarning = self.window.render_icon(gtk.STOCK_DIALOG_WARNING,gtk.ICON_SIZE_MENU)
136 self.iconerror = self.window.render_icon(gtk.STOCK_DIALOG_ERROR,gtk.ICON_SIZE_MENU)
137
138 #--------------------
139 # set up the context menu for fixing/freeing vars
140
141 # TODO import this menu from Glade (this code is a PITA)
142
143 self.treecontext = gtk.Menu();
144 self.fixmenuitem = gtk.ImageMenuItem("_Fix",True);
145 _img = gtk.Image()
146 _img.set_from_file('icons/locked.png')
147 self.fixmenuitem.set_image(_img)
148
149 self.freemenuitem = gtk.ImageMenuItem("F_ree",True);
150 _img = gtk.Image()
151 _img.set_from_file('icons/unlocked.png')
152 self.freemenuitem.set_image(_img)
153
154 self.plotmenuitem = gtk.ImageMenuItem("P_lot",True);
155 _img = gtk.Image()
156 _img.set_from_file('icons/plot.png')
157 self.plotmenuitem.set_image(_img)
158
159 self.propsmenuitem = gtk.ImageMenuItem("_Properties",True);
160 _img = gtk.Image()
161 _img.set_from_file('icons/properties.png')
162 self.propsmenuitem.set_image(_img)
163
164 self.fixmenuitem.show(); self.fixmenuitem.set_sensitive(False)
165 self.freemenuitem.show(); self.freemenuitem.set_sensitive(False)
166 self.plotmenuitem.show(); self.plotmenuitem.set_sensitive(False)
167 self.propsmenuitem.show()
168 self.treecontext.append(self.fixmenuitem);
169 self.treecontext.append(self.freemenuitem);
170 _sep = gtk.SeparatorMenuItem(); _sep.show()
171 self.treecontext.append(_sep);
172 self.treecontext.append(self.plotmenuitem);
173 _sep = gtk.SeparatorMenuItem(); _sep.show()
174 self.treecontext.append(_sep);
175 self.treecontext.append(self.propsmenuitem);
176 self.fixmenuitem.connect("activate",self.fix_activate)
177 self.freemenuitem.connect("activate",self.free_activate)
178 self.plotmenuitem.connect("activate",self.plot_activate)
179 self.propsmenuitem.connect("activate",self.props_activate)
180 if not self.treecontext:
181 raise RuntimeError("Couldn't create browsercontext")
182 #--------------------
183 # set up the error view
184
185 self.errorview = glade.get_widget("errorview")
186 errstorecolstypes = [gtk.gdk.Pixbuf,str,str,str,int]
187 self.errorstore = gtk.TreeStore(*errstorecolstypes)
188 errtitles = ["","Location","Message"];
189 self.errorview.set_model(self.errorstore)
190 self.errcols = [ gtk.TreeViewColumn() for _type in errstorecolstypes]
191
192 i = 0
193 for tvcolumn in self.errcols[:len(errtitles)]:
194 tvcolumn.set_title(errtitles[i])
195 self.errorview.append_column(tvcolumn)
196
197 if i>0:
198 _renderer = gtk.CellRendererText()
199 tvcolumn.pack_start(_renderer, True)
200 tvcolumn.add_attribute(_renderer, 'text', i)
201 if(i==2):
202 tvcolumn.add_attribute(_renderer, 'foreground', 3)
203 tvcolumn.add_attribute(_renderer, 'weight', 4)
204 else:
205 _renderer1 = gtk.CellRendererPixbuf()
206 tvcolumn.pack_start(_renderer1, False)
207 tvcolumn.add_attribute(_renderer1, 'pixbuf', int(0))
208
209 i = i + 1
210
211
212 #--------------------
213 # set up the error reporter callback
214 self.reporter = ascend.getReporter()
215 self.reporter.setPythonErrorCallback(self.error_callback)
216
217 #-------------------
218 # set up the module view
219
220 self.modtank = {}
221 self.moduleview = glade.get_widget("moduleview")
222 modulestorecoltypes = [str, str]
223 self.modulestore = gtk.TreeStore(*modulestorecoltypes)
224 moduleviewtitles = ["Module name", "Filename"]
225 self.moduleview.set_model(self.modulestore)
226 self.modcols = [ gtk.TreeViewColumn() for _type in modulestorecoltypes]
227 i = 0
228 for modcol in self.modcols[:len(moduleviewtitles)]:
229 modcol.set_title(moduleviewtitles[i])
230 self.moduleview.append_column(modcol)
231 _renderer = gtk.CellRendererText()
232 modcol.pack_start(_renderer, True)
233 modcol.add_attribute(_renderer, 'text', i)
234 i = i + 1
235 self.moduleview.connect("row-activated", self.module_activated )
236
237 #-------------------
238 # RE for units matching
239 self.units_re = re.compile("([-+]?(\d+(\.\d*)?|\d*\.d+)([eE][-+]?\d+)?)\s*(.*)");
240
241 #--------------------
242 # set up the methods combobox
243
244 self.methodstore = gtk.ListStore(str)
245 self.methodsel.set_model(self.methodstore)
246 _methodrenderer = gtk.CellRendererText()
247 self.methodsel.pack_start(_methodrenderer, True)
248 self.methodsel.add_attribute(_methodrenderer, 'text',0)
249
250 #--------
251 # set up the instance browser view
252
253 self.otank = {}
254 self.treeview = glade.get_widget("browserview")
255 columns = [str,str,str,str,int,bool]
256 self.treestore = gtk.TreeStore(*columns)
257 titles = ["Name","Type","Value"];
258 self.treeview.set_model(self.treestore)
259 self.tvcolumns = [ gtk.TreeViewColumn() for _type in columns[:len(titles)] ]
260
261 self.treeview.connect("row-expanded", self.row_expanded )
262 self.treeview.connect("button-press-event", self.tree_click )
263
264 # data columns are: name type value colour weight editable
265
266 i = 0
267 for tvcolumn in self.tvcolumns[:len(titles)]:
268 tvcolumn.set_title(titles[i])
269 self.treeview.append_column(tvcolumn)
270
271 renderer = gtk.CellRendererText()
272 tvcolumn.pack_start(renderer, True)
273 tvcolumn.add_attribute(renderer, 'text', i)
274 tvcolumn.add_attribute(renderer, 'foreground', 3)
275 tvcolumn.add_attribute(renderer, 'weight', 4)
276 if(i==2):
277 tvcolumn.add_attribute(renderer, 'editable', 5)
278 renderer.connect('edited',self.cell_edited_callback)
279 i = i + 1
280
281
282 if(len(args)==1):
283 self.do_open(args[0])
284
285 print "Options: ",options
286
287 if options.model:
288 try:
289 _t =self.library.findType(options.model);
290 self.do_sim(_t);
291 except RuntimeError, e:
292 self.reporter.reportError("Failed to create instance of '%s': %s" %(options.model, str(e)));
293
294
295 def run(self):
296 self.window.show()
297 gtk.main()
298
299 # --------------------------------------------
300 # MAJOR GUI COMMANDS
301
302
303 def do_open(self,filename):
304 # TODO does the user want to lose their work?
305 # TODO do we need to chdir?
306
307 _context = self.statusbar.get_context_id("do_open")
308
309 self.errorstore.clear()
310
311 self.treestore.clear()
312 self.otank = {}
313
314 # self.library.clear()
315
316 self.statusbar.push(_context,"Loading '"+filename+"'")
317 self.library.load(filename)
318 self.statusbar.pop(_context)
319
320 self.filename = filename
321
322 # Load the current list of modules into self.modules
323 self.modtank = {}
324 self.modulestore.clear()
325 modules = self.library.getModules()
326 for m in reversed(modules):
327 _n = str( m.getName() )
328 _f = str( m.getFilename() )
329 #print "ADDING ROW name %s, file = %s" % (_n, _f)
330 _r = self.modulestore.append(None, [ _n, _f ])
331 for t in self.library.getModuleTypes(m):
332 _n = t.getName()
333 #print "ADDING TYPE %s" % _n
334 _piter = self.modulestore.append(_r , [ _n, "" ])
335 _path = self.modulestore.get_path(_piter)
336 self.modtank[_path]=t
337
338 #print "DONE ADDING MODULES"
339
340 self.sim = None;
341 self.maintabs.set_current_page(0);
342
343 # See http://www.daa.com.au/pipermail/pygtk/2005-October/011303.html
344 # for details on how the 'wait cursor' is done.
345 def start_waiting(self, message):
346 self.waitcontext = self.statusbar.get_context_id("waiting")
347 self.statusbar.push(self.waitcontext,message)
348
349 if self.waitwin:
350 self.waitwin.show()
351
352 while gtk.events_pending():
353 gtk.main_iteration()
354
355 def stop_waiting(self):
356 if self.waitwin:
357 self.statusbar.pop(self.waitcontext)
358 self.waitwin.hide()
359
360 def do_sim(self, type_object):
361 self.sim = None;
362 # TODO: clear out old simulation first!
363
364 print "DO_SIM(%s)" % str(type_object.getName())
365 self.start_waiting("Compiling...")
366
367 self.sim = type_object.getSimulation(str(type_object.getName())+"_sim")
368 print "...DONE 'getSimulation'"
369 self.stop_waiting()
370
371 self.start_waiting("Building simulation...")
372 print "BUILDING SIMULATION"
373 self.sim.build()
374 print "DONE BUILDING"
375 self.stop_waiting()
376
377 self.sim.setSolver(ascend.Solver("QRSlv"))
378
379 # empty things out first
380 self.methodstore.clear()
381 self.treestore.clear()
382
383 # methods
384 _methods = self.sim.getType().getMethods()
385 _activemethod = None;
386 for _m in _methods:
387 _i = self.methodstore.append([_m.getName()])
388 if _m.getName()=="default_self":
389 self.methodsel.set_active_iter(_i)
390
391 # instance hierarchy
392 self.otank = {} # map path -> (name,value)
393 self.make( self.sim.getName(),self.sim.getModel() )
394 self.maintabs.set_current_page(1);
395
396 def do_solve(self):
397 if not self.sim:
398 self.reporter.reportError("No model selected yet")
399 return;
400
401 self.start_waiting("Solving...")
402
403 self.sim.solve(ascend.Solver("QRSlv"))
404
405 self.stop_waiting()
406 self.refreshtree()
407
408 def do_check(self):
409 if not self.sim:
410 self.reporter.reportError("No model selected yet")
411
412 self.start_waiting("Checking system...")
413
414 if self.sim.check():
415 self.reporter.reportNote("System check OK")
416
417 self.sim.checkDoF()
418
419 self.stop_waiting()
420
421 self.refreshtree()
422
423 def do_method(self,method):
424 if not self.sim:
425 self.reporter.reportError("No model selected yet")
426
427 self.sim.run(method)
428 self.refreshtree()
429
430 # --------------------------------------------
431 # MODULE LIST
432
433 def module_activated(self, treeview, path, column, *args):
434 modules = self.library.getModules()
435 print "PATH",path
436 if len(path)==1:
437 self.reporter.reportNote("Launching of external editor not yet implemented")
438 elif len(path)==2:
439 if(self.modtank.has_key(path)):
440 _type = self.modtank[path];
441 self.reporter.reportNote("Creating simulation for type %s" % str(_type.getName()) )
442 self.do_sim(_type)
443 else:
444 self.reporter.reportError("Didn't find type corresponding to row")
445
446 # --------------------------------------------
447 # INSTANCE TREE
448
449 def get_tree_row_data(self,instance):
450 _value = str(instance.getValue())
451 _type = str(instance.getType())
452 _name = str(instance.getName())
453 _fgcolor = "black"
454 _fontweight = pango.WEIGHT_NORMAL
455 _editable = False
456 if instance.getType().isRefinedSolverVar():
457 _editable = True
458 if instance.isFixed():
459 _fgcolor = "#008800"
460 _fontweight = pango.WEIGHT_BOLD
461 else:
462 _fgcolor = "#000088"
463 _fontweight = pango.WEIGHT_BOLD
464 elif instance.isBool() or instance.isReal() or instance.isInt():
465 # TODO can't edit constants that have already been refined
466 _editable = True
467
468 #if(len(_value) > 80):
469 # _value = _value[:80] + "..."
470
471 return [_name, _type, _value, _fgcolor, _fontweight, _editable]
472
473 def get_error_row_data(self,sev,filename,line,msg):
474 _sevicon = {
475 0: self.iconok
476 ,1: self.iconinfo
477 ,2: self.iconwarning
478 ,3: self.iconerror
479 ,4: self.iconinfo
480 ,5: self.iconwarning
481 ,6: self.iconerror
482 }[sev]
483
484 _fontweight = pango.WEIGHT_NORMAL
485 if sev==6:
486 _fontweight = pango.WEIGHT_BOLD
487
488 _fgcolor = "black"
489 if sev==4:
490 _fgcolor = "#888800"
491 elif sev==5:
492 _fgcolor = "#884400"
493 elif sev==6:
494 _fgcolor = "#880000"
495 elif sev==0:
496 _fgcolor = "#008800"
497
498 if not filename and not line:
499 _fileline = ""
500 else:
501 if(len(filename) > 25):
502 filename = "..."+filename[-22:]
503 _fileline = filename + ":" + str(line)
504
505 _res = [_sevicon,_fileline,msg.rstrip(),_fgcolor,_fontweight]
506 #print _res
507 return _res
508
509 def make_row( self, piter, name, value ):
510
511 _piter = self.treestore.append( piter, self.get_tree_row_data(value) )
512 return _piter
513
514 def refreshtree(self):
515 # @TODO FIXME use a better system than colour literals!
516 for _path in self.otank: # { path : (name,value) }
517 _iter = self.treestore.get_iter(_path)
518 _name, _instance = self.otank[_path]
519 self.treestore.set_value(_iter, 2, _instance.getValue())
520 if _instance.getType().isRefinedSolverVar():
521 if _instance.isFixed() and self.treestore.get_value(_iter,3)=="#000088":
522 self.treestore.set_value(_iter,3,"#008800")
523 elif not _instance.isFixed() and self.treestore.get_value(_iter,3)=="#008800":
524 self.treestore.set_value(_iter,3,"#000088")
525
526 def cell_edited_callback(self, renderer, path, newtext, **kwargs):
527 # get back the Instance object we just edited (having to use this seems like a bug)
528 path = tuple( map(int,path.split(":")) )
529
530 if not self.otank.has_key(path):
531 raise RuntimeError("cell_edited_callback: invalid path '%s'" % path)
532 return
533
534 _name, _instance = self.otank[path]
535
536 if _instance.isReal():
537 # only real-valued things can have units
538
539 try:
540 # match a float with option text afterwards, optionally separated by whitespace
541 _match = re.match(self.units_re,newtext)
542 if not _match:
543 self.reporter.reportError("Not a valid value-and-optional-units")
544 return
545
546 _val = _match.group(1)
547 _units = _match.group(5)
548 #_val, _units = re.split("[ \t]+",newtext,2);
549 except RuntimeError:
550 self.reporter.reportError("Unable to split value and units")
551 return
552 print "val = ",_val
553 print "units = ",_units
554
555 # parse the units, throw an error if no good
556 try:
557 _val = float(_val)
558 except RuntimeError:
559 self.reporter.reportError("Unable to convert number part '%s' to float" % _val)
560
561 if _units.strip() == "":
562 _u = _instance.getType().getPreferredUnits()
563 if _u == None:
564 _u = _instance.getDimensions().getDefaultUnits()
565 self.reporter.reportNote("Assuming units '%s'" % _u.getName().toString() )
566 else:
567 try:
568 _u = ascend.Units(_units)
569 self.reporter.reportNote("Parsed units %s" % _units)
570 except RuntimeError:
571 self.reporter.reportError("Unrecognisable units '%s'" % _units)
572 return
573
574 if _instance.getDimensions() != _u.getDimensions():
575
576 if _u.getDimensions().isDimensionless():
577 _units = "[dimensionless]"
578
579 _my_dims = _instance.getDimensions().getDefaultUnits()
580 if _instance.getDimensions().isDimensionless():
581 _my_dims = "[dimensionless]"
582
583 self.reporter.reportError("Incompatible units '%s' (must fit with '%s')"
584 % (_units, _my_dims) )
585 return
586
587 if _units.strip() != "" and not _instance.getDimensions().isDimensionless():
588 self.prefs.setPreferredUnits(str(_instance.getType().getName()), _units);
589
590 _conv = float(_u.getConversion())
591 # self.reporter.reportNote("Converting: multiplying '%s %s' by factor %s to get SI units" % (_val, _units, _conv) )
592 _val = _val * _conv;
593
594 self.reporter.reportNote("Setting '%s' to '%f'" % (_name, _val))
595
596 if _instance.getType().isRefinedSolverVar():
597 # set the 'fixed' flag as well
598 _instance.setFixedValue(float(_val))
599 else:
600 _instance.setRealValue(float(_val))
601 else:
602 if _instance.isBool():
603 _lower = newtext.lower();
604 if _lower.startswith("t") or _lower.startswith("y") or _lower.strip()=="1":
605 newtext = 1
606 elif _lower.startswith("f") or _lower.startswith("n") or _lower.strip()=="0":
607 newtext = 0
608 else:
609 self.reporter.reportError("Invalid entry for a boolean variable: '%s'" % newtext)
610 return
611 _val = bool(newtext);
612 if _val == _instance.getValue():
613 self.reporter.reportNote("Boolean atom '%s' was not altered" % _instance.getName())
614 return
615 _instance.setBoolValue(_val)
616
617 elif _instance.isInt():
618 _val = int(newtext)
619 if _val == _instance.getValue():
620 self.reporter.reportNote("Integer atom '%s' was not altered" % _instance.getName())
621 return
622 _instance.setIntValue(_val)
623 else:
624 self.reporter.reportError("Attempt to set a non-real, non-boolean, non-integer value!")
625 return
626
627 # now that the variable is set, update the GUI and re-solve if desired
628 _iter = self.treestore.get_iter(path)
629 self.treestore.set_value(_iter,2,_instance.getValue())
630
631 if _instance.getType().isRefinedSolverVar():
632 self.treestore.set_value(_iter,3,"#008800") # set the row green as fixed
633
634 if self.autotoggle.get_active():
635 self.sim.check()
636 self.do_solve()
637 #self.reporter.reportError("SOLVER completed")
638
639
640 def make_children(self, value, piter ):
641 if value.isCompound():
642 children=value.getChildren();
643 for child in children:
644 _name = child.getName();
645 _piter = self.make_row(piter,_name,child)
646 _path = self.treestore.get_path(_piter)
647 self.otank[_path]=(_name,child)
648 #self.reporter.reportError("2 Added %s at path %s" % (_name,repr(_path)))
649
650 def make(self, name=None, value=None, path=None, depth=1):
651 if path is None:
652 # make root node
653 piter = self.make_row( None, name, value )
654 path = self.treestore.get_path( piter )
655 self.otank[ path ] = (name, value)
656 #self.reporter.reportError("4 Added %s at path %s" % (name, path))
657 else:
658 name, value = self.otank[ path ]
659
660 piter = self.treestore.get_iter( path )
661 if not self.treestore.iter_has_child( piter ):
662 self.make_children(value,piter)
663
664 if depth:
665 for i in range( self.treestore.iter_n_children( piter ) ):
666 self.make( path = path+(i,), depth = depth - 1 )
667 else:
668 self.treeview.expand_row("0",False)
669
670 def row_expanded( self, treeview, piter, path ):
671 self.make( path = path )
672
673 # ----------------------------------
674 # ERROR PANEL
675
676 def error_callback(self,sev,filename,line,msg):
677 pos = self.errorstore.append(None, self.get_error_row_data(sev, filename,line,msg))
678 path = self.errorstore.get_path(pos)
679 col = self.errorview.get_column(3)
680 self.errorview.scroll_to_cell(path,col)
681
682 return 0;
683
684 # --------------------------------
685 # BUTTON METHODS
686
687 def open_click(self,*args):
688 dialog = gtk.FileChooserDialog("Open file...",
689 None,
690 gtk.FILE_CHOOSER_ACTION_OPEN,
691 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
692 gtk.STOCK_OPEN, gtk.RESPONSE_OK))
693 dialog.set_default_response(gtk.RESPONSE_OK)
694
695 filter = gtk.FileFilter()
696 filter.set_name("*.a4c, *.a4l")
697 filter.add_pattern("*.[Aa]4[Cc]")
698 filter.add_pattern("*.[Aa]4[Ll]")
699 dialog.add_filter(filter)
700
701 filter = gtk.FileFilter()
702 filter.set_name("All files")
703 filter.add_pattern("*")
704 dialog.add_filter(filter)
705
706 response = dialog.run()
707 _filename = dialog.get_filename()
708 dialog.destroy()
709
710 if response == gtk.RESPONSE_OK:
711 self.reporter.reportNote("File %s selected." % dialog.get_filename() )
712 self.library.clear()
713 self.do_open( _filename)
714
715
716 def reload_click(self,*args):
717 _type = None
718 if(self.sim):
719 _type = self.sim.getType().getName().toString();
720
721 self.library.clear()
722 self.do_open(self.filename)
723
724 if _type:
725 _t = self.library.findType(_type)
726 self.do_sim(_t)
727
728 def solve_click(self,*args):
729 #self.reporter.reportError("Solving simulation '" + self.sim.getName().toString() +"'...")
730 self.do_solve()
731
732 def check_click(self,*args):
733 self.do_check()
734 #self.reporter.reportError("CHECK clicked")
735
736 def preferences_click(self,*args):
737 if not self.sim:
738 self.reporter.reportError("No simulation created yet!");
739
740 _paramswin = SolverParametersWindow(self.sim, self.reporter)
741 _paramswin.show()
742
743 def methodrun_click(self,*args):
744 _sel = self.methodsel.get_active_text()
745 if _sel:
746 _method = None
747 _methods = self.sim.getType().getMethods()
748 for _m in _methods:
749 if _m.getName()==_sel:
750 _method = _m
751 if not _method:
752 self.reporter.reportError("Method is not valid")
753 return
754 self.do_method(_method)
755 else:
756 self.reporter.reportError("No method selected")
757
758 def auto_toggle(self,button,*args):
759 if button.get_active():
760 self.reporter.reportSuccess("Auto mode is now ON")
761 else:
762 self.reporter.reportSuccess("Auto mode is now OFF")
763
764 # ------------------------------
765 # CONTEXT MENU
766
767 def tree_click(self,widget,event):
768 # which button was clicked?
769 if event.button == 3:
770 _x = int(event.x)
771 _y = int(event.y)
772 _time = event.time
773 _pthinfo = self.treeview.get_path_at_pos(_x, _y)
774 if _pthinfo != None:
775 _canpop = False;
776 _path, _col, _cellx, _celly = _pthinfo
777 # self.reporter.reportError("Right click on %s" % self.otank[_path][0])
778 _instance = self.otank[_path][1]
779 if _instance.getType().isRefinedSolverVar():
780 _canpop = True;
781 if _instance.isFixed():
782 self.fixmenuitem.set_sensitive(False)
783 self.freemenuitem.set_sensitive(True)
784 else:
785 self.fixmenuitem.set_sensitive(True)
786 self.freemenuitem.set_sensitive(False)
787 elif _instance.isRelation():
788 _canpop = True;
789 self.propsmenuitem.set_sensitive(True)
790 else:
791 self.fixmenuitem.set_sensitive(False)
792 self.freemenuitem.set_sensitive(False)
793
794 if _instance.isPlottable():
795 self.plotmenuitem.set_sensitive(True)
796 _canpop = True;
797 else:
798 self.plotmenuitem.set_sensitive(False)
799
800 if _canpop:
801 self.treeview.grab_focus()
802 self.treeview.set_cursor( _path, _col, 0)
803 self.treecontext.popup( None, None, None, event.button, _time)
804 return 1
805
806 def fix_activate(self,widget):
807 _path,_col = self.treeview.get_cursor()
808 _name, _instance = self.otank[_path]
809 _instance.setFixed(True)
810 self.reporter.reportNote("Fixed variable %s" % _instance.getName().toString())
811 if self.autotoggle.get_active():
812 self.sim.check()
813 self.do_solve()
814 else:
815 self.refreshtree()
816 return 1
817
818 def free_activate(self,widget):
819 _path,_col = self.treeview.get_cursor()
820 _instance = self.otank[_path][1]
821 _instance.setFixed(False)
822 self.reporter.reportNote("Freed variable %s" % _instance.getName().toString())
823 if self.autotoggle.get_active():
824 self.sim.check()
825 self.do_solve()
826 else:
827 self.refreshtree()
828 return 1
829
830 def plot_activate(self,widget):
831 self.reporter.reportNote("plot_activate...");
832 _path,_col = self.treeview.get_cursor()
833 _instance = self.otank[_path][1]
834 if not _instance.isPlottable():
835 self.reporter.reportError("Can't plot instance %s" % _instance.getName().toString())
836 return
837 else:
838 self.reporter.reportNote("Instance %s about to be plotted..." % _instance.getName().toString())
839
840 print("Plotting instance '%s'..." % _instance.getName().toString())
841
842 _plot = _instance.getPlot()
843
844 print "Title: ", _plot.getTitle()
845 _plot.show(True)
846
847 return 1
848
849 def props_activate(self,widget):
850 _path,_col = self.treeview.get_cursor()
851 _instance = self.otank[_path][1]
852 if _instance.isRelation():
853 print "Relation '"+_instance.getName().toString()+"':", \
854 _instance.getRelationAsString(self.sim.getModel())
855 else:
856 self.reporter.reportWarning("props_activate not implemented")
857
858
859 # ---------------------------------
860 # WINDOW-LEVEL ACTIONS
861
862 def delete_event(self, widget, event, data=None):
863 self.reporter.clearPythonErrorCallback()
864 _w,_h = self.window.get_size()
865 _t,_l = self.window.get_position()
866 _display = self.window.get_screen().get_display().get_name()
867 self.prefs.setGeometrySizePosition(_display,"browserwin",_w,_h,_t,_l );
868
869 _p = self.browserpaned.get_position()
870 self.prefs.setGeometryValue(_display,"browserpaned",_p);
871
872 # causes prefs to be saved unless they are still being used elsewher
873 del(self.prefs)
874
875 gtk.main_quit()
876 print "GTK QUIT"
877 return False
878
879 #======================================================
880 # SOLVER PARAMETERS WINDOW
881
882 class SolverParametersWindow:
883 def __init__(self,sim,reporter):
884 self.sim = sim
885 self.params = self.sim.getSolverParameters();
886
887 self.reporter = reporter
888
889 _xml = gtk.glade.XML(GLADE_FILE,"paramswin")
890 self.window = _xml.get_widget("paramswin")
891 self.paramdescription = _xml.get_widget("paramdescription")
892 self.solvername = _xml.get_widget("solvername")
893 _xml.signal_autoconnect(self)
894
895 self.solvername.set_text(self.sim.getSolver().getName())
896
897 self.paramsview = _xml.get_widget("paramsview")
898 self.otank = {}
899 self.paramstore = gtk.TreeStore(str,str,str,bool,str,int)
900 self.paramsview.set_model(self.paramstore)
901
902 # name column
903 _renderer0 = gtk.CellRendererText()
904 _col0 = gtk.TreeViewColumn("Name", _renderer0, text=0, background=4, weight=5)
905 self.paramsview.append_column(_col0)
906
907 # value column: 'editable' set by column 3 of the model data.
908 _renderer1 = gtk.CellRendererText()
909 _renderer1.connect('edited',self.on_paramsview_edited)
910 _col1 = gtk.TreeViewColumn("Value", _renderer1, text=1, editable=3, background=4)
911 self.paramsview.append_column(_col1)
912
913 # range column
914 _renderer2 = gtk.CellRendererText()
915 _col2 = gtk.TreeViewColumn("Range", _renderer2, text=2, background=4)
916 self.paramsview.append_column(_col2)
917
918 self.populate()
919
920 self.paramsview.expand_all()
921
922 #def on_paramswin_key_press_event(self,widget,event):
923 # if event.keyval == ESCAPE_KEY:
924 # if not gtk.gdk.events_pending():
925 # self.do_destroy()
926
927 def on_paramsview_row_activated(self,treeview,path,view_column,*args,**kwargs):
928 # get back the object we just clicked
929
930 if not self.otank.has_key(path):
931 return
932
933 _iter,_param = self.otank[path]
934
935 if _param.isBool():
936 newvalue = not _param.getBoolValue()
937 _param.setBoolValue(newvalue)
938 if newvalue:
939 self.paramstore.set_value(_iter,1,SOLVERPARAM_BOOL_TRUE)
940 else:
941 self.paramstore.set_value(_iter,1,SOLVERPARAM_BOOL_FALSE)
942 self.paramstore.set_value(_iter,4, CHANGED_COLOR)
943
944 def on_paramsview_button_press_event(self,widget,event):
945 if event.button == 1:
946 _x = int(event.x)
947 _y = int(event.y)
948 _time = event.time
949 _pathinfo = self.paramsview.get_path_at_pos(_x, _y)
950 if _pathinfo != None:
951 _path, _col, _cellx, _celly = _pathinfo
952 if not self.otank.has_key(_path):
953 return
954 _iter, _param = self.otank[_path]
955
956 # update the description field
957 self.paramdescription.set_text(_param.getDescription())
958
959 if _param.isStr():
960 _menu = gtk.Menu();
961 _head = gtk.ImageMenuItem("Options",True)
962 _head.show()
963 _head.set_sensitive(False)
964 _img = gtk.Image()
965 _img.set_from_file('icons/folder-open.png')
966 _head.set_image(_img)
967 _menu.append(_head)
968 _sep = gtk.SeparatorMenuItem(); _sep.show()
969 _menu.append(_sep);
970
971 _item = None;
972 for i in _param.getStrOptions():
973 _item = gtk.RadioMenuItem(group=_item, label=i);
974 if i == _param.getStrValue():
975 _item.set_active(True)
976 else:
977 _item.set_active(False)
978 _item.show()
979 _item.connect('activate', self.on_menu_activate, _param, _iter, i);
980 _menu.append(_item)
981
982 _menu.show()
983 _menu.popup(None, None, None, event.button, _time)
984
985 def on_menu_activate(self, menuitem, param, iter, newvalue):
986 if param.getStrValue() != newvalue:
987 param.setStrValue(newvalue)
988 self.paramstore.set_value(iter, 1, newvalue)
989 self.paramstore.set_value(iter, 4, CHANGED_COLOR)
990 else:
991 print "NOT CHANGED"
992
993 def on_paramsview_cursor_changed(self, *args, **kwargs):
994 _path, _col = self.paramsview.get_cursor()
995 if not self.otank.has_key(_path):
996 self.paramdescription.set_text("")
997 return
998 _iter, _param = self.otank[_path]
999 self.paramdescription.set_text(_param.getDescription())
1000 #self.paramsview.set_cursor(_path,self.paramsview.get_column(1));
1001
1002 def on_paramsview_edited(self, renderer, path, newtext, **kwargs):
1003 # get back the Instance object we just edited (having to use this seems like a bug)
1004 path = tuple( map(int,path.split(":")) )
1005
1006 if not self.otank.has_key(path):
1007 raise RuntimeError("cell_edited_callback: invalid path '%s'" % path)
1008 return
1009
1010 _iter,_param = self.otank[path]
1011 # you can only edit real, int, str:
1012
1013 _changed = False
1014 if _param.isInt():
1015 newvalue = int(newtext)
1016 if _param.isBounded():
1017 if newvalue > _param.getIntUpperBound():
1018 self.doErrorDialog("The entered value '%d' is above the upper bound." % newvalue)
1019 return False
1020 if newvalue < _param.getIntLowerBound():
1021 self.doErrorDialog("The entered value '%d' is below the lower bound." % newvalue)
1022 return False
1023 if _param.getIntValue() != newvalue:
1024 _param.setIntValue(newvalue)
1025 _changed = True
1026 elif _param.isReal():
1027 newvalue = float(newtext)
1028 if _param.isBounded():
1029 if newvalue > _param.getRealUpperBound():
1030 self.doErrorDialog("The entered value '%f' is above the upper bound." % newvalue)
1031 return False
1032 if newvalue < _param.getRealLowerBound():
1033 self.doErrorDialog("The entered value '%f' is below the lower bound." % newvalue)
1034 return False
1035 if _param.getRealValue() != newvalue:
1036 _param.setRealValue(newvalue)
1037 _changed = True
1038 elif _param.isStr():
1039 newvalue = str(newtext)
1040 if _param.getStrValue() != newvalue:
1041 _param.setStrValue(newvalue)
1042 _changed = True
1043
1044 if _changed:
1045 self.paramstore.set_value(_iter, 1, newvalue)
1046 self.paramstore.set_value(_iter, 4, CHANGED_COLOR)
1047 else:
1048 print "NO CHANGE"
1049
1050
1051 def create_row_data(self,p):
1052 _row = [p.getLabel()];
1053 if p.isStr():
1054 _row.extend([p.getStrValue(), str(len(p.getStrOptions()))+" options", False]);
1055 elif p.isBool():
1056 if p.getBoolValue():
1057 _val = SOLVERPARAM_BOOL_TRUE
1058 else:
1059 _val = SOLVERPARAM_BOOL_FALSE
1060 _row.extend([_val,"",False])
1061 elif p.isReal():
1062 if not p.isBounded():
1063 _row.extend([str(p.getRealValue()), "",True])
1064 else:
1065 _row.extend([str(p.getRealValue()), "[ "+str(p.getRealLowerBound())+", "+str(p.getRealUpperBound())+" ]",True])
1066 elif p.isInt():
1067 if not p.isBounded():
1068 _row.extend([str(p.getIntValue()), "", True])
1069 else:
1070 _row.extend([str(p.getIntValue()), "[ "+str(p.getIntLowerBound())+", "+str(p.getIntLowerBound())+" ]", True])
1071
1072 else:
1073 raise RuntimeError("invalid type")
1074
1075 _row.extend(["white",pango.WEIGHT_NORMAL])
1076 return _row;
1077
1078 def populate(self):
1079 # Fill the paramstore with data
1080
1081 data = {}
1082 for i in self.params:
1083 if not data.has_key(i.getPage()):
1084 data[i.getPage()] = {}
1085 data[i.getPage()][i.getNumber()] = i;
1086
1087 _pagenum = 1;
1088 for _page in sorted(data.keys()):
1089 if len(data[_page].keys()):
1090 _pageiter = self.paramstore.append( None, ["Page "+str(_pagenum), "", "", False, "white", pango.WEIGHT_BOLD])
1091 for _number in sorted(data[_page].keys()):
1092 _param = data[_page][_number]
1093 _piter = self.paramstore.append( _pageiter, self.create_row_data(_param) )
1094 _path = self.paramstore.get_path(_piter)
1095 self.otank[ _path ] = (_piter, _param)
1096 _pagenum = _pagenum + 1
1097
1098 def show(self):
1099 self.window.show()
1100
1101 def on_paramscancel_clicked(self,*args,**kwargs):
1102 self.do_destroy()
1103
1104 def on_paramsapply_clicked(self,*args,**kwargs):
1105 self.sim.setSolverParameters(self.params);
1106 self.do_destroy()
1107
1108 def on_paramswin_destroy(self,*args,**kwargs):
1109 self.do_destroy()
1110
1111 def do_destroy(self):
1112 self.window.hide()
1113 del(self.window)
1114 del(self.params)
1115
1116 def test():
1117 import ascend
1118 b = Browser();
1119 b.run()
1120
1121 if __name__ == "__main__":
1122 test()

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