Package NetworkEditor :: Module simpleNE
[hide private]
[frames] | no frames]

Source Code for Module NetworkEditor.simpleNE

   1  ######################################################################### 
   2  # 
   3  # Date: Nov. 2001  Author: Michel Sanner, Daniel Stoffler 
   4  # 
   5  #    sanner@scripps.edu 
   6  #    stoffler@scripps.edu 
   7  # 
   8  #       The Scripps Research Institute (TSRI) 
   9  #       Molecular Graphics Lab 
  10  #       La Jolla, CA 92037, USA 
  11  # 
  12  # Copyright: Michel Sanner, Daniel Stoffler and TSRI 
  13  # 
  14  # revision: Guillaume Vareille 
  15  #   
  16  ######################################################################### 
  17  # 
  18  # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/simpleNE.py,v 1.172.2.4 2007/09/17 19:39:02 vareille Exp $ 
  19  # 
  20  # $Id: simpleNE.py,v 1.172.2.4 2007/09/17 19:39:02 vareille Exp $ 
  21  # 
  22   
  23   
  24   
  25  """ 
  26  This modules implements the classes NetworkBuilder. 
  27  An instance of a SimpleNetworkBuilder provides a Canvas to which NetworkNode 
  28  and NetworkConnections can be added to build networks. 
  29  The network maintains a list of selected nodes. 
  30  Node can have multiple ports. Connections know the 2 nodes they connect and 
  31  the port numbers on which they are connected. in a connection, node1 is always 
  32  the node from which the connection was initiated and is considered to be the 
  33  parent of node2. 
  34  Nodes maintain a dictionary of (connections:nodes) paires indicating which node 
  35  is conencted by which connection. 
  36   
  37  Every node has a computeFunction which can be run in a separate thread. 
  38  To run a node call the node's schedule method. This will place this node in 
  39  a thread safe queue. If the queue is started (see startQueueHandler, 
  40  stopQueueHandler), the node's compute function will be called as soon as 
  41  all parent nodes are not running. 
  42  For data flow, it is the node's compute function task to schedule it's 
  43  children node. 
  44   
  45  node addition and node connection, node selection and deselection, 
  46  connection selection and deselection 
  47   
  48  This file provides an implementation of 1 port networkNodes and multi-port 
  49  NetworkNodes as well as an implementation of NetworkConnection. 
  50   
  51  here is an example of how to use this: 
  52   
  53      # instanciate an editor 
  54      editor = NetworkBuilder('My Network Builder') 
  55   
  56      # instanciate a node 
  57      node1 = NetworkNode(name='node1') 
  58   
  59      # add this node to the editor 
  60      editor.addNode(node1, 100, 100) 
  61   
  62      # nodes have several methods tha can be used to alter its representation 
  63      node1.rename('operator1') 
  64      node1.highlight() 
  65      node1.unhighlight() 
  66       
  67      # create a second node 
  68      node2 = NetworkNode(name='node2') 
  69      editor.addNode(node2, 200, 200) 
  70   
  71      # connect these 2 nodes 
  72      conn1 = editor.connectNodes(node1, node2) 
  73   
  74      # connections are objects too that can be altered 
  75      conn1.highlight() 
  76      conn1.unhighlight() 
  77   
  78      # create a 3rd node and connect it using angular lines 
  79      node3 = NetworkNode(name='node3') 
  80      editor.addNode(node3, 300, 100) 
  81      conn2 = editor.connectNodes(node3, node2, mode='angles') 
  82   
  83      # create a 4th node and connect it with a spline 
  84      node4 = NetworkNode(name='node4') 
  85      editor.addNode(node4, 350, 270) 
  86      conn3 = editor.connectNodes(node2, node4, mode='angles', smooth=1) 
  87   
  88      # nodes can be moved 
  89      node1.move(200, 100) 
  90       
  91      # connections can update their representation to reflect node motion 
  92      conn1.updatePosition() 
  93   
  94      # nodes can be selected 
  95      editor.selectNodes([node1, node2, node3]) 
  96      editor.deselectNodes([node1, node4]) 
  97      editor.clearSelection() 
  98   
  99      # a sub graph can be moved 
 100      editor.moveSubGraph(editor.selectedNodes, 10, 20) 
 101   
 102      # nodes and connections can be deleted 
 103      editor.deleteNodes([node2]) 
 104   
 105      node5 = NetworkNode(name='node5') 
 106      editor.addNode(node5, 100, 200) 
 107      conn4 = editor.connectNodes(node1, node5, mode='angles') 
 108      editor.deleteConnections([conn4]) 
 109   
 110      # try nodes of type 2 
 111   
 112      n1 = NetworkNode2('test1') 
 113      ed.addNode(n1, 100, 350) 
 114      n1.addOutputPort() 
 115   
 116      from NetworkEditor.simpleNE import NetworkNode2 
 117      n2 = NetworkNode2('test2') 
 118      ed.addNode(n2, 300, 350) 
 119      n2.addInputPort() 
 120   
 121      from NetworkEditor.simpleNE import NetworkConnection 
 122      ed.connectNodes(n1, n2, 2, 1) 
 123   
 124      ed.connectNodes(node5, n2, 0, 2) 
 125      ed.selectNodes([n2]) 
 126      ed.moveSubGraph(ed.selectedNodes, 10, 20) 
 127   
 128  """ 
 129   
 130  ## TODO/bugs 
 131  ## 
 132  #- GUI to expose options (default Connection mode, default lineOptions) 
 133  #- GUI to expose bindings in InteractiveNetworkBuilder 
 134   
 135  #DONE 
 136  #- Create MacroNode, expand/shrink MacroNode 
 137  #- copy sub-graph 
 138  #- Multi-port nodes: resize node when adding ports 
 139  #- save/load network 
 140  #- gain access to idle and idlelib as a package 
 141   
 142  #FIXME: 
 143  # undo is broken 
 144  # hyperbolic scaling is broken (this is not really important, but anyways) 
 145   
 146   
 147  import sys, os 
 148  import warnings 
 149  import Tkinter, Pmw, tkFileDialog 
 150  import traceback 
 151  import string 
 152  import re 
 153  import thread 
 154  import weakref 
 155  import user 
 156   
 157  from mglutil.util.idleUtil import getShell 
 158   
 159  from tkSimpleDialog import askstring 
 160  from tkMessageBox import askokcancel, showwarning 
 161  from SimpleDialog import SimpleDialog 
 162   
 163  from mglutil.util.callback import CallBackFunction 
 164  from mglutil.util.callback import CallbackManager 
 165  from mglutil.util.packageFilePath import findResourceFile 
 166   
 167  from NetworkEditor.items import NetworkNode, NetworkConnection, NetworkNodeBase 
 168  from NetworkEditor.datatypes import TypeManager 
 169  from NetworkEditor.widgets import widgetsTable, PortWidget 
 170  from NetworkEditor.net import Network 
 171  from NetworkEditor.customizedWidgets import mglNoteBook 
 172  from NetworkEditor.macros import MacroNetwork 
 173  from NetworkEditor.itemBase import UserPanel 
 174  from mglutil.util.recentFiles import RecentFiles 
 175  from mglutil.util.packageFilePath import getResourceFolderWithVersion 
 176   
 177  import Vision 
 178  if hasattr( Vision, 'networkDefaultDirectory') is False: 
 179      Vision.networkDefaultDirectory = user.home 
 180   
 181   
182 -class EditorBase:
183 """Base class for a network builder. Note: all GUI-independent stuff 184 from NetworkBuilder should move to here!""" 185 186
187 - def __init__(self):
188 self._nodesID = 0 # used to assign a node a unique number
189 # in a given network. This is incremented 190 # in the addNodes() method 191 192 193
194 -class NetworkBuilder(Tkinter.Frame, EditorBase):
195 """ 196 This class implements a simple network builder that provides a Canvas 197 to which NetworkNodes can be added. These nodes can be connected by 198 NetworkConnections. 199 The builder stored Nodes and connections in 2 dictionaries where the key is a 200 unique tag (associated with the geometry in the Canvas) and the value is the 201 instance of either the node or the connection. 202 The Builder also maintains a list of currently selected nodes. 203 """ 204
205 - def __init__(self, name='NoName', master=None, font=None, 206 withShell=1, **kw):
207 """instance <- NetworkBuilder(name='NoName', master=None, font=None, 208 withShell=1, **kw) 209 master can be a Frame in which the editor has to be packed 210 withShell=0 will prevent the creation of an Idle shell (since the 211 default shell is used) 212 kw can contain the following named arguments: 213 visibleWidth, visibleHeight, 214 totalWidth, totalHeight 215 any legal keyword for a Canvas 216 """ 217 218 EditorBase.__init__(self) 219 220 if kw.has_key('visibleWidth'): # size of scrollable Canvas 221 self.visibleWidth = kw['visibleWidth'] 222 del kw['visibleWidth'] 223 else: 224 self.visibleWidth = 400 225 226 if kw.has_key('visibleHeight'): # size of scrollable Canvas 227 self.visibleHeight = kw['visibleHeight'] 228 del kw['visibleHeight'] 229 else: 230 self.visibleHeight= 400 231 232 if kw.has_key('totalWidth'): # Total size of Canvas 233 self.totalWidth = kw['totalWidth'] 234 del kw['totalWidth'] 235 else: 236 self.totalWidth = 4000 237 238 if kw.has_key('totalHeight'): # Total size of Canvas 239 self.totalHeight = kw['totalHeight'] 240 del kw['totalHeight'] 241 else: 242 self.totalHeight = 4000 243 244 Tkinter.Frame.__init__(self, master) 245 246 # FIXME we should catch whatever exception is thrown if self has no 247 # option_readfile method and say something meaningful 248 # maybe the name and location of the file should be setable in _vprc 249 try: 250 if os.path.exists('./Tkinter.defaults'): 251 self.option_readfile('./Tkinter.defaults') 252 except: 253 pass 254 255 self.font = {} # dict holding font configuration for various GUI 256 # elements. Now set the defaults 257 self.font['Menus'] = ('Helvetica', 8, 'bold') 258 self.font['LibTabs'] = ('Helvetica', 8, 'bold') 259 self.font['Categories'] = ('Helvetica', 8, 'bold') 260 self.font['LibNodes'] = ('Helvetica', 8, 'normal') 261 self.font['NetTabs'] = ('Helvetica', 8, 'bold') 262 self.font['Nodes'] = ('Helvetica', 8, 'normal') 263 self.font['Root'] = ('Helvetica', 8, 'normal') 264 265 if font is not None: # override default if user specified 266 # kw font can be either ['FONT] or ['FONT', SIZE] or 267 # ['FONT', SIZE, 'STYLE'] 268 ft = font[0] 269 sz = 8 270 sty = 'normal' 271 272 if len(font) == 2: 273 sz = font[1] 274 elif len(font) == 3: 275 sz = font[1] 276 sty = font[2] 277 278 fontList = (ft, sz, sty) 279 for k in self.font.keys(): 280 self.font[k] = fontList 281 282 # define self.root as the toplevel in which the editor is 283 root = self.master 284 while not isinstance(root, Tkinter.Toplevel) and not isinstance(root, Tkinter.Tk): 285 root = root.master 286 self.root = root 287 288 self.root.option_add('*font',self.font['Root']) 289 290 self.withShell = withShell # set to one is an IDLE shell is created 291 292 #self.master.option_readfile('NetworkEditor/Tkinter.defaults') 293 294 self.name = name # EditorName 295 296 self.eventHandler = {} # stores callback managers for network events 297 self.createCallbackManager('addNetwork') 298 self.createCallbackManager('deleteNetwork') 299 300 handle = self.eventHandler['addNetwork'] 301 handle.AddCallback(self.handleAddNetwork) 302 handle = self.eventHandler['deleteNetwork'] 303 handle.AddCallback(self.handleDeleteNetwork) 304 305 self.isFrozenTk = Tkinter.IntVar() 306 self.isFrozenTk.set(0) 307 308 threads = 0 # default is 0: do not run multi-threaded scheduling 309 self.withThreads = threads 310 self.withThreadsTk = Tkinter.IntVar() 311 self.withThreadsTk.set(threads) 312 313 self.mainThread = thread.get_ident() #used for idle shell 314 315 self.colorNodeByLibrary = 0 316 self.colorNodeByLibraryTk = Tkinter.IntVar() 317 self.colorNodeByLibraryTk.set(self.colorNodeByLibrary) 318 319 self.flashNodesWhenRun = 1 320 self.flashNodesWhenRunTk = Tkinter.IntVar() 321 self.flashNodesWhenRunTk.set(self.flashNodesWhenRun) 322 323 self.splineConnections = 0 324 self.splineConnectionsTk = Tkinter.IntVar() 325 self.splineConnectionsTk.set(self.splineConnections) 326 327 ## USED TO FLASH NETWORK ONCE ONLY 328 ## self.flashNetworkWhenRun = 1 329 ## self.flashNetworkWhenRunTk = Tkinter.IntVar() 330 ## self.flashNetworkWhenRunTk.set(self.flashNetworkWhenRun) 331 332 self.verbose = 0 # when set to 1 to see prints in stdout 333 self.verboseTk = Tkinter.IntVar() 334 self.verboseTk.set(self.verbose) 335 336 self.balloons = Pmw.Balloon(master, yoffset=0) 337 338 self.typeManager = TypeManager() 339 self.undoStack = [] 340 self.currentNetwork = None 341 342 Tkinter.Pack.config(self, expand=1, fill='both') 343 344 # register callback to keep track of window width 345 # so that we can resize the module libraries 346 self.bind('<Configure>', self.resize_cb, '+') 347 348 self.createMenus(self) 349 self.setFont("Menus", self.font['Menus']) 350 #self.menuButtons['Edit'].menu.invoke("flash Nodes When Run") 351 #self.menuButtons['Edit'].menu.invoke("restore Widget Value") 352 353 self.private_pasteBuffer = None # textual description of sub-network 354 # in copy buffer 355 356 self._tmpListOfSavedNodes = {} # used for saving networks or source 357 358 self.networkAreaF = Pmw.ScrolledFrame(self, 359 borderframe=1, horizscrollbar_width=7, vscrollmode='none', 360 frame_relief='flat', 361 frame_borderwidth=0, horizflex='fixed', 362 vertflex='elastic', 363 frame_width=self.visibleWidth, 364 frame_height=self.visibleHeight) 365 self.networkAreaF.pack(expand=1, fill='both') 366 367 self.networkArea = mglNoteBook(self.networkAreaF.interior(), 368 raisecommand=self.selectNetwork) 369 self.networkArea.pack(fill='both', expand=1, padx=1, pady=1) 370 371 # create the Python shell 372 if self.withShell: 373 self.pyshell = getShell(self.mainThread, rootTk = self.root, 374 enable_shell=True, enable_edit=False, 375 debug=False) 376 self.pyshell.menubar.children['file'].delete('Close','Exit') 377 self.pyshell.menubar.children['file'].add_command( 378 label='Clear Ouput', command=self.clearPyShell ) 379 # hide the Python shell 380 self.pyshell.top.withdraw() 381 self.pyshell.begin() 382 ### ???? what is this ? 383 Tkinter._default_root = self.root 384 self.pyshell.top.protocol('WM_DELETE_WINDOW', 385 self.togglePythonShell) 386 387 self.networks = {} # dictionary of networks (name:networkObject) 388 389 self.uniqNetworkNumber = 0 390 391 # add a default network 392 net = Network('Network 0') 393 self.addNetwork( net ) 394 # make it the current one 395 self.setNetwork( net ) 396 #net.buildIcons() 397 398 self.nbPaste = 1 399 self.resizeNetworkArea()
400 401
402 - def panelFromName(self, name):
403 return self.currentNetwork.userPanels.get(name, None)
404 405
406 - def createUserPanel_cb(self, event=None):
407 defaultName = 'Panel_%d'%len(self.currentNetwork.userPanels.keys()) 408 name = askstring('Panel Name', "Panel Name", initialvalue=defaultName) 409 while name in self.currentNetwork.userPanels.keys(): 410 name = askstring('Panel Name alread used, New value', "Panel Name", 411 initialvalue=defaultName) 412 if name is None or len(name) == 0: 413 return 414 self.currentNetwork.createUserPanel(name)
415 416
417 - def createUserPanel(self, name, **kw):
418 apply(self.currentNetwork.createUserPanel, (name,), kw)
419 420
421 - def deleteUserPanel_cb(self):
422 423 def internal_cb(name): 424 self.funcCB = CallBackFunction(self.currentNetwork.deleteUserPanel, name)
425 426 def external_cb(): 427 master.destroy() 428 self.funcCB()
429 430 if len(self.currentNetwork.userPanels) > 0: 431 master = Tkinter.Toplevel() 432 master.title('Panel deletion') 433 434 name = self.currentNetwork.userPanels.keys()[0] 435 self.funcCB = CallBackFunction(self.currentNetwork.deleteUserPanel, name) 436 437 lComboBoxPanels = Pmw.ComboBox( 438 master, 439 label_text='Choose the panel to delete', 440 labelpos='n', 441 entryfield_value=name, 442 scrolledlist_items=self.currentNetwork.userPanels.keys(), 443 selectioncommand=internal_cb 444 ) 445 lComboBoxPanels.pack(side='top', fill='x') 446 447 buttonsFrame = Tkinter.Frame(master) 448 buttonOk = Tkinter.Button( buttonsFrame, 449 text='OK', 450 command=external_cb) 451 buttonOk.pack(side='left', expand=1, fill='x') 452 buttonCancel = Tkinter.Button( buttonsFrame, text='Cancel', command=master.destroy) 453 buttonCancel.pack(side='left', expand=1, fill='x') 454 buttonsFrame.pack(side='top', fill='x') 455 456
457 - def getNetworkByName(self, name):
458 """networkList <- getNetworkByName(self, name) 459 Return all the network who's name match the name regular expression 460 """ 461 nets = [] 462 import re 463 reg = re.compile(name) 464 for netName, net in self.networks.items(): 465 if reg.match(netName): 466 nets.append(net) 467 return nets
468 469
470 - def getNodeByName(self, name):
471 """nodeList <- getNodeByName(self, name) 472 Return all the nodes from the current network who's name match the name 473 regular expression 474 """ 475 return self.currentNetwork.getNodeByName(name)
476 477
478 - def resize_cb(self, event=None):
479 if self.visibleWidth != event.width: 480 curwidth = self.visibleWidth 481 self.visibleWidth = event.width 482 # resize networkArea only if window got larger 483 if curwidth < event.width: 484 self.resizeNetworkArea()
485 486
487 - def resizeNetworkArea(self):
488 # compute how much width the individual panes get 489 if self.winfo_width() == 1: # first time of instanciation 490 self.update_idletasks() 491 self.networkArea.configure(hull_width=self.visibleWidth-6, 492 hull_height=self.visibleHeight)
493 494
495 - def clearPyShell(self, event=None):
496 if self.withShell: 497 self.pyshell.text.delete("1.0", "end-1c")
498 499
500 - def createCallbackManager(self, event):
501 assert not self.eventHandler.has_key(event) 502 self.eventHandler[event] = CallbackManager()
503 504
505 - def addCallbackManager(self, event, func, doc, **kw):
506 assert self.eventHandler.has_key(event) 507 assert callable(func) 508 509 self.eventHandler[event].AddCallback(func)
510 511
512 - def showGUI(self):
513 self.root.deiconify()
514 515
516 - def hideGUI(self):
517 self.root.withdraw()
518 519
520 - def toggleGuiVisibility(self):
521 if self.root.winfo_ismapped(): 522 self.hideGUI() 523 else: 524 self.showGUI()
525 526
527 - def setFont(self, *args):
528 msg = """Usage: self.setFont(<itemname>, <fontconfig>) 529 For Example: self.setFont('Menus', ('Helvetica', 9, 'bold') 530 Legal itemnames: 'All' 531 'Menus' 532 'NetTabs' 533 'Nodes' 534 'Root'""" 535 536 537 if args is None or args == () or len(args) != 2: 538 return 539 540 else: 541 tag = args[0] 542 font = args[1] 543 assert len(font) == 3 544 font = tuple(font) 545 546 if tag == 'Menus' or tag == 'All': 547 self.font['Menus'] = font 548 for menu in self.menuButtons.keys(): 549 # set menu button font 550 self.menuButtons[menu].configure(font=font) 551 # set cascading menu font 552 self.menuButtons[menu].menu.configure(font=font) 553 554 if tag == 'NetTabs' or tag == 'All': 555 self.font['NetTabs'] = font 556 for tab in self.networkArea.pagenames(): 557 netTab = self.networkArea.tab(tab) 558 netTab.configure(font=font) 559 # get info to resize the tab 560 width = netTab.winfo_reqwidth() 561 height = netTab.winfo_reqheight() 562 self.networkArea._pageAttrs[tab]['tabreqwidth'] = width 563 self.networkArea._pageAttrs[tab]['tabreqheight'] = height 564 # resize all tabs 565 self.networkArea._pending['tabs'] = 1 566 self.networkArea._layout() 567 568 if tag == 'Nodes' or tag == 'All': 569 self.font['Nodes'] = font 570 # set balloon for ports: 571 self.balloons.component('label').configure(font=font) 572 for net in self.networks.values(): 573 for node in net.nodes: 574 node.iconMaster.itemconfigure(node.textId, font=font) 575 576 # call refresh to rebuild nodes 577 self.refreshNet_cb(net) 578 579 if tag == 'Root' or tag == 'All': 580 self.font['Root'] = font 581 self.root.option_add('*font',font)
582 583
584 - def interactiveExit_cb(self, event=None):
585 msg = askokcancel("Quit","Are you sure you want to quit?") 586 if msg: 587 self.exit_cb(event)
588 589
590 - def exit_cb(self, event=None):
591 # Note: do not name this method 'exit', else Tkinter will call it 592 # too and the you get red ink upon exiting! 593 from NetworkEditor.macros import MacroNetwork 594 for net in self.networks.values(): 595 if not isinstance(net, MacroNetwork): 596 self.deleteNetwork(net) 597 if self.withShell: 598 self.pyshell.close() 599 self.root.destroy() 600 self.root.quit() 601 self.root = None 602 if type(self.withShell) != int: 603 sys.exit(0)
604 605
606 - def handleSelectionChangeMenus(self):
607 """This is called by callback manager on event select or deselect 608 nodes and turns on/off menu entries and menu buttons.""" 609 610 net = self.currentNetwork 611 menu = self.menuButtons['Edit'].menu 612 613 if len(net.selectedNodes): 614 menu.entryconfig("Cut", state=Tkinter.NORMAL) 615 menu.entryconfig("Copy", state=Tkinter.NORMAL) 616 #menu.entryconfig("Delete", state=Tkinter.NORMAL) 617 else: 618 menu.entryconfig("Cut", state=Tkinter.DISABLED) 619 menu.entryconfig("Copy", state=Tkinter.DISABLED) 620 #menu.entryconfig("Delete", state=Tkinter.DISABLED) 621 622 if self.private_pasteBuffer: 623 menu.entryconfig("Paste", state=Tkinter.NORMAL) 624 else: 625 menu.entryconfig("Paste", state=Tkinter.DISABLED)
626 627
628 - def handleAddNetwork(self):
629 """This is called by callback manager on the event add network and 630 turns on menu entries.""" 631 632 menuF = self.menuButtons['File'].menu 633 menuE = self.menuButtons['Edit'].menu 634 menuN = self.menuButtons['Networks'].menu 635 636 menuF.entryconfig("New...", state=Tkinter.NORMAL) 637 menuF.entryconfig("Open...", state=Tkinter.NORMAL) 638 menuF.entryconfig("Merge...", state=Tkinter.NORMAL) 639 menuF.entryconfig("Close...", state=Tkinter.NORMAL) 640 menuF.entryconfig("Save...", state=Tkinter.NORMAL) 641 menuF.entryconfig("Print", state=Tkinter.NORMAL) 642 menuF.entryconfig("Quit", state=Tkinter.NORMAL) 643 644 menuE.entryconfig("Select All", state=Tkinter.NORMAL) 645 menuE.entryconfig("Create Macro", state=Tkinter.NORMAL) 646 menuE.entryconfig("Create User Panel", state=Tkinter.NORMAL) 647 menuE.entryconfig("Delete User Panel", state=Tkinter.NORMAL) 648 649 menuN.entryconfig("Rename...", state=Tkinter.NORMAL) 650 menuN.entryconfig("Close...", state=Tkinter.NORMAL) 651 menuN.entryconfig("Run", state=Tkinter.NORMAL) 652 menuN.entryconfig("Refresh", state=Tkinter.NORMAL) 653 menuN.entryconfig("Reset Cache", state=Tkinter.NORMAL)
654 655
656 - def handleDeleteNetwork(self, **kw):
657 """This is called by callback manager on the event delete network and 658 turns on menu buttons.""" 659 menuF = self.menuButtons['File'].menu 660 menuE = self.menuButtons['Edit'].menu 661 menuN = self.menuButtons['Networks'].menu 662 663 if self.currentNetwork is None: 664 menuF.entryconfig("New...", state=Tkinter.NORMAL) 665 menuF.entryconfig("Open...", state=Tkinter.NORMAL) 666 menuF.entryconfig("Merge...", state=Tkinter.DISABLED) 667 menuF.entryconfig("Close...", state=Tkinter.DISABLED) 668 menuF.entryconfig("Save...", state=Tkinter.DISABLED) 669 menuF.entryconfig("Print", state=Tkinter.DISABLED) 670 menuF.entryconfig("Quit", state=Tkinter.NORMAL) 671 672 menuE.entryconfig("Select All", state=Tkinter.DISABLED) 673 menuE.entryconfig("Create Macro", state=Tkinter.DISABLED) 674 menuE.entryconfig("Create User Panel", state=Tkinter.DISABLED) 675 menuE.entryconfig("Delete User Panel", state=Tkinter.DISABLED) 676 677 menuN.entryconfig("Rename...", state=Tkinter.DISABLED) 678 menuN.entryconfig("Run", state=Tkinter.DISABLED) 679 menuN.entryconfig("Refresh", state=Tkinter.DISABLED) 680 menuN.entryconfig("Reset Cache", state=Tkinter.DISABLED)
681 682
683 - def copyNetwork_cb(self, event=None):
684 # reset keyboard modifier 685 # Note: we do that in case this method raises an exception. If we 686 # do not call modifierUp() we do not release the focus and Python 687 # is basically locking the screen until the process is killed. 688 if event and event.keysym == 'c': 689 event.keysym = "Control_L" 690 self.currentNetwork.modifierUp(event) 691 event.keysym = "Control_R" 692 self.currentNetwork.modifierUp(event) 693 694 # save the source code for the sub-network corresponding to the 695 # current selection 696 net = self.currentNetwork 697 if len(net.selectedNodes)==0: 698 self.private_pasteBuffer = None 699 # disable paste 700 net.eventHandler['onSelectionChange'].CallCallbacks() 701 return 702 703 curNet = self.currentNetwork 704 # prevent cut/copy of macroinput and macrooutput node 705 # by deselecting them 706 from macros import MacroNetwork 707 if isinstance(curNet, MacroNetwork): 708 curNet.deselectNodes([curNet.ipNode, curNet.opNode]) 709 710 # pass ignoreOriginal=True so that we can copy nodes in macro networks 711 # that came from a node library (where all nodes are marked original) 712 self.private_pasteBuffer = curNet.getNetworkCreationSourceCode( 713 selectedOnly=1, withRun=False, ignoreOriginal=True) 714 self.private_selectionbb = self.currentNetwork.canvas.bbox('selected') 715 # enable paste 716 curNet.eventHandler['onSelectionChange'].CallCallbacks() 717 self.nbPaste = 1
718 719
720 - def cutNetwork_cb(self, event=None):
721 # reset keyboard modifier 722 # Note: we do that in case this method raises an exception. If we 723 # do not call modifierUp() we do not release the focus and Python 724 # is basically locking the screen until the process is killed. 725 if event and event.keysym == 'x': 726 event.keysym = "Control_L" 727 self.currentNetwork.modifierUp(event) 728 event.keysym = "Control_R" 729 self.currentNetwork.modifierUp(event) 730 731 # save the source code for the sub-network corresponding to the 732 # current selection 733 net = self.currentNetwork 734 self.copyNetwork_cb(event) 735 # Note: if Macro Input/Output nodes were in the selection, the 736 # copyNetwork_cb will automatically deselect them (see above) 737 net.deleteNodes(net.selectedNodes) 738 # disable CUT and COPY 739 self.currentNetwork.eventHandler['onSelectionChange'].CallCallbacks()
740 741
742 - def delete(self, event=None):
743 self.currentNetwork.deleteNodes(self.currentNetwork.selectedNodes[:])
744 745
746 - def freezeNetwork(self):
747 self.isFrozenTk.set(1) 748 self.currentNetwork.freeze(updateGUI=1)
749 750
751 - def unfreezeNetwork(self):
752 self.isFrozenTk.set(0) 753 self.currentNetwork.unfreeze(updateGUI=1)
754 755
756 - def toggleFreezeNetwork_cb(self, event=None):
757 if self.isFrozenTk.get() == 0: 758 self.freezeNetwork() 759 else: 760 self.unfreezeNetwork()
761 762
763 - def saveNetwork(self, filename, hide=False):
764 765 lines = [] 766 767 if len(self.currentNetwork.userPanels) > 0: 768 #lines += ['#!%s\n'%sys.executable] 769 lines += ['#!/bin/ksh ~/.mgltools/pythonsh\n'] 770 771 try: 772 lines += self.currentNetwork.getNetworkCreationSourceCode(copyright=True) 773 774 f = open(filename, 'w') 775 map( f.write, lines ) 776 f.close() 777 778 if len(self.currentNetwork.userPanels) > 0: 779 os.chmod(filename, 0777) 780 781 self.currentNetwork._modified = False 782 self.currentNetwork.filename = os.path.abspath(filename) 783 784 except Exception, e: 785 warnings.warn('save Network failed ! %s'%e)
786 787
788 - def saveNetwork_cb(self, event=None, hide=False):
789 # reset keyboard modifier 790 # Note: we do that in case this method raises an exception. If we 791 # do not call modifierUp() we do not release the focus and Python 792 # is basically locking the screen until the process is killed. 793 if event and event.keysym == 's': 794 event.keysym = "Control_L" 795 self.currentNetwork.modifierUp(event) 796 event.keysym = "Control_R" 797 self.currentNetwork.modifierUp(event) 798 799 # save network 800 from macros import MacroNetwork 801 if isinstance(self.currentNetwork, MacroNetwork): 802 ans = showwarning('Warning', 'This network is inside a macro!') 803 return 804 name = string.replace(self.currentNetwork.name," ", "") 805 806 if self.currentNetwork.filename is not None: 807 lNetworkDir = os.path.dirname(self.currentNetwork.filename) 808 else: 809 lNetworkDir = Vision.networkDefaultDirectory 810 811 if self.libraries.has_key('Pmv'): 812 file = tkFileDialog.asksaveasfilename(parent = self, 813 initialdir = lNetworkDir, 814 filetypes=[('pmv network', '*_pmvnet.py'), 815 ('network', '*_net.py'), 816 ('all', '*')], 817 title='Save Network', 818 initialfile=name+"_pmvnet.py") 819 else: 820 file = tkFileDialog.asksaveasfilename(parent = self, 821 initialdir = lNetworkDir, 822 filetypes=[('network', '*_net.py'), 823 ('pmv network', '*_pmvnet.py'), 824 ('all', '*')], 825 title='Save Network', 826 initialfile=name+"_net.py") 827 if file: 828 self.saveNetwork(file, hide=hide) 829 # and also rename the network tab 830 if file.endswith('_pmvnet.py'): 831 networkName = os.path.basename(file)[:-10] 832 else: 833 networkName = os.path.basename(file)[:-7] 834 self.currentNetwork.rename(networkName) 835 self.recentFiles.add(file, 'loadNetwork')
836
837 - def pasteNetwork_cb(self, event=None):
838 # reset keyboard modifier 839 # Note: we do that in case this method raises an exception. If we 840 # do not call modifierUp() we do not release the focus and Python 841 # is basically locking the screen until the process is killed. 842 if event and event.keysym == 'v': 843 event.keysym = "Control_L" 844 self.currentNetwork.modifierUp(event) 845 event.keysym = "Control_R" 846 self.currentNetwork.modifierUp(event) 847 848 if not self.private_pasteBuffer: 849 return 850 851 if self.currentNetwork is None: 852 # this can happen when all networks were deleted. Yet, I prefer to 853 # have the Paste Button Icon active to indicate there is still 854 # stuff in the paste_buffer that can be pasted if a new network 855 # is created. 856 return 857 858 net = self.currentNetwork 859 net.clearSelection() 860 lastNodeInd = len(net.nodes) 861 codeSrc = reduce(lambda x, y: x+y, self.private_pasteBuffer) 862 obj = compile(codeSrc, '<string>', 'exec') 863 exec(obj, {'self':net, 'masterNet':net} ) 864 865 net.unfreeze() 866 net.needsResetNodeCache = 1 867 net.selectNodes(net.nodes[lastNodeInd:]) 868 869 # now shift pasted nodes 870 canvas = self.currentNetwork.canvas 871 bb = self.private_selectionbb 872 dx = bb[2]-bb[0] 873 dy = bb[3]-bb[1] 874 if dx < dy: 875 dx = dx*self.nbPaste 876 dy = 10*self.nbPaste 877 canvas.move('selected', dx, dy ) 878 else: 879 dx = 10*self.nbPaste 880 dy = dy*self.nbPaste 881 canvas.move('selected', dx, dy) 882 883 self.nbPaste = self.nbPaste + 1 884 885 for n in self.currentNetwork.selectedNodes: 886 n.posx = n.posx+dx 887 n.posy = n.posy+dy
888 889
890 - def loadNetwork_cb(self, event=None):
891 # reset keyboard modifier 892 # Note: we do that in case this method raises an exception. If we 893 # do not call modifierUp() we do not release the focus and Python 894 # is basically locking the screen until the process is killed. 895 if event and event.keysym == 'o': 896 event.keysym = "Control_L" 897 self.currentNetwork.modifierUp(event) 898 event.keysym = "Control_R" 899 self.currentNetwork.modifierUp(event) 900 901 if self.currentNetwork.filename is not None: 902 lNetworkDir = os.path.dirname(self.currentNetwork.filename) 903 else: 904 lNetworkDir = Vision.networkDefaultDirectory 905 906 # load network 907 if self.libraries.has_key('Pmv'): 908 file = tkFileDialog.askopenfilename( parent = self, 909 initialdir = lNetworkDir, 910 title='Load Network', 911 filetypes=[('network', '*_pmvnet.py *_net.py'), 912 ('pmv network', '*_pmvnet.py'), 913 ('vision network', '*_net.py'), 914 ('all', '*')] ) 915 else: 916 file = tkFileDialog.askopenfilename(parent = self, 917 initialdir = lNetworkDir, 918 title='Load Network', 919 filetypes=[('network', '*_net.py'), ('all', '*')] ) 920 921 if file: 922 self.loadNetwork(file, None) 923 self.recentFiles.add(file, 'loadNetwork')
924 925
926 - def mergeNetwork_cb(self, event=None):
927 if self.libraries.has_key('Pmv'): 928 file = tkFileDialog.askopenfilename(parent = self, 929 initialdir = '.', 930 filetypes=[('network', '*_pmvnet.py *_net.py'), 931 ('pmv network', '*_pmvnet.py'), 932 ('vision network', '*_net.py'), 933 ('all', '*')], 934 title='Merge Network') 935 else: 936 file = tkFileDialog.askopenfilename(parent = self, 937 initialdir = '.', 938 filetypes=[('network', '*_net.py'), ('all', '*')], 939 title='Merge Network') 940 941 if file: 942 self.loadNetwork(file, self.currentNetwork)
943 944
945 - def loadNetwork(self, filename, network=None, name=None):
946 # executes the file containing the network description 947 # masterNet is the parent network 948 949 # does file exist? If not, abort 950 if not os.path.exists(filename): 951 warnings.warn("ERROR: File not found! %s"%filename) 952 return 953 954 # if not parent net is specified, create a new notebook page 955 if network is None: 956 # humm seems like i could simply rename the tab here 957 # rather than deleting 958 if name is None: 959 if filename.endswith('_pmvnet.py'): 960 name = os.path.splitext(os.path.basename(filename))[0][:-7] 961 else: 962 name = os.path.splitext(os.path.basename(filename))[0][:-4] 963 964 # replace all underscore by minus because Pmw tabs names 965 # cannot have underscores in them 966 name = name.replace('_', '-') 967 968 if len(self.networks)==1 and len(self.currentNetwork.nodes)==0: 969 self.deleteNetwork(self.currentNetwork) 970 971 network = Network(name) 972 network.filename = os.path.abspath(filename) 973 network.freeze() 974 network.needsResetNodeCache = 0 975 self.addNetwork(network) 976 network.buildIcons() # we need to show the net to create the canvas 977 # BEFORE making it current, else widgets in nodes 978 # are not placed properly 979 self.setNetwork(network) 980 lastNodeInd = -1 981 982 network.unfreeze() 983 self.isFrozenTk.set(0) 984 985 else: # we are merging into an existing network 986 network.clearSelection() 987 lastNodeInd = len(network.nodes) 988 execfile(filename, {}, {'self':self, 'masterNet':network}) 989 990 network.needsResetNodeCache = 1 991 if lastNodeInd > -1: 992 network.selectNodes(network.nodes[lastNodeInd:]) 993 994 network._modified = False
995 996
997 - def addNetwork(self, network):
998 """None <- addNetwork(network) 999 adds a Network object to the editor 1000 """ 1001 ## while network.name in self.networks.keys(): 1002 ## name = askstring("Network name already used", "Network Name", 1003 ## initialvalue=network.name) 1004 ## network.rename(name) 1005 1006 # rename network if it already exists 1007 if network.name in self.networks.keys(): 1008 if isinstance(network, MacroNetwork): 1009 macnode = network.macroNode 1010 macnode.rename(macnode.name+str(self.uniqNetworkNumber)) 1011 1012 network.name = network.name+str(self.uniqNetworkNumber) 1013 1014 self.uniqNetworkNumber = self.uniqNetworkNumber + 1 1015 1016 self.networks[network.name] = network 1017 network.vEditor = weakref.ref(self) 1018 1019 handle = network.eventHandler['onSelectionChange'] 1020 handle.AddCallback(self.handleSelectionChangeMenus) 1021 self.eventHandler['addNetwork'].CallCallbacks() 1022 # and build the Pmw.Notebook page, the network canvas etc 1023 network.buildIcons() 1024 self.setFont("NetTabs",self.font["NetTabs"])
1025 1026
1027 - def closeNetwork_cb(self):
1028 """called from File->Close 1029 """ 1030 1031 if self.currentNetwork is None: 1032 return 1033 elif isinstance(self.currentNetwork, MacroNetwork): 1034 self.currentNetwork.macroNode.shrink() 1035 return 1036 1037 name = self.currentNetwork.name 1038 1039 text = "Do you want to save the changes you made to\n'"+name+"'?" 1040 d = SimpleDialog(self, text=text, 1041 buttons=["Don't save", "Cancel", "Save"], 1042 default=0, title="Close Network Dialog") 1043 result = d.go() 1044 1045 if result == 0: 1046 self.deleteNetwork(self.currentNetwork) 1047 return 1048 elif result == 1: 1049 return 1050 elif result == 2: 1051 self.saveNetwork_cb() 1052 self.deleteNetwork(self.currentNetwork) 1053 return
1054 1055
1056 - def deleteNetwork(self, network, saveValues=0):
1057 """None <- deleteNetwork(network) 1058 deletes a Network object from the editor 1059 """ 1060 del self.networks[network.name] 1061 network.delete(saveValues=saveValues) 1062 1063 if self.currentNetwork == network: 1064 keys = self.networks.keys() 1065 if len(keys): 1066 self.setNetwork(self.networks[keys[0]]) 1067 else: 1068 # FIXME: we should turn off most menus and buttons 1069 self.currentNetwork = None 1070 self.eventHandler['deleteNetwork'].CallCallbacks()
1071 1072
1073 - def selectNetwork(self, name):
1074 """ get's called by Pmw with the original name used to create the page 1075 """ 1076 1077 network = None 1078 for n in self.networks.values(): 1079 if n.origName == name: 1080 network = n 1081 break 1082 if network: 1083 if network.canvas: 1084 self.setNetwork(network) 1085 else: 1086 # do nothing. We can enter here when we quit and are deleting 1087 # macro networks 1088 return
1089 1090
1091 - def renameNetworkTab(self, network, newName):
1092 na = self.networkArea 1093 # update the tab name 1094 na.component(network.origName+'-tab').configure(text=newName)
1095 1096
1097 - def setNetwork(self, network):
1098 self.isFrozenTk.set( network.isFrozen() ) 1099 if network!=self.currentNetwork and network.canvas: 1100 self.networkArea.selectpage(network.origName) 1101 # get rid of any posted menu 1102 net = self.currentNetwork 1103 if net and net.postedMenu: 1104 net.postedMenu.unpost() 1105 net.postedMenu = None 1106 self.currentNetwork = network 1107 self.nbPaste = 0 # no shift on next paste operation 1108 network.eventHandler['onSelectionChange'].CallCallbacks()
1109 1110
1111 - def myButtonPress(self, event=None):
1112 #add button press callback to clear self.postedMenu attribute 1113 self.currentNetwork.postedMenu = None
1114 1115
1116 - def makeNetworksMenu(self):
1117 Networks_button = Tkinter.Menubutton(self.mBar, text='Networks') 1118 self.menuButtons['Networks'] = Networks_button 1119 Networks_button.pack(side=Tkinter.LEFT, padx="1m") 1120 1121 # re-set tearoff 1122 Networks_button.menu = Tkinter.Menu(Networks_button)#, tearoff=False) 1123 #Networks_button.menu.add_separator() 1124 1125 Networks_button.menu.bind("<Any-Button>", self.myButtonPress, '+') 1126 1127 # NOTE: the 'accelerator' entries are only used to display the key, 1128 # the binding actually occurs in net.py in bindCallbacks() 1129 # Vision.VPE.py also adds some events in it's addNetwork() method 1130 1131 Networks_button.menu.add_command(label='New...', 1132 accelerator="(Ctrl-n)", 1133 command=self.newNet_cb) 1134 Networks_button.menu.add_command(label='Rename...', 1135 command=self.renameNet_cb) 1136 Networks_button.menu.add_command(label='Close...', 1137 command=self.closeNetwork_cb) 1138 Networks_button.menu.add_command(label='Run', 1139 command=self.runCurrentNet_cb) 1140 1141 Networks_button.menu.add_checkbutton(label="Freeze Execution", 1142 command=self.toggleFreezeNetwork_cb, 1143 accelerator="(Ctrl-f)") 1144 1145 func = CallBackFunction( self.menuOption_cb, 'withThreads') 1146 Networks_button.menu.add_checkbutton(label="Run Multi-Threaded", 1147 variable = self.withThreadsTk, 1148 command=func) 1149 1150 Networks_button.menu.add_command(label='Refresh', 1151 command=self.refreshNet_cb) 1152 ## # Disabled Reset Widget Values for the time being, the method 1153 ## # can still be called with ed.currentNetwork.resetWidgetValues() 1154 ## Networks_button.menu.add_command(label='Reset Widget Values', 1155 ## command=self.resetWidgetValues_cb) 1156 Networks_button.menu.add_command(label='Reset Cache', 1157 command=self.resetCache_cb) 1158 Networks_button['menu'] = Networks_button.menu 1159 1160 Networks_button.menu.add_separator()
1161 1162
1163 - def makeFileMenu(self):
1164 File_button = Tkinter.Menubutton(self.mBar, text='File', underline=0) 1165 self.menuButtons['File'] = File_button 1166 File_button.pack(side=Tkinter.LEFT, padx="1m") 1167 1168 # re-set tearoff 1169 File_button.menu = Tkinter.Menu(File_button)#, tearoff=False) 1170 #File_button.menu.add_separator() 1171 1172 File_button.menu.add_command(label='New...', underline=0, 1173 accelerator='(Ctrl-n)', 1174 command=self.newNet_cb) 1175 File_button.menu.add_command(label='Open...', underline=0, 1176 accelerator='(Ctrl-o)', 1177 command=self.loadNetwork_cb) 1178 File_button.menu.add_command(label='Merge...', underline=0, 1179 command=self.mergeNetwork_cb) 1180 File_button.menu.add_command(label='Close...', underline=0, 1181 command=self.closeNetwork_cb) 1182 File_button.menu.add_separator() 1183 File_button.menu.add_command(label='Save...', underline=0, 1184 accelerator="(Ctrl-s)", 1185 command=self.saveNetwork_cb) 1186 File_button.menu.add_separator() 1187 File_button.menu.add_command(label='Print', underline=0, 1188 command=self.print_cb) 1189 File_button.menu.add_separator() 1190 File_button.menu.add_command(label='Quit', underline=0, 1191 command=self.interactiveExit_cb) 1192 File_button['menu'] = File_button.menu 1193 1194 rcFile = getResourceFolderWithVersion() 1195 if rcFile: 1196 rcFile += os.sep + 'Vision' + os.sep + "recent.pkl" 1197 self.recentFiles = RecentFiles(self, File_button.menu, filePath=rcFile, 1198 menuLabel = 'Open Recent')
1199
1200 - def makeEditMenu(self):
1201 Edit_button = Tkinter.Menubutton(self.mBar, text='Edit', underline=0) 1202 self.menuButtons['Edit'] = Edit_button 1203 Edit_button.pack(side=Tkinter.LEFT, padx="1m") 1204 1205 # got rid of useless tearoff 1206 Edit_button.menu = Tkinter.Menu(Edit_button, tearoff=False) 1207 Edit_button.menu.add_separator() 1208 1209 #Edit_button.menu.add('command', label="Undo", command=self.undo) 1210 #Edit_button.menu.entryconfig(1, state=Tkinter.DISABLED) 1211 Edit_button.menu.add_command(label="Undo", command=self.undo, 1212 state=Tkinter.DISABLED) 1213 Edit_button.menu.add_separator() 1214 1215 # Please note: accelerators such as Ctrl-a are just for visuals, 1216 # the real binding of these events takes place in net.py 1217 1218 Edit_button.menu.add_command(label="Cut", accelerator="(Ctrl-x)", 1219 command=self.cutNetwork_cb) 1220 Edit_button.menu.entryconfig("Cut", state=Tkinter.DISABLED) 1221 Edit_button.menu.add_command(label="Copy", accelerator="(Ctrl-c)", 1222 command=self.copyNetwork_cb) 1223 Edit_button.menu.entryconfig("Copy", state=Tkinter.DISABLED) 1224 Edit_button.menu.add_command(label="Paste", accelerator="(Ctrl-v)", 1225 command=self.pasteNetwork_cb) 1226 Edit_button.menu.entryconfig("Paste", state=Tkinter.DISABLED) 1227 Edit_button.menu.add_separator() 1228 1229 #Edit_button.menu.add_checkbutton(label="Options", command=self.options) 1230 Edit_button.menu.add_command(label="Select All", 1231 accelerator=("(Ctrl-a)"), 1232 command=self.selectAll_cb) 1233 1234 if self.withShell: 1235 Edit_button.menu.add_separator() 1236 Edit_button.menu.add_command(label="Show Python Shell", 1237 command=self.togglePythonShell) 1238 1239 Edit_button.menu.add_separator() 1240 func = CallBackFunction( self.menuOption_cb, 'verbose') 1241 Edit_button.menu.add_checkbutton(label="verbose", 1242 variable=self.verboseTk, 1243 command=func) 1244 1245 func = CallBackFunction( self.menuOption_cb, 'flashNodesWhenRun') 1246 Edit_button.menu.add_checkbutton(label="Flash Nodes When Run", 1247 variable=self.flashNodesWhenRunTk, 1248 command=func) 1249 func = CallBackFunction( self.menuOption_cb, 'splineConnections') 1250 Edit_button.menu.add_checkbutton(label="Spline connections", 1251 variable=self.splineConnectionsTk, 1252 command=self.toggleSplineConnections) 1253 1254 Edit_button.menu.add_checkbutton(label="Color Node by Library", 1255 variable=self.colorNodeByLibraryTk, 1256 command=self.toggleColorNodeByLibrary) 1257 #hyperbolic scaling is currently broken 1258 #Edit_button.menu.add_checkbutton(label="hyperbolique scaling", 1259 # command=self.scaleHyperToggle) 1260 1261 Edit_button.menu.add_command(label="Create Macro", 1262 accelerator=("(Ctrl-m)"), 1263 command=self.createMacro_cb) 1264 1265 Edit_button.menu.add_command(label="Create User Panel", 1266 #accelerator=("(Ctrl-m)"), 1267 command=self.createUserPanel_cb) 1268 1269 Edit_button.menu.add_command(label="Delete User Panel", 1270 command=self.deleteUserPanel_cb) 1271 1272 # set up a pointer from the file menubutton back to the file menu 1273 Edit_button['menu'] = Edit_button.menu
1274 1275
1276 - def createMenus(self, parent):
1277 self.mBar = Tkinter.Frame(parent, relief=Tkinter.RAISED, borderwidth=2) 1278 self.mBar.pack(side='top',fill=Tkinter.X) 1279 self.menuButtons = {} 1280 self.makeFileMenu() 1281 self.makeEditMenu() 1282 self.makeNetworksMenu() 1283 apply( self.mBar.tk_menuBar, self.menuButtons.values() ) 1284 self.title = Tkinter.Label(self.mBar, text=self.name) 1285 self.title.pack(side=Tkinter.RIGHT)
1286 1287
1288 - def setUndoLabel(self):
1289 b = self.menuButtons['Edit'] 1290 if len(self.undoStack)==0: 1291 b.menu.entryconfig(1, label='Undo',state=Tkinter.DISABLED) 1292 return 1293 else: 1294 l = self.undoStack[-1][1] 1295 # FIXME!!! since undo is broken, the entry remains disabled 1296 b.menu.entryconfig(1, label=l,state=Tkinter.DISABLED)
1297 1298
1299 - def undo(self, event=None):
1300 return 1301 # FIXME!!! This command is currently broken 1302 b = self.menuButtons['Edit'] 1303 if len(self.undoStack)==0: return 1304 command, comment = self.undoStack.pop() 1305 cmd = command[:string.rfind(command, ')')]+', undo=0)' 1306 exec( cmd ) 1307 self.setUndoLabel()
1308 1309
1310 - def setupUndo(self, command, comment):
1311 self.undoStack.append( (command, comment) ) 1312 self.setUndoLabel()
1313 1314
1315 - def scaleHyperToggle(self, event=None):
1316 cnet = self.currentNetwork 1317 if cnet.scalingHyper: 1318 cnet.canvas.unbind("<Motion>") 1319 cnet.resetScale() 1320 else: 1321 cnet.canvas.bind("<Motion>", cnet.scaleHyper_cb) 1322 cnet.lastx = None # we do not get an event in this callback :( 1323 cnet.scalingHyper = not cnet.scalingHyper
1324 1325
1326 - def scaleHyper_cb(self, event):
1327 if self.lastx is None: 1328 self.lastx = event.x 1329 self.lasty = event.y 1330 return 1331 cnet = self.currentNetwork 1332 cnet.scaleHyper(event.x, event.y, cnet.hyperScaleRad) 1333 self.lastx = event.x 1334 self.lasty = event.y
1335 1336
1337 - def createMacro_cb(self, event=None):
1338 # reset keyboard modifier 1339 # Note: we do that in case this method raises an exception. If we 1340 # do not call modifierUp() we do not release the focus and Python 1341 # is basically locking the screen until the process is killed. 1342 if event and event.keysym == 'm': 1343 event.keysym = "Control_L" 1344 self.currentNetwork.modifierUp(event) 1345 event.keysym = "Control_R" 1346 self.currentNetwork.modifierUp(event) 1347 1348 macName = 'macro0' 1349 i=1 1350 while macName in self.networks.keys(): 1351 macName = 'macro'+str(i) 1352 i = i + 1 1353 macName = askstring('Macro Name', "Macro Name", initialvalue=macName) 1354 while macName in self.networks.keys(): 1355 macName = askstring('Macro Name already in use', "Macro Name", 1356 initialvalue='macro') 1357 if macName is None or len(macName) == 0: 1358 return 1359 self.createMacro(self.currentNetwork, macName)
1360 1361
1362 - def createMacro(self, network, name):
1363 from NetworkEditor.macros import MacroNode 1364 n = MacroNode(name) 1365 network.addNode(n , 100, 100 )
1366 1367
1368 - def selectAll_cb(self, event=None):
1369 # reset keyboard modifier 1370 # Note: we do that in case this method raises an exception. If we 1371 # do not call modifierUp() we do not release the focus and Python 1372 # is basically locking the screen until the process is killed. 1373 if event and event.keysym == 'a': 1374 event.keysym = "Control_L" 1375 self.currentNetwork.modifierUp(event) 1376 event.keysym = "Control_R" 1377 self.currentNetwork.modifierUp(event) 1378 1379 self.currentNetwork.selectNodes(self.currentNetwork.nodes)
1380 1381
1382 - def menuOption_cb(self, varName):
1383 """menuOptions(var, varTk toggle menu options 1384 var: verbose 1385 colorNodeByLibrary 1386 flashNodesWhenRun 1387 """ 1388 varTk = getattr(self, varName+'Tk') 1389 setattr(self, varName, varTk.get())
1390 1391
1392 - def toggleSplineConnections(self, event=None):
1393 self.menuOption_cb('splineConnections') 1394 self.currentNetwork.setSplineConnections( 1395 self.splineConnections==1 )
1396 1397
1398 - def toggleColorNodeByLibrary(self, event=None):
1399 self.menuOption_cb('colorNodeByLibrary') 1400 for net in self.networks.values(): 1401 for node in net.nodes: 1402 1403 # if color by library is ON: 1404 if self.colorNodeByLibraryTk.get() == 1: 1405 # no node library? 1406 if node.library is None: 1407 if node.frozen is True: 1408 fcol = '#b6d3f6' # color for frozen state 1409 else: 1410 fcol = "gray85" 1411 # node library? 1412 else: 1413 if node.frozen is True: 1414 fcol = '#b6d3f6' # color for frozen state 1415 else: 1416 fcol = node.library.color 1417 1418 # elif color by library is OFF: 1419 else: 1420 if node.frozen is True: 1421 fcol = '#b6d3f6' # color for frozen state 1422 else: 1423 fcol = "gray85" 1424 1425 1426 ## if node.library is not None: 1427 ## if node.frozen == True: 1428 ## col = '#b6d3f6' # color for frozen state 1429 ## else: 1430 ## if self.colorNodeByLibraryTk.get() == 1: 1431 ## col = node.library.color 1432 ## else: 1433 ## col = 'gray85' 1434 node.deselectOptions['fill'] = fcol 1435 node.setColor(fcol)
1436 1437
1438 - def newNet_cb(self, event=None):
1439 # reset keyboard modifier 1440 # Note: we do that in case this method raises an exception. If we 1441 # do not call modifierUp() we do not release the focus and Python 1442 # is basically locking the screen until the process is killed. 1443 if event and event.keysym == 'n': 1444 event.keysym = "Control_L" 1445 self.currentNetwork.modifierUp(event) 1446 event.keysym = "Control_R" 1447 self.currentNetwork.modifierUp(event) 1448 name = askstring("Network name", "Network Name", initialvalue='', 1449 parent = self) 1450 while name in self.networks.keys(): 1451 name = askstring("Network name already used", "Network Name", 1452 initialvalue=name, parent = self) 1453 if name is None or len(name) == 0: 1454 return 1455 net = Network(name) 1456 self.addNetwork(net) 1457 net.buildIcons() 1458 self.setNetwork(net)
1459 1460
1461 - def renameNet_cb(self, event=None):
1462 name = None 1463 while 1 : 1464 name = askstring("Network name already used", "Network Name", 1465 initialvalue=self.currentNetwork.name) 1466 if name not in self.networks.keys(): break 1467 1468 if name is None or len(name) == 0: 1469 return 1470 self.currentNetwork.rename(name)
1471 1472 1473 # def deleteNet_cb(self, event=None): 1474 # ans = askokcancel("Confirm network deletion", 1475 # "Do you really want to delete the current network?") 1476 # if ans: 1477 # self.deleteNetwork(self.currentNetwork) 1478 1479
1480 - def runCurrentNet_cb(self, event=None):
1481 # this is now done in self.currentNetwork.run() 1482 #self.currentNetwork.forceExecution = 1 1483 self.currentNetwork.run()
1484 1485
1486 - def togglePauseCurrentNet_cb(self, event=None):
1487 net = self.currentNetwork 1488 net.togglePause()
1489 1490
1491 - def stopCurrentNet_cb(self, event=None):
1492 print '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%' 1493 net = self.currentNetwork 1494 net.stop()
1495 1496
1497 - def refreshNet_cb(self, net=None, event=None):
1498 if net is None: 1499 net = self.currentNetwork 1500 for node in net.nodes: 1501 node.scaleSum = 1.0 1502 net.destroyIcons(saveValues=1) 1503 net.buildIcons() 1504 selnodes = net.selectedNodes[:] 1505 net.clearSelection() 1506 net.selectNodes(selnodes)
1507 1508
1509 - def resetWidgetValues_cb(self, event=None):
1510 self.currentNetwork.resetWidgetValues()
1511 1512
1513 - def resetCache_cb(self, event=None):
1514 self.currentNetwork.resetNodeCache()
1515 1516
1517 - def print_cb(self, event=None):
1518 # FIXME: DS: highly experimental... currently, just dumps the 1519 # postscript data into a file. The user has to print this file 1520 # manually 1521 postscript = self.currentNetwork.scrolledCanvas.postscript() 1522 fd=open('network.ps', 'w') 1523 1524 fd.write(postscript) 1525 fd.close()
1526 1527
1528 - def extendTypesTable(self, lib):
1529 """add new typesTable to the typeManager""" 1530 for t in lib.typesTable: 1531 self.typeManager.addType(t)
1532 1533
1534 - def extendSynonymsTable(self, lib):
1535 """add new synonymsTable to the typeManager 1536 """ 1537 for s in lib.synonymsTable: 1538 self.typeManager.addSynonymDict(s)
1539 1540
1541 - def extendWidgetsTable(self, lib):
1542 """add new widgets to widgetsTable""" 1543 for w in lib.widgets: 1544 # w has to be instance of Port widgets 1545 assert issubclass(w, PortWidget) 1546 # w.__name__ must be different from predefined widgets 1547 if w.__name__ not in widgetsTable.keys(): 1548 widgetsTable[w.__name__] = w
1549 1550
1551 - def configure(self, **kw):
1552 if len( kw.items() ) == 0: # we return a dict with the current config 1553 dict = {} 1554 dict['visibleWidth'] = self.visibleWidth 1555 dict['visibleHeight'] = self.visibleHeight 1556 dict['withThreads'] = self.withThreadsTk.get() 1557 dict['freezeExecution'] = self.isFrozenTk.get() 1558 dict['verbose'] = self.verboseTk.get() 1559 dict['colorNodeByLibrary'] = self.colorNodeByLibraryTk.get() 1560 dict['flashNodesWhenRun'] = self.flashNodesWhenRunTk.get() 1561 return dict 1562 1563 for key,value in kw.items(): 1564 if key=='visibleWidth': 1565 self.visibleWidth = value 1566 self.resizeNetworkArea() 1567 elif key=='visibleHeight': 1568 self.visibleHeight = value 1569 self.resizeNetworkArea() 1570 elif key=='withThreads': self.setConfigValue(key, value) 1571 elif key=='freezeExecution': self.setFreezeUnfreezeNetwork(value) 1572 elif key=='verbose': self.setConfigValue(key, value) 1573 elif key=='colorNodeByLibrary': self.setConfigValue(key, value) 1574 elif key=='flashNodesWhenRun': self.setConfigValue(key, value)
1575 1576
1577 - def setConfigValue(self, varName, value):
1578 assert value in [0,1] 1579 varTk = getattr(self, varName+'Tk') 1580 setattr(self, varName, value) 1581 varTk.set(value)
1582 1583
1584 - def setFreezeUnfreezeNetwork(self, value):
1585 assert value in [0,1] 1586 if value == 1: 1587 self.freezeNetwork() 1588 else: 1589 self.unfreezeNetwork()
1590 1591
1592 - def togglePythonShell(self, event=None):
1593 if not self.withShell: 1594 return 1595 b = self.menuButtons['Edit'] 1596 shell = self.pyshell.top 1597 if shell.state() == 'withdrawn': 1598 b.menu.entryconfig("Show Python Shell", label='Hide Python Shell') 1599 self.pyshell.top.deiconify() 1600 else: 1601 b.menu.entryconfig("Hide Python Shell", label='Show Python Shell') 1602 self.pyshell.top.withdraw()
1603 1604
1605 - def sourceFile(self, resourceFile=None):
1606 if resourceFile is None or resourceFile == '': 1607 return 1608 resourceFileLocation = findResourceFile(self, resourceFile=resourceFile) 1609 1610 if resourceFileLocation is None or resourceFileLocation == '': 1611 return 1612 1613 if resourceFileLocation.has_key('currentdir') and \ 1614 not resourceFileLocation['currentdir'] is None: 1615 path = resourceFileLocation['currentdir'] 1616 1617 elif resourceFileLocation.has_key('home') and \ 1618 not resourceFileLocation['home'] is None: 1619 path = resourceFileLocation['home'] 1620 1621 elif resourceFileLocation.has_key('package') and \ 1622 not resourceFileLocation['package'] is None: 1623 path = resourceFileLocation['package'] 1624 else: 1625 return 1626 1627 if not path: 1628 return 1629 1630 # now execute the file: 1631 localDict = sys.modules['__main__'].__dict__ 1632 # save self entry in local dict if it exists 1633 oldself = None 1634 if localDict.has_key('self'): 1635 oldself = localDict['self'] 1636 1637 localDict['self'] = self 1638 globalDict = self.__dict__ 1639 1640 execfile( path, globalDict, localDict) 1641 1642 # restore old self on local dict 1643 if oldself is not None: 1644 localDict['self'] = oldself 1645 1646 # complete vpe menu with additionnal fastLibs 1647 if hasattr(self, 'updateFastLibs'): 1648 self.updateFastLibs() 1649 elif localDict.has_key('mv'): 1650 localDict['mv'].vision.ed.updateFastLibs()
1651 1652 1653 if __name__=='__main__': 1654 ed = NetworkBuilder("test builder") 1655 top = Tkinter.Toplevel() 1656 ed1 = NetworkBuilder("test builder1", master=top, 1657 visibleWidth=600, totalWidth=3000) 1658 node1 = NetworkNode(name='node1') 1659 ed.addNode(node1, 100, 100) 1660 node1.rename('operator1') 1661 node1.highlight() 1662 node1.unhighlight() 1663 1664 node2 = NetworkNode(name='node2') 1665 ed.addNode(node2, 200, 200) 1666 top.mainloop() 1667 conn1 = ed.connectNodes(node1, node2) 1668 conn1.highlight() 1669 conn1.unhighlight() 1670 1671 node3 = NetworkNode(name='node3') 1672 ed.addNode(node3, 300, 100) 1673 conn2 = ed.connectNodes(node3, node2, mode='angles') 1674 1675 node4 = NetworkNode(name='node4') 1676 ed.addNode(node4, 350, 270) 1677 conn3 = ed.connectNodes(node2, node4, mode='angles', smooth=1) 1678 1679 node1.move(200, 100) 1680 conn1.updatePosition() 1681 1682 ed.selectNodes([node1, node2, node3]) 1683 ed.deselectNodes([node1, node4]) 1684 1685 ed.moveSubGraph(ed.selectedNodes, 10, 20) 1686 1687 node5 = NetworkNode(name='node5') 1688 ed.addNode(node5, 100, 200) 1689 conn4 = ed.connectNodes(node1, node5, mode='angles') 1690
1691 - def connectCB(connection):
1692 print 'connecting %s with %s'%(connection.port1.node.name, 1693 connection.port2.node.name)
1694 ed.actionCallback['onAddConnection'].AddCallback(connectCB) 1695 conn4 = ed.connectNodes(node2, node5) 1696 1697 # try nodes of type 2 1698 1699 n1 = NetworkNode('test1') 1700 ed.addNode(n1, 100, 350) 1701 n1.addOutputPort() 1702 1703 from NetworkEditor.simpleNE import NetworkNode 1704 n2 = NetworkNode('test2') 1705 ed.addNode(n2, 300, 350) 1706 n2.addInputPort() 1707 n2.addInputPort(name='test', balloon='test help', 1708 required=True, datatype=None, validate=None) 1709 1710 1711 ## from NetworkEditor.simpleNE import NetworkConnection 1712 ## ed.connectNodes(n1, n2, 2, 1) 1713 1714 ## ed.connectNodes(node5, n2, 0, 2) 1715 ## ed.selectNodes([n2]) 1716 ## ed.moveSubGraph(ed.selectedNodes, 10, 20) 1717