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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 154 - (show annotations) (download) (as text)
Thu Dec 22 15:18:02 2005 UTC (16 years, 9 months ago) by johnpye
File MIME type: text/x-python
File size: 21387 byte(s)
Removing debug output, adding self_test to iapws95.
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 class Browser:
28
29 # ---------------------------------
30 # SETUP
31
32 def __init__(self):
33 #--------
34 # load the file referenced in the command line, if any
35
36 parser = optparse.OptionParser(usage="%prog [[-m typename] file]", version="gtkbrowser $rev$" )
37 # add options here if we want
38
39 parser.add_option("-m", "--model",
40 action="store", type="string", dest="model",help="specify the model to instantiate upon loading modules")
41 (options, args) = parser.parse_args()
42
43 #print "OPTIONS_______________:",options
44
45 #--------
46 # load up the preferences ini file
47
48 self.prefs = preferences.Preferences()
49
50 #--------
51 # initialise ASCEND
52
53 self.library = ascend.Library()
54
55 self.sim = None
56
57 #-------------------
58 # Set up the window and main widget actions
59
60 glade = gtk.glade.XML(GLADE_FILE,"browserwin")
61
62 self.window = glade.get_widget("browserwin")
63
64 if not self.window:
65 raise RuntimeError("Couldn't load window from glade file")
66 self.window.connect("delete_event", self.delete_event)
67
68 self.openbutton=glade.get_widget("openbutton")
69 self.openbutton.connect("clicked",self.open_click)
70
71 self.reloadbutton=glade.get_widget("reloadbutton")
72 self.reloadbutton.connect("clicked",self.reload_click)
73
74 self.solvebutton=glade.get_widget("solvebutton")
75 self.solvebutton.connect("clicked",self.solve_click)
76
77 self.checkbutton=glade.get_widget("checkbutton")
78 self.checkbutton.connect("clicked",self.check_click)
79
80 self.autotoggle=glade.get_widget("autotoggle")
81 self.autotoggle.connect("toggled",self.auto_toggle)
82
83 self.methodrunbutton=glade.get_widget("methodrunbutton")
84 self.methodrunbutton.connect("clicked",self.methodrun_click)
85
86 self.methodsel=glade.get_widget("methodsel")
87
88 self.maintabs = glade.get_widget("maintabs")
89
90 #-------------------
91 # pixbufs to be used in the error listing
92
93 self.iconok = self.window.render_icon(gtk.STOCK_YES,gtk.ICON_SIZE_MENU)
94 self.iconinfo = self.window.render_icon(gtk.STOCK_DIALOG_INFO,gtk.ICON_SIZE_MENU)
95 self.iconwarning = self.window.render_icon(gtk.STOCK_DIALOG_WARNING,gtk.ICON_SIZE_MENU)
96 self.iconerror = self.window.render_icon(gtk.STOCK_DIALOG_ERROR,gtk.ICON_SIZE_MENU)
97
98 #--------------------
99 # set up the context menu for fixing/freeing vars
100
101 self.treecontext = gtk.Menu();
102 self.fixmenuitem = gtk.MenuItem("Fix");
103 self.freemenuitem = gtk.MenuItem("Free");
104 self.fixmenuitem.show()
105 self.freemenuitem.show()
106 self.treecontext.append(self.fixmenuitem);
107 self.treecontext.append(self.freemenuitem);
108 self.fixmenuitem.connect("activate",self.fix_activate)
109 self.freemenuitem.connect("activate",self.free_activate)
110 if not self.treecontext:
111 raise RuntimeError("Couldn't create browsercontext")
112 #--------------------
113 # set up the error view
114
115 self.errorview = glade.get_widget("errorview")
116 errstorecolstypes = [gtk.gdk.Pixbuf,str,str,str,int]
117 self.errorstore = gtk.TreeStore(*errstorecolstypes)
118 errtitles = ["","Location","Message"];
119 self.errorview.set_model(self.errorstore)
120 self.errcols = [ gtk.TreeViewColumn() for _type in errstorecolstypes]
121
122 i = 0
123 for tvcolumn in self.errcols[:len(errtitles)]:
124 tvcolumn.set_title(errtitles[i])
125 self.errorview.append_column(tvcolumn)
126
127 if i>0:
128 _renderer = gtk.CellRendererText()
129 tvcolumn.pack_start(_renderer, True)
130 tvcolumn.add_attribute(_renderer, 'text', i)
131 if(i==2):
132 tvcolumn.add_attribute(_renderer, 'foreground', 3)
133 tvcolumn.add_attribute(_renderer, 'weight', 4)
134 else:
135 _renderer1 = gtk.CellRendererPixbuf()
136 tvcolumn.pack_start(_renderer1, False)
137 tvcolumn.add_attribute(_renderer1, 'pixbuf', int(0))
138
139 i = i + 1
140
141
142 #--------------------
143 # set up the error reporter callback
144 self.reporter = ascend.getReporter()
145 self.reporter.setPythonErrorCallback(self.error_callback)
146
147 #-------------------
148 # set up the module view
149
150 self.modtank = {}
151 self.moduleview = glade.get_widget("moduleview")
152 modulestorecoltypes = [str, str]
153 self.modulestore = gtk.TreeStore(*modulestorecoltypes)
154 moduleviewtitles = ["Module name", "Filename"]
155 self.moduleview.set_model(self.modulestore)
156 self.modcols = [ gtk.TreeViewColumn() for _type in modulestorecoltypes]
157 i = 0
158 for modcol in self.modcols[:len(moduleviewtitles)]:
159 modcol.set_title(moduleviewtitles[i])
160 self.moduleview.append_column(modcol)
161 _renderer = gtk.CellRendererText()
162 modcol.pack_start(_renderer, True)
163 modcol.add_attribute(_renderer, 'text', i)
164 i = i + 1
165 self.moduleview.connect("row-activated", self.module_activated )
166
167 #-------------------
168 # RE for units matching
169 self.units_re = re.compile("([-+]?(\d+(\.\d*)?|\d*\.d+)([eE][-+]?\d+)?)\s*(.*)");
170
171 #--------------------
172 # set up the methods combobox
173
174 self.methodstore = gtk.ListStore(str)
175 self.methodsel.set_model(self.methodstore)
176 self.methodsel.set_text_column(0)
177 _methodrenderer = gtk.CellRendererText()
178 self.methodsel.pack_start(_methodrenderer, True)
179 self.methodsel.add_attribute(_methodrenderer, 'text',0)
180
181 #--------
182 # set up the instance browser view
183
184 self.otank = {}
185 self.treeview = glade.get_widget("browserview")
186 columns = [str,str,str,str,int,bool]
187 self.treestore = gtk.TreeStore(*columns)
188 titles = ["Name","Type","Value"];
189 self.treeview.set_model(self.treestore)
190 self.tvcolumns = [ gtk.TreeViewColumn() for _type in columns[:len(titles)] ]
191
192 self.treeview.connect("row-expanded", self.row_expanded )
193 self.treeview.connect("button-press-event", self.tree_click )
194
195 # data columns are: name type value colour weight editable
196
197 i = 0
198 for tvcolumn in self.tvcolumns[:len(titles)]:
199 tvcolumn.set_title(titles[i])
200 self.treeview.append_column(tvcolumn)
201
202 renderer = gtk.CellRendererText()
203 tvcolumn.pack_start(renderer, True)
204 tvcolumn.add_attribute(renderer, 'text', i)
205 tvcolumn.add_attribute(renderer, 'foreground', 3)
206 tvcolumn.add_attribute(renderer, 'weight', 4)
207 if(i==2):
208 tvcolumn.add_attribute(renderer, 'editable', 5)
209 renderer.connect('edited',self.cell_edited_callback)
210 i = i + 1
211
212
213 if(len(args)==1):
214 self.do_open(args[0])
215
216 print "Options: ",options
217
218 if options.model:
219 try:
220 _t =self.library.findType(options.model);
221 self.do_sim(_t);
222 except RuntimeError, e:
223 self.reporter.reportError("Failed to create instance of '%s': %s" %(options.model, str(e)));
224
225 def run(self):
226 self.window.show()
227 gtk.threads_enter();
228 gtk.main()
229 gtk.threads_leave();
230
231 # --------------------------------------------
232 # MAJOR GUI COMMANDS
233
234
235 def do_open(self,filename):
236 # TODO does the user want to lose their work?
237 # TODO do we need to chdir?
238
239 self.errorstore.clear()
240
241 self.treestore.clear()
242 self.otank = {}
243
244 self.library.clear()
245 self.library.load(filename)
246
247 self.filename = filename
248
249 # Load the current list of modules into self.modules
250 self.modtank = {}
251 self.modulestore.clear()
252 modules = self.library.getModules()
253 for m in reversed(modules):
254 _n = str( m.getName() )
255 _f = str( m.getFilename() )
256 #print "ADDING ROW name %s, file = %s" % (_n, _f)
257 _r = self.modulestore.append(None, [ _n, _f ])
258 for t in self.library.getModuleTypes(m):
259 _n = t.getName()
260 #print "ADDING TYPE %s" % _n
261 _piter = self.modulestore.append(_r , [ _n, "" ])
262 _path = self.modulestore.get_path(_piter)
263 self.modtank[_path]=t
264
265 #print "DONE ADDING MODULES"
266
267 self.sim = None;
268 self.maintabs.set_current_page(0);
269
270 def do_sim(self, type_object):
271 self.sim = None;
272 # TODO: clear out old simulation first!
273
274 print "DO_SIM(%s)" % str(type_object.getName())
275 self.sim = type_object.getSimulation(str(type_object.getName())+"_sim")
276 print "...DONE 'getSimulation'"
277
278 print "BUILDING SIMULATION"
279 self.sim.build();
280 print "DONE BUILDING"
281
282 # empty things out first
283 self.methodstore.clear()
284 self.treestore.clear()
285
286 # methods
287 _methods = self.sim.getType().getMethods()
288 for _m in _methods:
289 self.methodstore.append ([_m.getName()])
290
291 # instance hierarchy
292 self.otank = {} # map path -> (name,value)
293 self.make( self.sim.getName(),self.sim.getModel() )
294 self.maintabs.set_current_page(1);
295
296 def do_solve(self):
297 if not self.sim:
298 self.reporter.reportError("No model selected yet")
299
300 self.sim.solve(ascend.Solver("QRSlv"))
301 self.refreshtree()
302
303 def do_check(self):
304 if not self.sim:
305 self.reporter.reportError("No model selected yet")
306
307 if self.sim.check():
308 self.reporter.reportNote("System check OK")
309 self.sim.checkDoF()
310
311 self.refreshtree()
312
313 def do_method(self,method):
314 if not self.sim:
315 self.reporter.reportError("No model selected yet")
316
317 self.sim.run(method)
318 self.refreshtree()
319
320 # --------------------------------------------
321 # MODULE LIST
322
323 def module_activated(self, treeview, path, column, *args):
324 modules = self.library.getModules()
325 print "PATH",path
326 if len(path)==1:
327 self.reporter.reportNote("Launching of external editor not yet implemented")
328 elif len(path)==2:
329 if(self.modtank.has_key(path)):
330 _type = self.modtank[path];
331 self.reporter.reportNote("Creating simulation for type %s" % str(_type.getName()) )
332 self.do_sim(_type)
333 else:
334 self.reporter.reportError("Didn't find type corresponding to row")
335
336 # --------------------------------------------
337 # INSTANCE TREE
338
339 def get_tree_row_data(self,instance):
340 _value = str(instance.getValue())
341 _type = str(instance.getType())
342 _name = str(instance.getName())
343 _fgcolor = "black"
344 _fontweight = pango.WEIGHT_NORMAL
345 _editable = False
346 if instance.getType().isRefinedSolverVar():
347 _editable = True
348 if instance.isFixed():
349 _fgcolor = "#008800"
350 _fontweight = pango.WEIGHT_BOLD
351 else:
352 _fgcolor = "#000088"
353 _fontweight = pango.WEIGHT_BOLD
354 elif instance.isBool() or instance.isReal() or instance.isInt():
355 # TODO can't edit constants that have already been refined
356 _editable = True
357
358 #if(len(_value) > 80):
359 # _value = _value[:80] + "..."
360
361 return [_name, _type, _value, _fgcolor, _fontweight, _editable]
362
363 def get_error_row_data(self,sev,filename,line,msg):
364 _sevicon = {
365 0: self.iconok
366 ,1: self.iconinfo
367 ,2: self.iconwarning
368 ,3: self.iconerror
369 ,4: self.iconinfo
370 ,5: self.iconwarning
371 ,6: self.iconerror
372 }[sev]
373
374 _fontweight = pango.WEIGHT_NORMAL
375 if sev==6:
376 _fontweight = pango.WEIGHT_BOLD
377
378 _fgcolor = "black"
379 if sev==4:
380 _fgcolor = "#888800"
381 elif sev==5:
382 _fgcolor = "#884400"
383 elif sev==6:
384 _fgcolor = "#880000"
385 elif sev==0:
386 _fgcolor = "#008800"
387
388 if not filename and not line:
389 _fileline = ""
390 else:
391 if(len(filename) > 25):
392 filename = "..."+filename[-22:]
393 _fileline = filename + ":" + str(line)
394
395 _res = [_sevicon,_fileline,msg.rstrip(),_fgcolor,_fontweight]
396 #print _res
397 return _res
398
399 def make_row( self, piter, name, value ):
400
401 _piter = self.treestore.append( piter, self.get_tree_row_data(value) )
402 return _piter
403
404 def refreshtree(self):
405 # @TODO FIXME use a better system than colour literals!
406 for _path in self.otank: # { path : (name,value) }
407 _iter = self.treestore.get_iter(_path)
408 _name, _instance = self.otank[_path]
409 self.treestore.set_value(_iter, 2, _instance.getValue())
410 if _instance.getType().isRefinedSolverVar():
411 if _instance.isFixed() and self.treestore.get_value(_iter,3)=="#000088":
412 self.treestore.set_value(_iter,3,"#008800")
413 elif not _instance.isFixed() and self.treestore.get_value(_iter,3)=="#008800":
414 self.treestore.set_value(_iter,3,"#000088")
415
416 def cell_edited_callback(self, renderer, path, newtext, **kwargs):
417 # get back the Instance object we just edited (having to use this seems like a bug)
418 path = tuple( map(int,path.split(":")) )
419
420 if not self.otank.has_key(path):
421 raise RuntimeError("cell_edited_callback: invalid path '%s'" % path)
422 return
423
424 _name, _instance = self.otank[path]
425
426 if _instance.isReal():
427 # only real-valued things can have units
428
429 try:
430 # match a float with option text afterwards, optionally separated by whitespace
431 _match = re.match(self.units_re,newtext)
432 if not _match:
433 self.reporter.reportError("Not a valid value-and-optional-units")
434 return
435
436 _val = _match.group(1)
437 _units = _match.group(5)
438 #_val, _units = re.split("[ \t]+",newtext,2);
439 except RuntimeError:
440 self.reporter.reportError("Unable to split value and units")
441 return
442 print "val = ",_val
443 print "units = ",_units
444
445 # parse the units, throw an error if no good
446 try:
447 _val = float(_val)
448 except RuntimeError:
449 self.reporter.reportError("Unable to convert number part '%s' to float" % _val)
450
451 if _units.strip() == "":
452 _u = _instance.getType().getPreferredUnits()
453 if _u == None:
454 _u = _instance.getDimensions().getDefaultUnits()
455 self.reporter.reportNote("Assuming units '%s'" % _u.getName().toString() )
456 else:
457 try:
458 _u = ascend.Units(_units)
459 self.reporter.reportNote("Parsed units %s" % _units)
460 except RuntimeError:
461 self.reporter.reportError("Unrecognisable units '%s'" % _units)
462 return
463
464 if _instance.getDimensions() != _u.getDimensions():
465
466 if _u.getDimensions().isDimensionless():
467 _units = "[dimensionless]"
468
469 _my_dims = _instance.getDimensions().getDefaultUnits()
470 if _instance.getDimensions().isDimensionless():
471 _my_dims = "[dimensionless]"
472
473 self.reporter.reportError("Incompatible units '%s' (must fit with '%s')"
474 % (_units, _my_dims) )
475 return
476
477 if _units.strip() != "" and not _instance.getDimensions().isDimensionless():
478 self.prefs.setPreferredUnits(str(_instance.getType().getName()), _units);
479
480 _conv = float(_u.getConversion())
481 # self.reporter.reportNote("Converting: multiplying '%s %s' by factor %s to get SI units" % (_val, _units, _conv) )
482 _val = _val * _conv;
483
484 self.reporter.reportNote("Setting '%s' to '%f'" % (_name, _val))
485
486 if _instance.getType().isRefinedSolverVar():
487 # set the 'fixed' flag as well
488 _instance.setFixedValue(float(_val))
489 else:
490 _instance.setRealValue(float(_val))
491 else:
492 if _instance.isBool():
493 _lower = newtext.lower();
494 if _lower.startswith("t") or _lower.startswith("y") or _lower.strip()=="1":
495 newtext = 1
496 elif _lower.startswith("f") or _lower.startswith("n") or _lower.strip()=="0":
497 newtext = 0
498 else:
499 self.reporter.reportError("Invalid entry for a boolean variable: '%s'" % newtext)
500 return
501 _val = bool(newtext);
502 if _val == _instance.getValue():
503 self.reporter.reportNote("Boolean atom '%s' was not altered" % _instance.getName())
504 return
505 _instance.setBoolValue(_val)
506
507 elif _instance.isInt():
508 _val = int(newtext)
509 if _val == _instance.getValue():
510 self.reporter.reportNote("Integer atom '%s' was not altered" % _instance.getName())
511 return
512 _instance.setIntValue(_val)
513 else:
514 self.reporter.reportError("Attempt to set a non-real, non-boolean, non-integer value!")
515 return
516
517 # now that the variable is set, update the GUI and re-solve if desired
518 _iter = self.treestore.get_iter(path)
519 self.treestore.set_value(_iter,2,_instance.getValue())
520
521 if _instance.getType().isRefinedSolverVar():
522 self.treestore.set_value(_iter,3,"#008800") # set the row green as fixed
523
524 if self.autotoggle.get_active():
525 self.sim.check()
526 self.do_solve()
527 #self.reporter.reportError("SOLVER completed")
528
529
530 def make_children(self, value, piter ):
531 if value.isCompound():
532 children=value.getChildren();
533 for child in children:
534 _name = child.getName();
535 _piter = self.make_row(piter,_name,child)
536 _path = self.treestore.get_path(_piter)
537 self.otank[_path]=(_name,child)
538 #self.reporter.reportError("2 Added %s at path %s" % (_name,repr(_path)))
539
540 def make(self, name=None, value=None, path=None, depth=1):
541 if path is None:
542 # make root node
543 piter = self.make_row( None, name, value )
544 path = self.treestore.get_path( piter )
545 self.otank[ path ] = (name, value)
546 #self.reporter.reportError("4 Added %s at path %s" % (name, path))
547 else:
548 name, value = self.otank[ path ]
549
550 piter = self.treestore.get_iter( path )
551 if not self.treestore.iter_has_child( piter ):
552 self.make_children(value,piter)
553
554 if depth:
555 for i in range( self.treestore.iter_n_children( piter ) ):
556 self.make( path = path+(i,), depth = depth - 1 )
557
558 def row_expanded( self, treeview, piter, path ):
559 self.make( path = path )
560
561 # ----------------------------------
562 # ERROR PANEL
563
564 def error_callback(self,sev,filename,line,msg):
565 pos = self.errorstore.append(None, self.get_error_row_data(sev, filename,line,msg))
566 path = self.errorstore.get_path(pos)
567 col = self.errorview.get_column(3)
568 self.errorview.scroll_to_cell(path,col)
569
570 return 0;
571
572 # --------------------------------
573 # BUTTON METHODS
574
575 def open_click(self,*args):
576 dialog = gtk.FileChooserDialog("Open file...",
577 None,
578 gtk.FILE_CHOOSER_ACTION_OPEN,
579 (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
580 gtk.STOCK_OPEN, gtk.RESPONSE_OK))
581 dialog.set_default_response(gtk.RESPONSE_OK)
582
583 filter = gtk.FileFilter()
584 filter.set_name("*.a4c, *.a4l")
585 filter.add_pattern("*.[Aa]4[Cc]")
586 filter.add_pattern("*.[Aa]4[Ll]")
587 dialog.add_filter(filter)
588
589 filter = gtk.FileFilter()
590 filter.set_name("All files")
591 filter.add_pattern("*")
592 dialog.add_filter(filter)
593
594 response = dialog.run()
595 _filename = dialog.get_filename()
596 dialog.destroy()
597
598 if response == gtk.RESPONSE_OK:
599 self.reporter.reportNote("File %s selected." % dialog.get_filename() )
600 self.do_open( _filename)
601
602
603 def reload_click(self,*args):
604 _type = None
605 if(self.sim):
606 _type = self.sim.getType().getName().toString();
607
608 self.do_open(self.filename)
609
610 if _type:
611 _t = self.library.findType(_type)
612 self.do_sim(_t)
613
614 def solve_click(self,*args):
615 #self.reporter.reportError("Solving simulation '" + self.sim.getName().toString() +"'...")
616 self.do_solve()
617
618 def check_click(self,*args):
619 self.do_check()
620 #self.reporter.reportError("CHECK clicked")
621
622 def methodrun_click(self,*args):
623 _sel = self.methodsel.get_active_text()
624 if _sel:
625 _method = None
626 _methods = self.sim.getType().getMethods()
627 for _m in _methods:
628 if _m.getName()==_sel:
629 _method = _m
630 if not _method:
631 self.reporter.reportError("Method is not valid")
632 return
633 self.do_method(_method)
634 else:
635 self.reporter.reportError("No method selected")
636
637 def auto_toggle(self,button,*args):
638 if button.get_active():
639 self.reporter.reportError("Auto mode is now ON")
640 else:
641 self.reporter.reportError("Auto mode is now OFF")
642
643 # ------------------------------
644 # CONTEXT MENU
645
646 def tree_click(self,widget,event):
647 # which button was clicked?
648 if event.button == 3:
649 _x = int(event.x)
650 _y = int(event.y)
651 _time = event.time
652 _pthinfo = self.treeview.get_path_at_pos(_x, _y)
653 if _pthinfo != None:
654 _path, _col, _cellx, _celly = _pthinfo
655 # self.reporter.reportError("Right click on %s" % self.otank[_path][0])
656 _instance = self.otank[_path][1]
657 if _instance.getType().isRefinedSolverVar():
658 self.treeview.grab_focus()
659 self.treeview.set_cursor( _path, _col, 0)
660 if _instance.isFixed():
661 self.fixmenuitem.hide()
662 self.freemenuitem.show()
663 else:
664 self.fixmenuitem.show()
665 self.freemenuitem.hide()
666 self.treecontext.popup( None, None, None, event.button, _time)
667 return 1
668 else:
669 self.reporter.reportError("Invalid selection for right-click")
670
671 def fix_activate(self,widget):
672 _path,_col = self.treeview.get_cursor()
673 _name, _instance = self.otank[_path]
674 _instance.setFixed(True)
675 self.reporter.reportNote("Fixed variable %s" % _instance.getName().toString())
676 if self.autotoggle.get_active():
677 self.sim.check()
678 self.do_solve()
679 else:
680 self.refreshtree()
681 return 1
682
683 def free_activate(self,widget):
684 _path,_col = self.treeview.get_cursor()
685 _instance = self.otank[_path][1]
686 _instance.setFixed(False)
687 self.reporter.reportNote("Freed variable %s" % _instance.getName().toString())
688 if self.autotoggle.get_active():
689 self.sim.check()
690 self.do_solve()
691 else:
692 self.refreshtree()
693 return 1
694
695 # ---------------------------------
696 # WINDOW-LEVEL ACTIONS
697
698 def delete_event(self, widget, event, data=None):
699 self.reporter.clearPythonErrorCallback()
700
701 # causes prefs to be saved unless they are still being used elsewher
702 del(self.prefs)
703
704 gtk.main_quit()
705 print "GTK QUIT"
706 return False
707
708 def test():
709 import ascend
710 gtk.threads_init();
711 b = Browser();
712 b.run()
713
714 if __name__ == "__main__":
715 test()

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