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

Source Code for Module NetworkEditor.itemBase

  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  ######################################################################### 
 15   
 16  import sys 
 17  import types 
 18  import Tkinter 
 19  import threading 
 20  import traceback 
 21  import warnings 
 22  import weakref 
 23  import tkMessageBox 
 24   
 25  from mglutil.gui.BasicWidgets.Tk.TreeWidget.objectBrowser import ObjectBrowser 
 26  from mglutil.util.callback import CallbackManager, CallBackFunction 
 27   
 28   
29 -class UserPanel:
30 """Tk frame onto which a user can place selected widgets""" 31
32 - def __init__(self, name, master=None, network=None, width=120, height=90):
33 #print "UserPanel.__init__" 34 35 self.widgets = [] # list of widget placed onto this panel 36 if master is None: 37 self.master = Tkinter.Toplevel() 38 self.master.title(name) 39 self.master.protocol('WM_DELETE_WINDOW', self.withdraw_cb) 40 else: 41 self.master = master 42 43 self.network = network 44 self.editor = network.getEditor() 45 46 ## build menu 47 if master is None: 48 buttonsFrame = Tkinter.Frame(self.master) 49 self.buttonRun = Tkinter.Button(buttonsFrame, text='Run', command=network.run) 50 self.buttonRun.pack(side='left', expand=1, fill='x') 51 if hasattr(network, 'isApplication') is True: 52 self.buttonQuit = Tkinter.Button(buttonsFrame, text='Quit', command=self.master.quit) 53 self.buttonQuit.pack(side='left', expand=1, fill='x') 54 else: 55 self.buttonShowHideNetwork = Tkinter.Button(buttonsFrame, text='Show / Hide Vision', 56 command=self.editor.toggleGuiVisibility) 57 self.buttonShowHideNetwork.pack(side='left', expand=1, fill='x') 58 buttonsFrame.pack(side='top', fill='x') 59 60 ## build frame for widgets 61 self.frame = Tkinter.Frame( 62 self.master, relief='sunken', bd=3, bg='#c3d0a6') 63 self.frame.configure(width=width, height=height) 64 self.frame.pack(side='top', expand=1, fill='both')
65 66
67 - def withdraw_cb(self, event=None):
68 """ 69 """ 70 #print "withdraw_cb" 71 if hasattr(self.network, 'isApplication') is True: 72 if len(self.network.userPanels) == 1: 73 self.master.quit() 74 else: 75 self.master.iconify() 76 else: 77 self.master.iconify() 78 self.editor.master.master.master.deiconify()
79 80
81 - def placeWidgetAndLabel(self, widget, relx, rely):
82 #print "UserPanel.placeWidgetAndLabel", widget.widgetFrame.winfo_reqwidth() 83 84 labelSide = widget.widgetGridCfg['labelSide'] 85 if labelSide is None: 86 labelSide = 'left' 87 88 widget.widgetFrame.place(relx=relx, rely=rely, anchor=Tkinter.CENTER) 89 if labelSide == 'left': 90 labelx = .5 * widget.widgetFrame.winfo_width() \ 91 / widget.widgetFrame.master.winfo_width() 92 widget.tklabel.place(relx=relx-labelx, rely=rely, anchor='e') 93 elif labelSide == 'right': 94 labelx = .5 * widget.widgetFrame.winfo_width() \ 95 / widget.widgetFrame.master.winfo_width() 96 widget.tklabel.place(relx=relx+labelx, rely=rely, anchor='w') 97 elif labelSide == 'top': 98 labely = .5 * widget.widgetFrame.winfo_height() \ 99 / widget.widgetFrame.master.winfo_height() 100 widget.tklabel.place(relx=relx, rely=rely-labely, anchor='s') 101 elif labelSide == 'bottom': 102 labely = .5 * widget.widgetFrame.winfo_height() \ 103 / widget.widgetFrame.master.winfo_height() 104 widget.tklabel.place(relx=relx, rely=rely+labely, anchor='n') 105 else: 106 warnings.warn('Wrong side for label')
107 108
109 - def rePlaceWidget(self, widget, labelSide=None):
110 #print "UserPanel.rePlaceWidget" 111 relx = float( widget.widgetFrame.winfo_x() \ 112 + .5*widget.widgetFrame.winfo_width() ) \ 113 / widget.widgetFrame.master.winfo_width() 114 rely = float(widget.widgetFrame.winfo_y() \ 115 + .5*widget.widgetFrame.winfo_height() ) \ 116 / widget.widgetFrame.master.winfo_height() 117 self.placeWidgetAndLabel(widget, relx, rely)
118 119
120 - def motionWidget_cb(self, widget, event=None):
121 #print "UserPanel.motionWidget_cb" 122 relx = float(event.x_root - widget.widgetFrame.master.winfo_rootx()) \ 123 /widget.widgetFrame.master.winfo_width() 124 rely = float(event.y_root - widget.widgetFrame.master.winfo_rooty()) \ 125 /widget.widgetFrame.master.winfo_height() 126 self.placeWidgetAndLabel(widget, relx, rely)
127 128
129 - def postWidgetMenu(self, widget, event=None):
130 #print widget 131 widget.menu.post(event.x_root, event.y_root)
132 133
134 - def addWidget(self, widget, widgetPlacerCfg=None):
135 #print "UserPanel.addWidget" 136 self.widgets.append(widget) 137 138 if hasattr(self.network, 'isApplication') is True: 139 widget.menu.delete(1,'end') 140 cbpost = CallBackFunction(self.postWidgetMenu, widget) 141 widget.tklabel.bind("<Button-3>", cbpost) 142 if hasattr(widget.widget, 'canvas'): 143 widget.widget.canvas.bind("<Button-3>", cbpost) 144 else: 145 widget.widget.bind("<Button-3>", cbpost) 146 147 cb = CallBackFunction(self.motionWidget_cb, widget) 148 widget.tklabel.bind("<B2-Motion>", cb) 149 if hasattr(widget.widget, 'canvas'): 150 widget.widget.canvas.bind("<B2-Motion>", cb) 151 else: 152 widget.widget.bind("<B2-Motion>", cb) 153 154 if widgetPlacerCfg is not None: 155 relx = eval(widgetPlacerCfg['relx']) 156 rely = eval(widgetPlacerCfg['rely']) 157 else: 158 relx = .5 159 rely = .5 160 161 self.placeWidgetAndLabel(widget, relx, rely) 162 # we need this otherwise the widget.widgetFrame.winfo_width 163 # is not set correctly the first time (so the label is hidden behind the widget) 164 widget.port.node.network.canvas.update_idletasks() 165 self.placeWidgetAndLabel(widget, relx, rely)
166 167
168 - def deleteWidget(self, widget):
169 #print "UserPanel.deleteWidget" 170 if widget in self.widgets: 171 self.widgets.remove(widget) 172 173 widget.tklabel.unbind("<Button-2>") 174 if hasattr(widget.widget, 'canvas'): 175 widget.widget.canvas.unbind("<Button-2>") 176 #widget.widget.valueLabel.bind("<Button-2>", self.moveWidget_cb) 177 else: 178 widget.widget.unbind("<Button-2>")
179 180 181
182 -class ParamPanel:
183 """This class provides show / hide for all parameter panels 184 """
185 - def __init__(self, node, master=None, title=None, applyFun=None, 186 dismissFun=None):
187 188 self.node = weakref.ref(node) 189 self.visible = 0 # visible=1 means panel is displayed 190 191 192 self.applyFun = applyFun 193 self.dismissFun = dismissFun 194 if self.dismissFun is None: 195 self.dismissFun = self.hide 196 197 if master is None: 198 master=Tkinter.Toplevel() 199 self.master = master 200 if title: 201 self.master.title(title) 202 self.master.withdraw() 203 self.master.protocol('WM_DELETE_WINDOW', self.dismissFun ) 204 else: 205 self.master = master 206 207 # the mainFrame holds the widgetFrame plus some buttons 208 self.mainFrame = Tkinter.Frame(self.master) 209 self.mainFrame.pack(fill='both',expand=1) 210 211 # add menu entries 212 self.createMenus(self.mainFrame) 213 214 # the widgetFrame holds the widgets bound to this panel 215 self.widgetFrame = Tkinter.Frame(self.mainFrame, borderwidth=3, 216 bg='#c3d0a6', relief='ridge') 217 218 self.applyButton = Tkinter.Button(self.mainFrame, text="Apply", 219 command=self.forceRun) 220 self.dismissButton = Tkinter.Button(self.mainFrame, text="Dismiss", 221 command=self.dismissFun) 222 self.widgetFrame.pack(fill='both', expand=1) 223 self.applyButton.pack(side='left', fill='x', expand=1) 224 self.dismissButton.pack(side='left')
225 226
227 - def createMenus(self, parent):
228 self.mBar = Tkinter.Frame(parent, relief=Tkinter.RAISED, 229 borderwidth=2) 230 self.mBar.pack(side='top',fill='x') 231 self.menuButtons = {} 232 self.makeOptionsMenu(self.mBar) 233 apply( self.mBar.tk_menuBar, self.menuButtons.values() )
234 235
236 - def makeOptionsMenu(self, parent):
237 Options_button = Tkinter.Menubutton(parent, text='Options', 238 underline=0) 239 self.menuButtons['Options'] = Options_button 240 Options_button.pack(side=Tkinter.LEFT, padx="1m") 241 242 # got rid of useless tearoff 243 Options_button.menu = Tkinter.Menu(Options_button, tearoff=False) 244 245 self.immediateTk = Tkinter.IntVar() 246 self.immediateTk.set(1) 247 Options_button.menu.add_checkbutton(label='Immediate', 248 variable=self.immediateTk, 249 command=self.setImmediate) 250 251 252 Options_button.menu.add_separator() 253 Options_button.menu.add_command(label='Dismiss', underline=0, 254 command=self.dismissFun) 255 Options_button['menu'] = Options_button.menu
256 257
258 - def setImmediate(self, event=None, immediate=None, tagModified=True):
259 if tagModified is True: 260 self.node()._setModified(True) 261 if immediate is not None: 262 assert immediate in [0,1,True,False] 263 self.immediateTk.set(immediate)
264 265
266 - def show(self):
267 """ show paramter panel 268 """ 269 if self.master.winfo_ismapped() == 0: 270 self.master.deiconify() 271 self.master.lift() 272 273 # we make sure these are on 274 self.node().paramPanelTk.set(1) 275 self.visible = 1
276 277
278 - def hide(self, event=None):
279 if self.master.winfo_ismapped() == 1: 280 self.master.withdraw() 281 282 # we make sure these are off 283 self.node().paramPanelTk.set(0) 284 self.visible = 0
285 286
287 - def toggleVisibility(self, event=None):
288 if self.master.winfo_ismapped() == 1: 289 self.hide() 290 else: 291 self.show()
292 293
294 - def destroy(self, event=None):
295 if isinstance(self.master, Tkinter.Toplevel): 296 self.master.destroy() 297 else: 298 self.mainFrame.destroy()
299 300
301 - def run(self, event=None):
302 # only run if immediate==1. This method is called by the widget's set 303 # method 304 if self.immediateTk.get(): 305 if self.applyFun is not None: 306 self.applyFun()
307 308
309 - def forceRun(self, event=None):
310 # always call the apply fun. This method is called by the 'apply' 311 # button at the bottom of this param panel 312 if self.applyFun is not None: 313 self.applyFun()
314 315
316 -class NetworkItemsBase(object):
317 """Base class for all objects that can be added to a Canvas.""" 318 319 # the following 3 methods are used to maintain backwards compatibility 320 # because we changed certain attributes to weakref
321 - def getEditor(self):
322 return self.vEditor()
323
324 - def setEditor(self, editor):
325 self.vEditor = weakref.ref(editor)
326
327 - def delEditor(self):
328 del self.vEditor
329 330 # "self.editor" has to be created here. because we use the property mechan. 331 editor = property(getEditor, setEditor, delEditor, 332 "Used for backwards compatibility because we changed this to a weakref.") 333 334
335 - def __init__(self, name):
336 self.name = name # item's name 337 338 # for backwards-compatibility we created the following attribute: 339 # vEditor will be a weakref to editor and only a network will keep 340 # the attribute ".editor" in order to be able to load old networks. 341 # newer saved networks will use the method getEditor() to get a handle 342 # to the editor 343 self.vEditor = None # comment: see above 344 345 self._modified = False # if the item is instanciated, or if certain 346 # attributes are changed, for example, 347 # through configure(), this will be set 'True' 348 349 self._original = True # used for saving networks: if set to False
350 # indicates this object was created by the 351 # user (such as adding a node to a network 352 # or adding a port to a node using the node 353 # editor 354 355
356 - def _setModified(self, modified):
357 """set the _modified attribute. Users should not call this method""" 358 359 assert modified in [True, False] 360 self._modified = modified
361 362
363 - def _setOriginal(self, original):
364 """set the _original attribute. Users should not call this method""" 365 366 assert original in [True, False] 367 self._original = original
368 369
370 -class NetworkItems(NetworkItemsBase):
371 """Base class for objects that can be added to a Canvas. 372 Network items sublassing this have to extend the buildIcons method to create 373 a Canvas geometry representing this item. 374 """ 375
376 - def __init__(self, name='NoName', autoRun=True):
377 NetworkItemsBase.__init__(self, name) 378 379 self.paramPanel = None 380 self.iconMaster = None # canvas on which this item is placed 381 self.id = None # Tkinter.Canvas primitive ID that will correspond 382 # to this node 383 self.iconTag = None# unique tag shared by all primitives used to 384 # draw this node's icon 385 386 # editor and network are set in the addNode method of Network 387 self.network = None # NetworkObject to which this node belongs 388 389 self.objEditor = None 390 self.dynamicComputeFunction = None 391 392 self.selected = 0 393 self.nodeScaleFactor = 1.0 394 self.distScaleFactor = 1.0 395 self.posx = 0 396 self.posy = 0 397 self.menu = None 398 self.highlightOptions = {'highlightbackground':'red'} 399 self.unhighlightOptions = {'highlightbackground':'gray50'} 400 self.selectOptions = {'background':'yellow'} 401 self.deselectOptions = {'background':'gray85'} 402 self.objectBrowser = None # introspect object window 403 self.editFuncWindowWidth = 500 # width and height of source code editor 404 self.editFuncWindowHeight = 200 405 self.netSwitchOriginal = None 406 self.frozen = False # set to True to prevent node from running 407 self.autoRun = autoRun # If True, the node will run if valid data is 408 # provided on each required input port. When set to False 409 # the node only runs when the user requests it 410 self.isSchedulingNode = False # if set to 1, this node and its children 411 # will not be scheduled. Example: the 412 # Iterate Node in Vision.StandardNodes 413 414 self.frozenTk = Tkinter.IntVar() 415 self.frozenTk.set(self.frozen) 416 417 self.autoRunTk = Tkinter.IntVar() 418 self.autoRunTk.set(self.autoRun) 419 420 # lock that needs to be acquired before node can be modified 421 # FIXME: we have to update all methods operating on nodes to make 422 # them thread safe 423 self.runLock = threading.RLock() 424 425 # table of functions to call for specific mouse events on canvas 426 self.mouseButtonFlag = 0 427 self.mouseActionEvents = [ 428 '<Button-1>', '<Shift-Button-1>', 429 '<Alt-Button-1>', '<Control-Button-1>', 430 '<Double-Button-1>', '<Double-Shift-Button-1>', 431 '<Double-Alt-Button-1>', '<Double-Control-Button-1>', 432 '<Button-2>', '<Shift-Button-2>', 433 '<Alt-Button-2>', '<Control-Button-2>', 434 '<Double-Button-2>', '<Double-Shift-Button-2>', 435 '<Double-Alt-Button-2>', '<Double-Control-Button-2>', 436 '<Button-3>', '<Shift-Button-3>', 437 '<Alt-Button-3>', '<Control-Button-3>', 438 '<Double-Button-3>', '<Double-Shift-Button-3>', 439 '<Double-Alt-Button-3>', '<Double-Control-Button-3>' 440 ] 441 self.mouseAction = {} 442 for event in self.mouseActionEvents: 443 self.mouseAction[event] = None 444 445 self.mouseAction['<Button-3>'] = self.postItemMenu
446 447
448 - def toggleAutoRun_cb(self, event=None):
449 self.autoRun = not self.autoRun 450 self.autoRunTk.set(self.autoRun) 451 self._setModified(True)
452 453
454 - def toggleFrozen_cb(self, event=None):
455 if self.frozen: 456 self.unfreeze() 457 else: 458 self.freeze() 459 self.frozen = not self.frozen 460 self.frozenTk.set(self.frozen) 461 self._setModified(True)
462 463
464 - def freeze(self):
465 # Note: whether color by library is on or off, we always need to color 466 # the node back to the frozen color after deselecting this node 467 col = '#b6d3f6' # light blue color 468 self.unfreezeCol = self.setColor(col) 469 self.deselectOptions["fill"] = col
470 471
472 - def unfreeze(self):
473 # here we have to be more careful and check which mode is currently 474 # on: color node by library 0/1. Depending on the status we need 475 # to set a different deselect color 476 #self.setColor(self.unfreezeCol) 477 if self.editor.colorNodeByLibraryTk.get() == 1: 478 if self.library is not None: 479 col = self.library.color 480 else: col = "gray85" 481 else: 482 col = "gray85" 483 self.deselectOptions['fill'] = col 484 self.setColor(col)
485 #self.schedule() 486 487
488 - def run(self, force=0):
489 """run this node. 490 if force is set, the node will run regardless of the availability of new data. 491 return 0 if run failed. (Thread safe)""" 492 493 if force or self.forceExecution: 494 self.forceExecution = 1 495 elif not self.autoRun or self.frozen: 496 return 497 # acquire the node's lock before doing anything to it 498 #print " noderun acquire runLock" 499 500 ed = self.getEditor() 501 502 self.runLock.acquire() 503 504 ## import traceback 505 ## print '******************', self.name 506 ## traceback.print_stack() 507 508 try: 509 ## FIXME ... thisis also done in self.computeFunction() 510 511 # if forceExecute is set, we run the node regardless of newdata 512 # else we test that at least one parent present new data before 513 # running the node 514 ## if self.forceExecution == 0: 515 ## newdata = 0 516 ## for p in self.inputPorts: 517 ## if p.hasNewData(): 518 ## newdata = 1 519 ## break 520 ## if newdata == 0: 521 ## #print 'No new data for', self.name 522 ## self.runLock.release() 523 ## return 1 524 525 if ed.flashNodesWhenRun: 526 c = self.iconMaster 527 if c is not None: 528 c.tk.call((c._w, 'itemconfigure', self.innerBox, 529 '-outline', '#FF0000', '-width', 4)) 530 col = '#000000' 531 if sys.platform !='win32' or not \ 532 ed.withThreads: 533 ed.update_idletasks() 534 535 stat = self.computeFunction() 536 537 if ed.flashNodesWhenRun and stat!='Stop': 538 if col=='#557700': # after successfull run of failed node 539 col = self.colorBeforeFail 540 #self.dirty = 1 541 #else: 542 #self.dirty = 0 543 if c is not None: 544 c.tk.call((c._w, 'itemconfigure', self.innerBox, 545 '-outline', col, '-width', 1)) 546 547 self.forceExecution = 0 548 549 except: 550 print 551 print "***********************************************************" 552 print "*** ERROR while executing node: ", self.name 553 print "***********************************************************" 554 self.forceExecution = 0 555 traceback.print_exc() 556 if ed.flashNodesWhenRun: 557 if col != '#557700': 558 self.colorBeforeFail = col 559 c.tk.call((c._w, 'itemconfigure', self.innerBox, 560 '-outline', '#557700')) 561 #self.setColor('#557700') 562 self.runLock.release() 563 return 'stop' 564 565 self.runLock.release() 566 567 if stat == 'Go': 568 # force execution of children nodes of trigger output port 569 if len(self.specialOutputPorts) \ 570 and len(self.specialOutputPorts[0].children): 571 self.scheduleChildren(portList=[self.specialOutputPorts[0]]) 572 573 if stat is None: 574 return 1 575 else: 576 return stat
577 578
579 - def postItemMenu(self, event):
580 if self.network.postedMenu: 581 self.network.postedMenu.unpost() 582 self.network.postedMenu = None 583 elif self.menu: 584 self.menu.post(event.x_root, event.y_root) 585 self.network.postedMenu = self.menu
586 587
588 - def editNodeMenu(self, type, *args, **kw):
589 apply( eval("self.menu."+type), args, kw )
590 591
592 - def __repr__(self):
593 return '<%s %s>'%(self.__class__.__name__, self.name)
594 595
596 - def rename(self, name, tagModified=True):
597 self.name=name 598 if tagModified is True: 599 self._setModified(True)
600 601
602 - def beforeAddingToNetwork(self, network):
603 # get's called when a node is instanciated in a network 604 #print 'beforeAddingToNetwork:', self.name 605 pass
606 607
608 - def afterAddingToNetwork(self):
609 # get's called when a node is instanciated in a network 610 #print 'afterAddingToNetwork:', self.name 611 pass
612 613
615 # get's called when a node is deleted from a network 616 #print 'beforeRemovingFromNetwork:', self.name 617 pass
618 619
620 - def afterRemovingFromNetwork(self):
621 # get's called when a node is deleted from a network 622 #print 'afterRemovingFromNetwork:', self.name 623 pass
624 625
626 - def highlight(self, event=None):
627 pass
628 629
630 - def unhighlight(self, event=None):
631 pass
632 633
634 - def select(self):
635 self.selected = 1 636 if self.iconMaster: 637 self.iconMaster.addtag_withtag('selected', self.iconTag)
638 639
640 - def deselect(self):
641 self.selected = 0 642 if self.iconMaster: 643 self.iconMaster.dtag(self.iconTag, 'selected')
644 645
646 - def deleteIcon(self):
647 # delete what the item has to delete 648 ed = self.getEditor() 649 if self.iconMaster: 650 self.iconMaster.delete(self.iconTag) 651 self.iconTag = None 652 653 # Note: I am not sure if we have to destroy the menu at all 654 # to avoid memory leaks). But if we do destroy, we have to set 655 # postedMenu to None if menu == postedMenu 656 if ed.currentNetwork is None: 657 return 658 postedMenu = ed.currentNetwork.postedMenu 659 if self.menu: 660 if postedMenu and postedMenu == self.menu: 661 ed.currentNetwork.postedMenu = None 662 self.menu.destroy() 663 self.menu = None
664 665
666 - def gettags(self):
667 if self.iconMaster and self.iconTag: 668 return self.iconMaster.gettags(self.iconTag)
669 670
671 - def buildIcons(self, canvas):
672 """method to be implmented by subclass 673 this method should create the geometric primitives for this item 674 and tag them all with self.iconTag 675 define its uniqueTag 676 set its tags 677 """ 678 679 ed = self.getEditor() 680 if ed is not None: 681 self.paramPanel = ParamPanel(self, master=None, title=self.name, 682 applyFun=self.schedule_cb, 683 dismissFun=self.hideParamPanel) 684 if self.menu is None: 685 # got rid of useless tearoff 686 self.menu = Tkinter.Menu(ed, title=self.name, tearoff=False) 687 self.menu.add_separator() 688 689 self.menu.bind("<Any-Button>", ed.myButtonPress, '+') 690 self.iconMaster = canvas
691 692
693 - def hideParamPanel(self, event=None):
694 self.paramPanel.hide()
695 696
697 - def schedule_cb(self):
698 # virtual method, has to be implemented by subclass 699 pass
700 701
702 - def introspect(self):
703 if self.objectBrowser is None: 704 self.objectBrowser = ObjectBrowser( 705 self, rootName=self.name, 706 title='Introspect Node: %s'%self.name, 707 refresh=self._introspectCB) 708 else: 709 self.objectBrowser.show()
710 711
712 - def _introspectCB(self):
713 """helper method used for the introspect data GUI""" 714 return self
715 716
717 - def deletePort(self, p, resize=True):
718 """Delete a port from a node. Removes it from the list of ports, 719 updates the portsDescr list and return the descriptin of the deleted port, 720 renumber all ports after this ports and mark them modified. Update code. 721 """ 722 # NOTE: this method moved here because we want to subclass it in 723 # NetworkNode and in MacroBase 724 725 net = self.network 726 # save configuration of port about to be deleted 727 cfg = p.configure() 728 729 from ports import InputPort, OutputPort, SpecialInputPort, \ 730 SpecialOutputPort 731 732 # delete port Icon 733 if p.visible: 734 p.deleteIcon() 735 736 # remove port from list 737 ports = None 738 if isinstance(p, SpecialInputPort): 739 self.specialInputPorts.remove(p) 740 741 elif isinstance(p, SpecialOutputPort): 742 self.specialOutputPorts.remove(p) 743 744 elif isinstance(p, InputPort): 745 ports = self.inputPorts 746 if p.widget: 747 p.deleteWidget() 748 p.deleteIcon() 749 self.inputPorts.remove(p) 750 del self.inputPortsDescr[p.number] 751 752 elif isinstance(p, OutputPort): 753 ports = self.outputPorts 754 self.outputPorts.remove(p) 755 del self.outputPortsDescr[p.number] 756 757 else: 758 print "FAILED. Port is neither of class InputPort or OuputPort!" 759 return 760 761 # remove all connections from this port 762 self.network.deleteConnections(p.connections, 0) 763 764 p.relposx = p.computePortPosX() 765 766 # renumber the ports 767 if ports: 768 for i in range(p.number, len(ports)): 769 port = ports[i] 770 port.number = i 771 if port.visible: 772 port.updateIconPosition() 773 774 # delete circular referneces 775 del p.data 776 del p.node 777 del p.network 778 del p.vEditor 779 del p 780 781 if resize: 782 self.autoResizeX()
783