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

Contents of /trunk/pygtk/interface/gtkbrowser.py

Parent Directory Parent Directory | Revision Log Revision Log


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

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