Package topo :: Package tkgui :: Module topoconsole
[hide private]
[frames] | no frames]

Source Code for Module topo.tkgui.topoconsole

  1  """ 
  2  TopoConsole class file. 
  3   
  4   
  5  $Id: topoconsole.py 11310 2010-07-27 16:56:14Z ceball $ 
  6  """ 
  7  __version__='$Revision: 11310 $' 
  8   
  9   
 10  # CB: does the status bar need to keep saying 'ok'? Sometimes 
 11  # positive feedback is useful, but 'ok' doesn't seem too helpful. 
 12   
 13   
 14  import os 
 15  import copy 
 16  import sys 
 17  import __main__ 
 18  import webbrowser 
 19  import string 
 20   
 21  from Tkinter import Frame, Button, \ 
 22       LEFT, YES, Label, DISABLED, \ 
 23       NORMAL, DoubleVar 
 24  from tkFileDialog import asksaveasfilename,askopenfilename 
 25   
 26  import param 
 27  from param import tk,normalize_path,resolve_path 
 28   
 29   
 30  import topo 
 31  from topo.plotting.plotgroup import plotgroups, FeatureCurvePlotGroup 
 32  from topo.misc.keyedlist import KeyedList 
 33  from topo.misc.commandline import sim_name_from_filename 
 34  import topo.misc.genexamples 
 35  import topo.command.basic 
 36   
 37  import topo.tkgui  
 38   
 39  from templateplotgrouppanel import TemplatePlotGroupPanel 
 40  from featurecurvepanel import FeatureCurvePanel 
 41  from projectionpanel import CFProjectionPanel,ProjectionActivityPanel,ConnectionFieldsPanel,RFProjectionPanel 
 42  from testpattern import TestPattern 
 43  from editor import ModelEditor 
 44   
 45   
 46  tk.AppWindow.window_icon_path = resolve_path('tkgui/icons/topo.xbm') 
 47   
 48  SCRIPT_FILETYPES = [('Topographica scripts','*.ty'), 
 49                      ('Python scripts','*.py'), 
 50                      ('All files','*')] 
 51  SAVED_FILE_EXTENSION = '.typ' 
 52  SAVED_FILETYPES = [('Topographica saved networks', 
 53                      '*'+SAVED_FILE_EXTENSION), 
 54                     ('All files','*')] 
 55   
 56  turl = "http://topographica.org/" 
 57  userman = "User_Manual/index.html" 
 58  tuts = "Tutorials/index.html" 
 59  refman = "Reference_Manual/index.html" 
 60  plotman = "User_Manual/plotting.html" 
 61   
 62  # for deb on ubuntu; will need to check others 
 63  pkgdoc = "/usr/share/doc/topographica/doc/"  
 64   
 65  # Documentation locations: locally built and web urls. 
 66  user_manual_locations      = ('doc/'+userman, 
 67                                pkgdoc+userman, 
 68                                turl+userman) 
 69  tutorials_locations        = ('doc/'+tuts, 
 70                                pkgdoc+tuts,   
 71                                turl+tuts) 
 72  reference_manual_locations = ('doc/'+refman, 
 73                                pkgdoc+refman, 
 74                                turl+refman) 
 75  python_doc_locations = ('http://www.python.org/doc/',) 
 76  topo_www_locations = (turl,) 
 77  plotting_help_locations = ('doc/'+plotman, 
 78                             pkgdoc+plotman, 
 79                             turl+plotman) 
 80   
 81  # If a particular plotgroup_template needs (or works better with) a 
 82  # specific subclass of PlotPanel, the writer of the new subclass 
 83  # or the plotgroup_template can declare here that that template 
 84  # should use a specific PlotPanel subclass.  For example: 
 85  #   plotpanel_classes['Hue Pref Map'] = HuePreferencePanel 
 86  plotpanel_classes = {} 
 87  # CEBALERT: why are the other plotpanel_classes updates at the end of this file? 
 88   
 89   
 90   
 91   
92 -def open_plotgroup_panel(class_,plotgroup=None,**kw):
93 94 if class_.valid_context(): 95 win = topo.guimain.some_area.new_window() 96 panel = class_(win,plotgroup=plotgroup,**kw) 97 98 if not panel.dock: 99 topo.guimain.some_area.eject(win) 100 else: 101 topo.guimain.some_area.consume(win) 102 103 panel.refresh_title() 104 105 panel.pack(expand='yes',fill='both') 106 win.sizeright() 107 108 #frame.sizeright() 109 110 #topo.guimain.messageBar.message('state', 'OK') 111 return panel 112 else: 113 topo.guimain.messageBar.response( 114 'No suitable objects in this simulation for this operation.')
115 116 117 118 119
120 -class PlotsMenuEntry(param.Parameterized):
121 """ 122 Stores information about a Plots menu command 123 (including the command itself, and the plotgroup template). 124 """
125 - def __init__(self,plotgroup,class_=TemplatePlotGroupPanel,**params):
126 """ 127 Store the template, and set the class that will be created by this menu entry 128 129 If users want to extend the Plot Panel classes, then they 130 should add entries to the plotpanel_classes dictionary. 131 If no entry is defined there, then the default class is used. 132 133 The class_ is overridden for any special cases listed in this method. 134 """ 135 super(PlotsMenuEntry,self).__init__(**params) 136 137 self.plotgroup = plotgroup 138 139 # Special cases. These classes are specific to the topo/tkgui 140 # directory and therefore this link must be made within the tkgui 141 # files. 142 if isinstance(self.plotgroup,FeatureCurvePlotGroup): 143 class_ = plotpanel_classes.get(self.plotgroup.name,FeatureCurvePanel) 144 145 self.class_ = plotpanel_classes.get(self.plotgroup.name,class_)
146 147
148 - def __call__(self,event=None,**kw):
149 """ 150 Instantiate the class_ (used as menu commands' 'command' attribute). 151 152 Keyword args are passed to the class_. 153 """ 154 new_plotgroup = copy.deepcopy(self.plotgroup) 155 156 # CB: hack to share plot_templates with the current 157 # plotgroup in plotgroups 158 new_plotgroup.plot_templates = topo.plotting.plotgroup.plotgroups[self.plotgroup.name].plot_templates 159 160 return open_plotgroup_panel(self.class_,new_plotgroup,**kw)
161 162 163 # Notebook only available for Tkinter>=8.5 164 try: 165 from param.external import Notebook
166 - class DockManager(Notebook):
167 """Manages windows that can be tabs in a notebook, or toplevels."""
168 - def __init__(self, master=None, cnf={}, **kw):
169 Notebook.__init__(self, master, cnf=cnf, **kw) 170 self._tab_ids = {}
171
172 - def _set_tab_title(self,win,title):
173 self.tab(self._tab_ids[win],text=title)
174
175 - def _set_toplevel_title(self,win,title):
176 prefix = topo.sim.name+": " 177 if not title.startswith(prefix): 178 title=prefix+title 179 self.tk.call("wm","title",win._w,title)
180
181 - def add(self, child, cnf={}, **kw):
182 self._tab_ids[child]=len(self.tabs()) 183 Notebook.add(self,child,cnf=cnf,**kw)
184 185 ## def unhide(self,win): 186 ## if win in self._tab_ids: 187 ## self.tab(self._tab_ids[win],state='normal') 188
189 - def new_window(self):
190 win = tk.AppWindow(self,status=True) 191 #self.consume(win) 192 return win
193
194 - def consume(self,win):
195 if win not in self._tab_ids: 196 self.tk.call('wm','forget',win._w) 197 win.title = lambda x: self._set_tab_title(win,x) 198 self.add(win)
199
200 - def eject(self,win):
201 if win in self._tab_ids: 202 self.forget(self._tab_ids[win]) 203 204 # manage my tab ids (HACK) 205 del self._tab_ids[win] 206 for w in self._tab_ids: 207 self._tab_ids[w]-=1 208 self._tab_ids[w]=max(self._tab_ids[w],0) 209 210 self.tk.call('wm','manage',win._w) 211 win.renew() 212 win.title = lambda x: self._set_toplevel_title(win,x) 213 return win
214 215 except ImportError:
216 - class FakeDockManager(Frame):
217 - def _set_tab_title(self,*args):
218 pass
219 - def _set_toplevel_title(self,win,title):
220 prefix = topo.sim.name+": " 221 if not title.startswith(prefix): 222 title=prefix+title 223 self.tk.call("wm","title",win._w,title)
224 - def add(self,*args):
225 pass
226 - def new_window(self):
227 win = tk.AppWindow(self,status=True) 228 return win
229 - def consume(self,win):
230 pass
231 - def eject(self,win):
232 win.renew() 233 win.title = lambda x: self._set_toplevel_title(win,x) 234 return win
235 236 DockManager = FakeDockManager 237 238 239 # This is really a hack. There doesn't seem to be any easy way to tie 240 # an exception to the window from which it originated. (I couldn't 241 # find an example of tkinter software displaying a gui exception on 242 # the originating window.)
243 -def _tkinter_report_exception(widget):
244 exc, val, tb = sys.exc_type, sys.exc_value, sys.exc_traceback 245 msg = "(%s) %s"%(exc.__name__,val) 246 # If the supplied widget has no master, it's probably the Tk 247 # instance. In that case, resort to the 'last-one-set' hack (see 248 # CEBALERT "provide a way of allowing other gui components" in 249 # topo/param/tk.py). 250 if not widget.master: 251 widget = tk._last_one_set 252 253 stat = None 254 255 while (widget is not None and widget.master): 256 # CEBALERT: should rename all status bars to the same thing 257 # (status_bar) 258 if hasattr(widget,'status'): 259 stat = widget.status 260 break 261 elif hasattr(widget,'messageBar'): 262 stat = widget.messageBar 263 break 264 widget = widget.master 265 266 if stat is not None: 267 stat.error('%s'%msg) 268 else: 269 topo.guimain.messageBar.error('%s'%msg) 270 271 param.Parameterized().warning(msg)
272 #import traceback; traceback.print_exc() 273 274 275 import Tkinter 276
277 -class TopoConsole(tk.AppWindow,tk.TkParameterized):
278 """ 279 Main window for the Tk-based GUI. 280 """ 281
282 - def _getmenubar(self):
283 return self.master.menubar
284 285 menubar = property(_getmenubar) 286
287 - def __getitem__(self,menu_name):
288 """Allow dictionary-style access to the menu bar.""" 289 return self.menubar[menu_name]
290
291 - def __init__(self,root,**params):
292 tk.AppWindow.__init__(self,root,status=True) 293 tk.TkParameterized.__init__(self,root,**params) 294 295 # Instead of displaying tracebacks on the commandline, try to display 296 # them on the originating window. 297 # CEBALERT: on destroy(), ought to revert this 298 Tkinter.Misc._report_exception=_tkinter_report_exception 299 300 self.auto_refresh_panels = [] 301 self._init_widgets() 302 self.title(topo.sim.name) # If -g passed *before* scripts on commandline, this is useless. 303 # So topo.misc.commandline sets the title as its last action (if -g) 304 305 306 307 # catch click on the 'x': offers choice to quit or not 308 self.protocol("WM_DELETE_WINDOW",self.quit_topographica) 309 310 311 ########## 312 ### Make cascade menus open automatically on linux when the mouse 313 ### is over the menu title. 314 ### [Tkinter-discuss] Cascade menu issue 315 ### http://mail.python.org/pipermail/tkinter-discuss/2006-August/000864.html 316 if topo.tkgui.system_platform is 'linux': 317 activate_cascade = """\ 318 if {[%W cget -type] != {menubar} && [%W type active] == {cascade}} { 319 %W postcascade active 320 } 321 """ 322 self.bind_class("Menu", "<<MenuSelect>>", activate_cascade) 323 ########## 324 325 # Install warning and message handling 326 from param.parameterized import Parameterized 327 self.__orig_P_warning = Parameterized.warning 328 self.__orig_P_message = Parameterized.message 329 type.__setattr__(Parameterized,'warning',self.gui_warning) 330 type.__setattr__(Parameterized,'message',self.gui_message)
331
332 - def gui_warning(self,*args):
333 stat = self.__get_status_bar() 334 s = string.join(args,' ') 335 stat.warn(s) 336 self.__orig_P_warning(self,*args)
337
338 - def gui_message(self,*args):
339 stat = self.__get_status_bar() 340 s = string.join(args,' ') 341 stat.message(s) 342 self.__orig_P_message(self,*args)
343 344
345 - def title(self,t=None):
346 newtitle = "Topographica" 347 if t: newtitle+=": %s" % t 348 tk.AppWindow.title(self,newtitle)
349 350
351 - def _init_widgets(self):
352 353 ## CEBALERT: now we can have multiple operations at the same time, 354 ## status bar could be improved to show all tasks? 355 356 # CEBALERT 357 self.messageBar = self.status 358 359 self.some_area = DockManager(self) 360 self.some_area.pack(fill="both", expand=1) 361 362 ### Balloon, for pop-up help 363 self.balloon = tk.Balloon(self.content) 364 365 ### Top-level (native) menu bar 366 #self.menubar = tk.ControllableMenu(self.content) 367 self.configure(menu=self.menubar) 368 369 #self.menu_balloon = Balloon(topo.tkgui.root) 370 371 # no menubar in tile yet 372 # http://news.hping.org/comp.lang.tcl.archive/4679.html 373 374 self.__simulation_menu() 375 self.__create_plots_menu() 376 self.refresh_plots_menu() 377 self.__help_menu() 378 379 380 ### Running the simulation 381 run_frame = Frame(self.content) 382 run_frame.pack(side='top',fill='x',padx=4,pady=8) 383 384 self.run_frame = run_frame 385 386 Label(run_frame,text='Run for: ').pack(side=LEFT) 387 388 self.run_for_var=DoubleVar() 389 self.run_for_var.set(1.0) 390 391 run_for = tk.TaggedSlider(run_frame, 392 variable=self.run_for_var, 393 tag_width=11, 394 slider_length=150, 395 bounds=(0,20000)) 396 self.balloon.bind(run_for,"Duration to run the simulation, e.g. 0.0500, 1.0, or 20000.") 397 run_for.pack(side=LEFT,fill='x',expand=YES) 398 run_for.tag.bind("<Return>",self.run_simulation) 399 400 # When return is pressed, the TaggedSlider updates itself...but we also want to run 401 # the simulation in this case. 402 run_frame.optional_action=self.run_simulation 403 404 go_button = Button(run_frame,text="Go", 405 command=self.run_simulation) 406 go_button.pack(side=LEFT) 407 408 self.balloon.bind(go_button,"Run the simulation for the specified duration.") 409 410 self.step_button = Button(run_frame,text="Step",command=self.run_step) 411 self.balloon.bind(self.step_button,"Run the simulation through the time at which the next events are processed.") 412 self.step_button.pack(side=LEFT) 413 414 self.sizeright()
415 416
417 - def __simulation_menu(self):
418 """Add the simulation menu options to the menubar.""" 419 simulation_menu = ControllableMenu(self.menubar,tearoff=0) 420 421 self.menubar.add_cascade(label='Simulation',menu=simulation_menu) 422 423 simulation_menu.add_command(label='Run script',command=self.run_script) 424 simulation_menu.add_command(label='Save script',command=self.save_script_repr) 425 simulation_menu.add_command(label='Load snapshot',command=self.load_snapshot) 426 simulation_menu.add_command(label='Save snapshot',command=self.save_snapshot) 427 #simulation_menu.add_command(label='Reset',command=self.reset_network) 428 simulation_menu.add_command(label='Test Pattern',command=self.open_test_pattern) 429 430 simulation_menu.add_command(label='Model Editor',command=self.open_model_editor) 431 simulation_menu.add_command(label='Quit',command=self.quit_topographica)
432 433 434
435 - def open_test_pattern(self):
437 438 439 440 441 442
443 - def __create_plots_menu(self):
444 """ 445 Add the plot menu to the menubar, with Basic plots on the menu itself and 446 others in cascades by category (the plots come from plotgroup_templates). 447 """ 448 plots_menu = ControllableMenu(self.menubar,tearoff=0) 449 self.menubar.add_cascade(label='Plots',menu=plots_menu)
450 451 452 # CEBALERT: should split other menus in same way as plots (create/refresh)
453 - def refresh_plots_menu(self):
454 plots_menu = self['Plots'] 455 plots_menu.delete(0,'end') 456 457 # create menu entries, and get list of categories 458 entries=KeyedList() # keep the order of plotgroup_templates (which is also KL) 459 categories = [] 460 for label,plotgroup in plotgroups.items(): 461 entries[label] = PlotsMenuEntry(plotgroup) 462 categories.append(plotgroup.category) 463 categories = sorted(set(categories)) 464 465 466 # The Basic category items appear on the menu itself. 467 assert 'Basic' in categories, "'Basic' is the category for the standard Plots menu entries." 468 for label,entry in entries: 469 if entry.plotgroup.category=='Basic': 470 plots_menu.add_command(label=label,command=entry.__call__) 471 472 categories.remove('Basic') 473 474 plots_menu.add_separator() 475 476 # Add the other categories to the menu as cascades, and the plots of each category to 477 # their cascades. 478 for category in categories: 479 category_menu = ControllableMenu(plots_menu,tearoff=0) 480 plots_menu.add_cascade(label=category,menu=category_menu) 481 482 # could probably search more efficiently than this 483 for label,entry in sorted(entries): 484 if entry.plotgroup.category==category: 485 category_menu.add_command(label=label,command=entry.__call__) 486 487 488 plots_menu.add_separator() 489 490 plots_menu.add_command(label="Help",command=(lambda x=plotting_help_locations: self.open_location(x)))
491 492
493 - def __help_menu(self):
494 """Add the help menu options.""" 495 496 help_menu = ControllableMenu(self.menubar,tearoff=0,name='help') 497 self.menubar.add_cascade(label='Help',menu=help_menu) 498 499 help_menu.add_command(label='About',command=self.new_about_window) 500 help_menu.add_command(label="User Manual", 501 command=(lambda x=user_manual_locations: self.open_location(x))) 502 503 help_menu.add_command(label="Tutorials", 504 command=(lambda x=tutorials_locations: self.open_location(x))) 505 506 help_menu.add_command(label="Examples", 507 command=self.run_example_script) 508 509 help_menu.add_command(label="Reference Manual", 510 command=(lambda x=reference_manual_locations: self.open_location(x))) 511 512 help_menu.add_command(label="Topographica.org", 513 command=(lambda x=topo_www_locations: self.open_location(x))) 514 515 help_menu.add_command(label="Python documentation", 516 command=(lambda x=python_doc_locations: self.open_location(x)))
517 518 519 520
521 - def quit_topographica(self,check=True,exit_status=0):
522 """Quit topographica.""" 523 if not check or (check and tk.askyesno("Quit Topographica","Really quit?")): 524 self.destroy() 525 526 # matplotlib's tk backend starts its own Tk instances; we 527 # need to close these ourselves (at least to avoid error 528 # message about 'unusual termination' in Windows). 529 try: # not that there should be an error, but just in case... 530 import matplotlib._pylab_helpers 531 for figman in matplotlib._pylab_helpers.Gcf.get_all_fig_managers(): 532 figman.destroy() 533 except: 534 pass 535 536 print "Quit selected; exiting" 537 538 # Workaround for obscure problem on some UNIX systems 539 # as of 4/2007, probably including Fedora Core 5. 540 # On these systems, if Topographica is started from a 541 # bash prompt and then quit from the Tkinter GUI (as 542 # opposed to using Ctrl-D in the terminal), the 543 # terminal would suppress echoing of all future user 544 # input. stty sane restores the terminal to sanity, 545 # but it is not clear why this is necessary. 546 # For more info: 547 # http://groups.google.com/group/comp.lang.python/browse_thread/thread/68d0f33c8eb2e02d 548 if topo.tkgui.system_platform=="linux" and os.getenv('EMACS')!='t': 549 try: os.system("stty sane") 550 except: pass 551 552 # CEBALERT: there was no call to self.master.destroy() 553 sys.exit(exit_status)
554 555
556 - def run_script(self):
557 """ 558 Dialog to run a user-selected script 559 560 The script is exec'd in __main__.__dict__ (i.e. as if it were specified on the commandline.) 561 """ 562 script = askopenfilename(initialdir=normalize_path(),filetypes=SCRIPT_FILETYPES) 563 if script in ('',(),None): # (representing the various ways no script was selected in the dialog) 564 self.messageBar.response('Run canceled') 565 else: 566 execfile(script,__main__.__dict__) 567 self.messageBar.response('Ran ' + script) 568 sim_name_from_filename(script) 569 self.title(topo.sim.name)
570 571 # CEBALERT: duplicates most of run_script()
572 - def run_example_script(self):
573 574 575 script = askopenfilename(initialdir=topo.misc.genexamples.find_examples(), 576 filetypes=SCRIPT_FILETYPES) 577 578 if script in ('',(),None): # (representing the various ways no script was selected in the dialog) 579 self.messageBar.response('No example opened') 580 else: 581 execfile(script,__main__.__dict__) 582 self.messageBar.response('Ran ' + script) 583 sim_name_from_filename(script) 584 self.title(topo.sim.name)
585 586 587
588 - def save_script_repr(self):
589 script_name = asksaveasfilename(filetypes=SCRIPT_FILETYPES, 590 initialdir=normalize_path(), 591 initialfile=topo.sim.basename()+"_script_repr.ty") 592 593 if script_name: 594 topo.command.basic.save_script_repr(script_name) 595 self.messageBar.response('Script saved to ' + script_name)
596 597
598 - def load_snapshot(self):
599 """ 600 Dialog to load a user-selected snapshot (see topo.command.basic.load_snapshot() ). 601 """ 602 snapshot_name = askopenfilename(initialdir=normalize_path(),filetypes=SAVED_FILETYPES) 603 604 if snapshot_name in ('',(),None): 605 self.messageBar.response('No snapshot loaded.') 606 else: 607 self.messageBar.dynamicinfo('Loading snapshot (may take some time)...') 608 self.update_idletasks() 609 topo.command.basic.load_snapshot(snapshot_name) 610 self.messageBar.response('Loaded snapshot ' + snapshot_name) 611 self.title(topo.sim.name) 612 613 self.auto_refresh()
614 615
616 - def save_snapshot(self):
617 """ 618 Dialog to save a snapshot (see topo.command.basic.save_snapshot() ). 619 620 Adds the file extension .typ if not already present. 621 """ 622 snapshot_name = asksaveasfilename(filetypes=SAVED_FILETYPES, 623 initialdir=normalize_path(), 624 initialfile=topo.sim.basename()+".typ") 625 626 if snapshot_name in ('',(),None): 627 self.messageBar.response('No snapshot saved.') 628 else: 629 if not snapshot_name.endswith('.typ'): 630 snapshot_name = snapshot_name + SAVED_FILE_EXTENSION 631 632 self.messageBar.dynamicinfo('Saving snapshot (may take some time)...') 633 self.update_idletasks() 634 topo.command.basic.save_snapshot(snapshot_name) 635 self.messageBar.response('Snapshot saved to ' + snapshot_name)
636 637
638 - def auto_refresh(self):
639 """ 640 Refresh all windows in auto_refresh_panels. 641 642 Panels can add and remove themselves to the list; those in the list 643 will have their refresh() method called whenever this console's 644 autorefresh() is called. 645 """ 646 for win in self.auto_refresh_panels: 647 win.refresh() 648 649 self.set_step_button_state() 650 self.update_idletasks()
651 652 653 654 ### CEBERRORALERT: why doesn't updatecommand("display=True") for an 655 ### orientation preference map measurement work with the 656 ### hierarchical example? I guess this is the reason I thought the 657 ### updating never worked properly (or I really did break it 658 ### recently - or I'm confused)...
659 - def refresh_activity_windows(self):
660 """ 661 Update any windows with a plotgroup_key of 'Activity'. 662 663 Used primarily for debugging long scripts that present a lot of activity patterns. 664 """ 665 for win in self.auto_refresh_panels: 666 if win.plotgroup.name=='Activity' or win.plotgroup.name=='ProjectionActivity' : 667 win.refresh() 668 self.update_idletasks()
669 670 671 672
673 - def open_model_editor(self):
674 """Start the Model editor.""" 675 return ModelEditor(self)
676 677 678 679
680 - def new_about_window(self):
681 win = tk.AppWindow(self) 682 win.withdraw() 683 win.title("About Topographica") 684 text = Label(win,text=topo.about(display=False),justify=LEFT) 685 text.pack(side=LEFT) 686 win.deiconify()
687 #self.messageBar.message('state', 'OK') 688
689 - def open_location(self, locations):
690 """ 691 Try to open one of the specified locations in a new window of the default 692 browser. See webbrowser module for more information. 693 694 locations should be a tuple. 695 """ 696 # CB: could have been a list. This is only here because if locations is set 697 # to a string, it will loop over the characters of the string. 698 assert isinstance(locations,tuple),"locations must be a tuple." 699 700 for location in locations: 701 try: 702 existing_location = resolve_path(location) 703 webbrowser.open(existing_location,new=2,autoraise=True) 704 self.messageBar.response('Opened local file '+existing_location+' in browser.') 705 return ### 706 except: 707 pass 708 709 for location in locations: 710 if location.startswith('http'): 711 try: 712 webbrowser.open(location,new=2,autoraise=True) 713 self.messageBar.response('Opened remote location '+location+' in browser.') 714 return ### 715 except: 716 pass 717 718 self.messageBar.response("Could not open any of %s in a browser."%locations)
719 720 721 # CEBALERT: need to take care of removing old messages automatically? 722 # (Otherwise callers might always have to pass 'ok'.)
723 - def status_message(self,m):
724 self.messageBar.response(m)
725 726
727 - def run_simulation(self,event=None): # event=None allows use as callback
728 """ 729 Run the simulation for the duration specified in the 730 'run for' taggedslider. 731 """ 732 fduration = self.run_for_var.get() 733 self.open_progress_window(timer=topo.sim.timer) 734 topo.sim.run_and_time(fduration) 735 self.auto_refresh()
736 737 738 # CEBERRORALERT: Step button does strange things at time==0. 739 # E.g. for lissom_oo_or, nothing appears to happen. For 740 # hierarchical, runs to time==10.
741 - def run_step(self):
742 743 if not topo.sim.events: 744 # JP: step button should be disabled if there are no events, 745 # but just in case... 746 return 747 748 # JPALERT: This should really use .run_and_time() but it doesn't support 749 # run(until=...) 750 topo.sim.run(until=topo.sim.events[0].time) 751 self.auto_refresh()
752
753 - def set_step_button_state(self):
754 if topo.sim.events: 755 self.step_button.config(state=NORMAL) 756 else: 757 self.step_button.config(state=DISABLED)
758 759
760 - def __get_status_bar(self,i=2):
761 # Hack to find appropriate status bar: Go back through frames 762 # until a widget with a status bar is found, and return it. 763 try: 764 while True: 765 f = sys._getframe(i) 766 if hasattr(f,'f_locals'): 767 if 'self' in f.f_locals: 768 o = f.f_locals['self'] 769 # (temporary hack til ScrolledFrame cleaned up) 770 if o.__class__.__name__!='ScrolledFrame': 771 if hasattr(o,'messageBar'): 772 return o.messageBar 773 elif hasattr(o,'status'): 774 return o.status 775 i+=1 776 except: 777 pass 778 779 #print "GUI INTERNAL WARNING: failed to determine window on which to display message." 780 return self.messageBar
781 782
783 - def open_progress_window(self,timer,title=None):
784 """ 785 Provide a convenient link to progress bars. 786 """ 787 stat = self.__get_status_bar() 788 return stat.open_progress_window(timer=timer,sim=topo.sim)
789 790 791 792 # CEBALERT: of course dictionary access is used as an alternative to 793 # the config method or whatever it's called! So this could cause 794 # serious confusion to someone trying to set config options using the 795 # dictionary style access rather than .config()! Either document 796 # clearly or abandon, and get() and set() to access menu entries by 797 # name.
798 -class ControllableMenu(tk.Menu):
799 """ 800 A Menu, but where entries are accessible by name (using 801 dictionary-style access). 802 803 ** Not truly compatible with Tkinter; work in progress ** 804 """
805 - def __getitem__(self,name):
806 return self.named_commands[name]
807 808 809 810 if __name__ != '__main__': 811 plotpanel_classes['Connection Fields'] = ConnectionFieldsPanel 812 plotpanel_classes['RF Projection'] = RFProjectionPanel 813 plotpanel_classes['Projection'] = CFProjectionPanel 814 plotpanel_classes['Projection Activity'] = ProjectionActivityPanel 815