NetworkEditor-1.5.7~rc1+cvs.20140424/0000755000175000017500000000000012326213117016474 5ustar moellermoellerNetworkEditor-1.5.7~rc1+cvs.20140424/MANIFEST.in0000644000175000017500000000070610254610207020234 0ustar moellermoeller# This list all other files to be included in the distribution # which are not python modules # include, exclude.... which are not described in the setup.py. include MANIFEST.in # list of the non python module to be included in the distribution include NetworkEditor/RELNOTES include NetworkEditor/LICENSE include NetworkEditor/doc.tar.gz # include all the CVS directories include NetworkEditor/CVS/* include NetworkEditor/Tests/CVS/* include version.pyNetworkEditor-1.5.7~rc1+cvs.20140424/setup.py0000644000175000017500000000655112232310177020215 0ustar moellermoeller#!/usr/bin/env python from distutils.core import setup from distutils.command.sdist import sdist from distutils.command.install_data import install_data from glob import glob import os ######################################################################## # Had to overwrite the prunrefile_list method of sdist to not # remove automatically the RCS/CVS directory from the distribution. ######################################################################## class modified_sdist(sdist): def prune_file_list(self): """ Prune off branches that might slip into the file list as created by 'read_template()', but really don't belong there: * the build tree (typically 'build') * the release tree itself (only an issue if we ran 'sdist previously with --keep-temp, or it aborted) """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) class modified_install_data(install_data): def run(self): install_cmd = self.get_finalized_command('install') self.install_dir = getattr(install_cmd, 'install_lib') return install_data.run(self) ######################################################################## # list of the python packages to be included in this distribution. # sdist doesn't go recursively into subpackages so they need to be # explicitaly listed. # From these packages only the python modules will be taken packages = ['NetworkEditor', 'NetworkEditor.Tests', ] # list of the python modules not part of a package. Give the path and the # filename without the extension. i.e you want to add the # test.py module which is located in MyPack/Tests/ you give # 'MyPack/Tests/test' py_modules = [] # list of the files that are not python packages but are included in the # distribution and need to be installed at the proper place by distutils. # The list in MANIFEST.in lists is needed for including those files in # the distribution, data_files in setup.py is needed to install them # at the right place. data_files = [] def getDataFiles(file_list, directory, names): fs = [] for name in names: ext = os.path.splitext(name)[1] #print directory, name, ext, len(ext) if ext !=".py" and ext !=".pyc": fullname = os.path.join(directory,name) if not os.path.isdir(fullname): fs.append(fullname) if len(fs): file_list.append((directory, fs)) os.path.walk("NetworkEditor", getDataFiles, data_files) print 'data_files', data_files # description of what is going to be included in the distribution and # installed. from version import VERSION setup (name = 'NetworkEditor', version = VERSION, description = "an OpenGL based 3D geometry viewer python package", author = 'Molecular Graphics Laboratory', author_email = 'sanner@scripps.edu', download_url = 'http://www.scripps.edu/~sanner/software/packager.html', url = 'http://www.scripps.edu/~sanner/software/index.html', packages = packages, py_modules = py_modules, data_files = data_files, cmdclass = {'sdist': modified_sdist, 'install_data': modified_install_data }, ) NetworkEditor-1.5.7~rc1+cvs.20140424/version.py0000644000175000017500000000002011475262407020535 0ustar moellermoellerVERSION="1.5.6" NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/0000755000175000017500000000000012326213130021267 5ustar moellermoellerNetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/.packager.py0000644000175000017500000000547007770420500023512 0ustar moellermoeller# The .packager.py is used by the DistTools package to get the files to be # included in a distribution. def getFiles(root, what = 'all', plat=None): """ files <- getFiles(root, what='all', plat=None) files -- list of the files to be included in the download arguments: root -- path to the package. This is used by glob to get all the python modules. what -- string that can be 'all' and 'supported' to specified what files to include in the distribution. By default 'all' the files are added. plat -- platform ('linux2', 'irix646', 'sunos5' etc...) """ import os from glob import glob # 1- Specify the list of all the Python module allPyModule = ["*.py"] # 2- Specify the list of the non supported Python module. These files # will be removed from the release of the supported python modules. pynotsupported = [] # 3- Specify the documentation files and directories to be included in the # release docFiles = []#["doc/"] # 4-Specify the extraFiles to be included in the release. extraFiles = ["CVS", "RELNOTES"] # 5-Specify the testFiles to be included in the release. testFiles = ['Tests/*.py', 'Tests/CVS', 'LICENSE'] ######################################################### ## Where things are done for you . ######################################################### # if some files need to be removed, we need the exact list of the pymodule. if len(pynotsupported): # store the path of the current directory olddir = os.getcwd() os.chdir(root) files = [] # we use glob to get the exact list of files. for p in allPyModule: files = files + glob(p) allPyModule = files files = [] # need to get the list of the files ... no wild card possible. for p in pynotsupported: files = files + glob(p) pynotsupported = files os.chdir(olddir) # Creation of the proper list of files depending on the value of what if what == 'supported' and len(pynotsupported): # need to remove the non supported python files from all the python # files # These are the keys used for to make the releases... supportedFiles = filter(lambda x, l = pynotsupported: not x in l, allPyModule) return supportedFiles + testFiles + extraFiles elif what == 'all' or ( what == 'supported' and not len(pynotsupported)): # Other wise just add the documentation, test and extra files to all # the python modules. allFiles= allPyModule + docFiles + testFiles + extraFiles return allFiles elif what == 'documentation': return docFiles else: return [] NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/Editor.py0000644000175000017500000016400112223052334023074 0ustar moellermoeller######################################################################### # # Date: Nov. 2001 Authors: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner, Daniel Stoffler and TSRI # ######################################################################### # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/Editor.py,v 1.87 2013/10/02 17:16:12 sanner Exp $ # # $Id: Editor.py,v 1.87 2013/10/02 17:16:12 sanner Exp $ # import os, Tkinter, Pmw, ImageTk import string, types from mglutil.gui.BasicWidgets.Tk.customizedWidgets import kbComboBox from mglutil.util.callback import CallbackFunction from mglutil.gui.BasicWidgets.Tk.thumbwheel import ThumbWheel from NetworkEditor.ports import InputPort, OutputPort from NetworkEditor.widgets import WidgetEditor from NetworkEditor.widgets import PortWidget, widgetsTable, createWidgetDescr from NetworkEditor.gridEditor import GridEditor from mglutil.gui.BasicWidgets.Tk.colorWidgets import ColorChooser from mglutil.util.packageFilePath import findFilePath ICONPATH = findFilePath('Icons', 'NetworkEditor') ## TODO # # expose more things, like port color, tags, Tk options # expose widgets # write source code describing node # Disable OK CANCEL in node editor while port editors are open # class ObjectEditor: """Base class for editors such as node and port editor""" def __init__(self, object, type, master=None): """Contructor of the editor. object is the object the editor exposes (i.e. a node of a port for instance). type is a string used to name the Editor window 'Node Editor' or 'Port Editor'. master master is an optional container into which to place the editor. If ommited the editor will appear in its own top level. """ self.nbEditorWindows = 0 # count number of sub windows open # when more than 1 disable ok apply cancel # buttons self.obj = object # obejct to be edited (node or port) self.type = type if master is None: master = Tkinter.Toplevel() self.createForm(master) self.values = {} # dict of values describing state of the form self.modal = 0 def createForm(self, master): """Build the form. Every editor has a frame called top followed by an Entry allowing to change the edited-object's name. Every editor also has Ok, Apply, Cancel buttons as the bottom of the form. These buttons are in a frame called okFrame which is a child of top. """ self.top = Tkinter.Frame(master) self.top.master.title(self.type+' Editor: '+self.obj.name) self.top.master.protocol('WM_DELETE_WINDOW',self.Dismiss) # node name self.nameGroup = w = Pmw.Group(self.top, tag_text=self.type+' Name') self.nameTk = Tkinter.StringVar() self.nameEntry = Tkinter.Entry( w.interior(), textvariable=self.nameTk) self.nameEntry.bind('', self.nameEntryChange_cb) self.nameTk.set(self.obj.name) self.nameEntry.pack(padx = 6, pady = 6, expand='yes', fill='x') w.pack(padx = 6, pady = 6, expand='yes', fill='both') self.addOkApplyCancel() def addOkApplyCancel(self): """Add the Ok, Apply and Cancel buttons """ self.okFrame = f = Tkinter.Frame(self.top) self.okButton = Tkinter.Button(f, text='OK', command=self.OK) self.okButton.pack(side='left', expand=1, fill='x') self.applyButton = Tkinter.Button(f, text='Apply', command=self.Apply) self.applyButton.pack(side='left', expand=1, fill='x') self.cancelButton = Tkinter.Button(f, text='Cancel', command=self.Cancel) self.cancelButton.pack(side='right', expand=1, fill='x') f.pack(side='bottom', expand=1, fill='x') self.top.pack(padx=10, pady=10, expand='yes', fill='both') def manageEditorButtons(self): """Helper function to enable/disable the ok apply cancel buttons. These buttons are disabled if another window is opened and needs to be closed before this editor can be closed. """ if self.nbEditorWindows > 0: self.applyButton['state'] = 'disabled' self.okButton['state'] = 'disabled' self.cancelButton['state'] = 'disabled' else: self.applyButton['state'] = 'normal' self.okButton['state'] = 'normal' self.cancelButton['state'] = 'normal' def nameEntryChange_cb(self, event=None): # callback fron key in Entry holding name pass def Apply(self, event=None): """Callback for Apply Button""" pass def OK(self, event=None): """Callback for OK Button, calls Apply and Dismiss""" self.Apply() self.Dismiss() def Cancel(self): """Callback for Cancel Button""" self.Dismiss() def Dismiss(self, event=None): """Destroy edit form and reset objcet's point to editor""" if not self.modal: self.top.master.destroy() self.obj.objEditor = None else: self.top.master.quit() def go(self): """method for starting an editor in modal form (i.e. the application blocks until the editor is dismissed """ self.modal=1 self.top.master.mainloop() self.top.master.destroy() return self.values class NodeEditor(ObjectEditor): """Node Editor. Allows to rename, add input and output ports, modifed the node's functions and start editors for ports and widgets""" def __init__(self, node, master=None): ObjectEditor.__init__(self, node, 'node', master) self.gridEditor = None # widget grid configuration GUI self.funcEditorDialog = None # edit source code GUI def nameEntryChange_cb(self, event=None): """apply the new name to the node and remember it has been modified""" name = self.nameTk.get() self.obj.configure(name=name) if self.funcEditorDialog is not None: self.funcEditorDialog.top.title( "Code editor: Node %s"%self.obj.name) def Apply(self, event=None): self.nameEntryChange_cb() self.Dismiss() def Dismiss(self, event=None): if self.funcEditorDialog: self.funcEditorDialog.hide() self.funcEditorDialog = None if self.gridEditor is not None: self.gridEditor.Cancel_cb() self.gridEditor = None for p in self.obj.inputPorts: self.deletePortButtons(p) for p in self.obj.outputPorts: self.deletePortButtons(p) ObjectEditor.Dismiss(self) def createForm(self, master): """Create standard editor form and add a Pmw Group for input ports, one for output ports and a check button for viewing the source node's code. """ ObjectEditor.createForm(self, master) # input Ports w = Pmw.Group(self.top, tag_pyclass=Tkinter.Button, tag_command=self.newInputPort, tag_text='Add Input Port') ipf = self.ipFrame = Tkinter.Frame(w.interior(),) Tkinter.Label(ipf, text='port #').grid(row=0, column=0, sticky='ew') Tkinter.Label(ipf, text='name').grid(row=0, column=1, sticky='ew') Tkinter.Label(ipf, text='edit\nport').grid(row=0, column=2, sticky='ew') Tkinter.Label(ipf, text='edit\nwidget').grid(row=0, column=3, sticky='ew') Tkinter.Label(ipf, text='del.\nport').grid(row=0, column=4, sticky='ew') for p in self.obj.inputPorts: self.addPortToForm(p, ipf) self.ipFrame.pack(expand=1, fill='x', padx=3, pady=3) w.pack(padx=6, pady=6, expand='yes', fill='both') # output Ports w = Pmw.Group(self.top, tag_pyclass=Tkinter.Button, tag_command=self.newOutputPort, tag_text='Add Output Port') opf = self.opFrame = Tkinter.Frame(w.interior(),) Tkinter.Label(opf, text='port #').grid(row=0, column=0, sticky='ew') Tkinter.Label(opf, text='name').grid(row=0, column=1, sticky='ew') Tkinter.Label(opf, text='edit\nport').grid(row=0, column=2,sticky='ew') Tkinter.Label(opf, text='del.\nport').grid(row=0, column=4, sticky='ew') for p in self.obj.outputPorts: self.addPortToForm(p, opf) self.opFrame.pack(expand=1, fill='x', padx=3, pady=3) w.pack(padx = 6, pady = 6, expand='yes', fill='both') # widget grid editor w = Pmw.Group(self.top, tag_text='widgets grid config') self.editGridVarTk = Tkinter.IntVar() self.editGridButton = Tkinter.Checkbutton( w.interior(), text='Edit ...', var=self.editGridVarTk, command=self.editGrid_cb ) self.editGridButton.pack(expand='yes', fill='x', padx=3, pady=3) w.pack(padx = 6, pady = 6, expand='yes', fill='both') from NetworkEditor.items import ImageNode # ImageNode Appearance if isinstance(self.obj, ImageNode): w = Pmw.Group(self.top, tag_text='Node Appearance') frame = Tkinter.Frame(w.interior()) Tkinter.Label(frame, text="fill color ").grid(row=0, column=0, sticky='ne') photo = ImageTk.PhotoImage( file=os.path.join(ICONPATH, 'colorChooser24.png')) cb = CallbackFunction(self.setColor, 'fillColor') b = Tkinter.Button(frame, command=cb, image=photo) b.photo = photo b.grid(row=0, column=1, sticky='ne') cb = CallbackFunction(self.setOpacity, 'fillColor') fillOpacityThumbwheel = ThumbWheel( frame, labCfg={'text':'Opac.', 'side':'left'}, showLabel=1, width=40, height=14, min=.001, max=1., type=float, value = self.obj.nodeStyle.fillColor[3], callback = cb, continuous=True, oneTurn=1., wheelPad=0) fillOpacityThumbwheel.grid(row=0, column=2, sticky='ne') Tkinter.Label(frame, text="outline color ").grid(row=1, column=0, sticky='ne') photo = ImageTk.PhotoImage( file=os.path.join(ICONPATH, 'colorChooser24.png')) cb = CallbackFunction(self.setColor, 'outlineColor') b = Tkinter.Button(frame, command=cb, image=photo) b.photo = photo b.grid(row=1, column=1, sticky='ne') cb = CallbackFunction(self.setOpacity, 'outlineColor') outlineOpacityThumbwheel = ThumbWheel( frame, labCfg={'text':'Opac.', 'side':'left'}, showLabel=1, width=40, height=14, min=.001, max=1., type=float, value = self.obj.nodeStyle.fillColor[3], callback = cb, continuous=True, oneTurn=1., wheelPad=0) outlineOpacityThumbwheel.grid(row=1, column=2, sticky='ne') frame.pack() w.pack(padx = 6, pady = 6, expand='yes', fill='both') # compute function w = Pmw.Group(self.top, tag_text='compute function') self.editButtonVarTk = Tkinter.IntVar() self.editButton = Tkinter.Checkbutton( w.interior(), text='Edit ...', var=self.editButtonVarTk, command=self.editFunction_cb ) self.editButton.pack(expand='yes', fill='x', padx=3, pady=3) w.pack(padx = 6, pady = 6, expand='yes', fill='both') self.manageEditorButtons() def setColor(self, what): def cb(color): self.obj.nodeStyle.configure(**{what:color}) self.obj.currentNodeStyle = self.obj.nodeStyle self.obj.redrawNode() cc = ColorChooser(immediate=1, commands=cb, title='Node %s color'%what) cc.pack(expand=1, fill='both') def setOpacity(self, what, value): if what=='fillColor': self.obj.nodeStyle.fillColor[3] = value elif what=='outlineColor': self.obj.nodeStyle.outlineColor[3] = value self.obj.currentNodeStyle = self.obj.nodeStyle self.obj.redrawNode() self.currentNodeStyle = None def addPortToForm(self, port, frame): """create a new line of widgets for a new port in the node editor""" line = port.number+1 port.portLabelTk = Tkinter.Label(frame, text=str(port.number)) port.portLabelTk.grid(row=line, column=0, sticky='ew') port.portNameTk = Tkinter.Label(frame, text=str(port.name)) port.portNameTk.grid(row=line, column=1, sticky='w') cb1 = CallbackFunction( self.editPort, port) cb2 = CallbackFunction( self.deletePort, port) cbvar = Tkinter.IntVar() if port.objEditor is not None: self.nbEditorWindows += 1 cbvar.set(int(not port.objEditor==None)) port.portEditCB = Tkinter.Checkbutton(frame, command=cb1, variable=cbvar) port.portEditCB.var = cbvar port.portEditCB.grid(row=line, column=2, sticky='ew') port.portDelCB = Tkinter.Checkbutton(frame, command=cb2) port.portDelCB.grid(row=line, column=4, sticky='ew') if port.widget: cb = CallbackFunction( self.editWidget, port) port.editWtk = Tkinter.Checkbutton(self.ipFrame,command=cb) port.editWtk.grid(row=port.number+1, column=3, sticky='ew') def newInputPort(self, event=None): """create a new input port with a default name""" self.obj.addSaveNodeMenuEntries() port = apply( self.obj.addInputPort, (), {} ) port._setModified(True) port.editWtk = None port.delWtk = None # add port to node edition form self.addPortToForm( port, self.ipFrame ) # and update funcEditorDialog if self.funcEditorDialog is not None: self.funcEditorDialog.settext(self.obj.sourceCode) def newOutputPort(self, event=None): # create a new output port with a default name self.obj.addSaveNodeMenuEntries() port = apply( self.obj.addOutputPort, (), {} ) port._setModified(True) # add port to node edition form self.addPortToForm( port, self.opFrame ) # and update funcEditorDialog if self.funcEditorDialog is not None: self.funcEditorDialog.settext(self.obj.sourceCode) def deletePortButtons(self, port): """removes widgets for this port from node editor""" node = port.node # remove buttons fot this port form the node editor panel: if port.portLabelTk is not None: port.portLabelTk.destroy() if port.portNameTk is not None: port.portNameTk.destroy() if port.portEditCB is not None: port.portEditCB.destroy() if port.portDelCB is not None: port.portDelCB.destroy() if port.widget: if port.editWtk is not None: port.editWtk.destroy() port.portLabelTk = None port.portNameTk = None port.portEditCB = None port.portDelCB = None port.editWtk = None def deletePort(self, port, event=None): """delete a port""" self.obj.addSaveNodeMenuEntries() # delete port editor is any if port.objEditor: port.objEditor.Cancel() # if the node editor is up update it if port.node.objEditor: self.deletePortButtons(port) # get a handle to port befoer we lose it in deletePort node = port.node # delete port editor is any if port.objEditor: port.objEditor.Cancel() # delete the port from the node's list of ports self.obj.deletePort(port, resize=True) # renumber remaining input ports in editor panel if isinstance(port, InputPort): for p in node.inputPorts: p.portLabelTk.configure(text=str(p.number)) # renumber remaining output ports in editor panel elif isinstance(port, OutputPort): for p in node.outputPorts: p.portLabelTk.configure(text=str(p.number)) # and update funcEditorDialog if self.funcEditorDialog is not None: self.funcEditorDialog.settext(self.obj.sourceCode) def editGrid_cb(self, event=None): """open a widget grid configuration editor""" # Note, every time activate the checkbutton, we want to build a fresh # window, since in the meantime, somebody could have added a new widget if self.editGridVarTk.get() == 1: self.nbEditorWindows += 1 self.manageEditorButtons() self.gridEditor = GridEditor(node=self.obj) else: self.gridEditor.Cancel_cb() self.gridEditor = None def editFunction_cb(self, event=None): """callback function of edit source code check button. Manages button's state, and status of Ok, Apply and Cancel buttons """ if self.editButtonVarTk.get() == 1: self.nbEditorWindows += 1 self.manageEditorButtons() if self.funcEditorDialog is None: self.displaySourceCode() else: self.funcEditorDialog.show() return else: self.funcEditorDialog.cancelCmd() def displaySourceCode(self): """If no source code editor is created yet build one, else just show it If idlelib is found we use it, else we use default Pmw-based widget. """ if self.funcEditorDialog is None: self.funcEditorDialog = SourceCodeEditor( node=self.obj, editor=self) else: self.funcEditorDialog.show() def editPort(self, port, event=None): # create a port editor if port.objEditor is not None: status = port.portEditCB.var.get() if status == 1: #port.portEditCB.toggle() port.objEditor.top.master.deiconify() else: port.objEditor.top.master.withdraw() return else: port.objEditor = ipEditor = PortEditor(port) self.nbEditorWindows += 1 self.manageEditorButtons() def editWidget(self, port, event=None): """start widget editor""" if port.widget.objEditor: port.widget.objEditor.Cancel_cb() else: port.widget.edit() class PortEditor(ObjectEditor): def __init__(self, port, master=None): """PortEditor constructor. Port is the port which we want to edit. """ if isinstance(port, InputPort): title = 'Input Port' elif isinstance(port, OutputPort): title = 'Output Port' else: title = 'Port' ObjectEditor.__init__(self, port, title, master) port.objEditor = self self.node = port.node self.callbackEditorDialog = None # code editor window self.abortApply = 0 # setWidget() can remove port nodeEditor = self.node.objEditor if nodeEditor is not None: port.portEditCB.select() nodeEditor.nbEditorWindows -= 1 nodeEditor.manageEditorButtons() def createForm(self, master): """Create default form and add widgets for setting data type, start the widget editor, set the required, singleConnection and toolTip """ port = self.obj ObjectEditor.createForm(self, master) #w = Tkinter.Frame(self.top, relief='groove') # add protocol for killing this window self.top.master.protocol("WM_DELETE_WINDOW", self.Cancel) w = Pmw.Group(self.top, tag_text='Port options') # datatype vpe = port.getEditor() l = vpe.typeManager.portTypeInstances.keys() l.sort() self.dataType = kbComboBox( w.interior(), label_text='data type:', labelpos='w', entryfield_entry_width=14, scrolledlist_items = l ) if port.datatypeObject: self.dataType.setentry(port.datatypeObject['name']) self.dataType.grid() # Edit Callbacks w2 = Pmw.Group(self.top, tag_text='Port callbacks') l2 = ['beforeConnect', 'afterConnect', 'beforeDisconnect', 'afterDisconnect'] self.chooseCallback = kbComboBox( w2.interior(), label_text='edit source code', labelpos='w', entryfield_entry_width=14, scrolledlist_items = l2, selectioncommand=self.editCallback) self.chooseCallback.grid() w1 = None if isinstance(port, InputPort): # required var = Tkinter.IntVar() var.set(port.required) tk = Tkinter.Checkbutton( w.interior(), text='required', indicatoron=1, variable=var) tk.var = var tk.grid() self.requiredTk = tk # singleConnection from NetworkEditor.macros import MacroNode if isinstance(port.node, MacroNode): # to avoid multiple list enclosure, MacroInputNode # must always be singleConnection. # to have multiple connections, # the solution is to duplicate the input port in the macronode # (there is no such need for the MacroOutputNode) connTypes = ['True'] else: connTypes = ['True', 'False', 'auto'] self.singleConnectionTk = kbComboBox( w.interior(), label_text='single connection:', labelpos='w', scrolledlist_items=connTypes, entryfield_entry_width=15 ) status = str(port.singleConnection) self.singleConnectionTk.selectitem(status) self.singleConnectionTk.grid() # widget section w1 = Pmw.Group(self.top, tag_text='Widget') ## # port has a widget add Unbind button ## if port.widget: ## var = Tkinter.IntVar() ## tk = Tkinter.Checkbutton( ## w.interior(), text='single connection', ## variable=var, indicatoron=1) ## tk.var = var ## tk.grid() ## self.unbindWidget = tk # BIND WIDGET widgetList = [] if port.widget: # this port has a widget widgetList.append('Unbind') if port._previousWidgetDescr: widgetList.append('Rebind') widgetList = widgetList + widgetsTable.keys() widgetList.sort() # widget self.widgetType = kbComboBox( w1.interior(), label_text='Type:', labelpos='w', scrolledlist_items=widgetList, entryfield_entry_width=15 ) if port.widget: self.widgetType.set(port.widget.__class__.__name__) self.widgetType.grid(row=0, column=0, padx=6, pady=6) masterList = ['ParamPanel', 'Node'] if len(self.obj.network.userPanels.keys()): masterList += self.obj.network.userPanels.keys() self.widgetMaster = kbComboBox( w1.interior(), label_text='Place in:', labelpos='w', scrolledlist_items=masterList, entryfield_entry_width=15, selectioncommand=self.setMaster_cb) # self.setMaster_cb() method will update the GUI but not # call setMaster(), only Apply button will do that self.widgetMaster.grid(row=1, column=0, padx=6, pady=6) self.widgetMaster.selectitem('ParamPanel') # set the choice to current master if port.widget: if port.widget.inNode is True: self.widgetMaster.selectitem('Node') else: if port.widget.master in self.obj.network.userPanels.keys(): self.widgetMaster.selectitem(port.widget.master) self.editWidgetVarTk = Tkinter.IntVar() self.editWidgetButton = Tkinter.Checkbutton( w1.interior(), text='Edit Widget...', var=self.editWidgetVarTk, command=self.toggleWidgetEditor) self.editWidgetButton.grid() if self.obj.widget is not None: if self.obj.widget.objEditor: self.editWidgetVarTk.set(1) w.pack(padx = 6, pady = 6, expand='yes', fill='both') w2.pack(padx = 6, pady = 6, expand='yes', fill='both') if w1: w1.pack(padx = 6, pady = 6, expand='yes', fill='both') self.tt = Pmw.ScrolledText(self.top, usehullsize=1, borderframe=1, labelpos = 'nw', label_text='ToolTip', hull_width = 300, hull_height = 100, text_wrap='none' ) if port.balloon: self.tt.settext(port.balloon) self.tt.pack(padx=5, pady=5, fill='both', expand=1) self.top.pack(padx=10, pady=10, expand='yes', fill='both') def nameEntryChange_cb(self, event=None): """apply the new name to the node and remember it has been modified""" pass # disabled callback because we want this to run only if Apply is pressed ## name = self.nameTk.get() ## port = self.obj ## if name != port.name: ## port.configure(name=name) def editCallback(self, event=None): mode = self.chooseCallback.get() if mode in self.obj.callbacks.keys(): self.displayCallbackSourceCode(mode) def displayCallbackSourceCode(self, mode): if self.callbackEditorDialog is not None: self.callbackEditorDialog.hide() self.callbackEditor = None self.callbackEditorDialog = CallbackEditor( port=self.obj, mode=mode) def OK(self, event=None): ObjectEditor.OK(self) nodeEditor = self.obj.node.objEditor if nodeEditor: nodeEditor.nbEditorWindows -= 1 nodeEditor.manageEditorButtons() self.obj.portEditCB.toggle() def Cancel(self): """Callback for Cancel Button""" nodeEditor = self.obj.node.objEditor port = self.obj ObjectEditor.Cancel(self) if nodeEditor: nodeEditor.nbEditorWindows -= 1 nodeEditor.manageEditorButtons() port.portEditCB.toggle() def Apply(self, event=None): """Callback for Apply Button""" opts = {} port = self.obj name = self.nameTk.get() if name != self.obj.name: port.configure(name=name) # and rename entry in node editor window if hasattr(self.obj, 'portNameTk') and \ self.obj.portNameTk is not None: self.obj.portNameTk.configure(text=name) datatype = self.dataType.get() if datatype != port.datatypeObject['name']: opts['datatype'] = datatype if isinstance(port, InputPort): required = self.requiredTk.var.get()==1 if required != port.required: opts['required'] = required singleConnection = self.singleConnectionTk.get() if singleConnection == 'True': singleConnection = True elif singleConnection == 'False': singleConnection = False if singleConnection != port.singleConnection: opts['singleConnection'] = singleConnection widgetName = self.widgetType.get() if widgetName == 'Rebind': port.rebindWidget() widgetList = ['Unbind'] + widgetsTable.keys() widgetList.sort() self.widgetType.setlist(widgetList) self.widgetType.set('Unbind') elif widgetName == 'Unbind': port.unbindWidget() widgetList = ['Rebind'] + widgetsTable.keys() widgetList.sort() self.widgetType.setlist(widgetList) self.widgetType.set('Rebind') # destroy edit widget checkbutton to node editor if port.editWtk: port.editWtk.destroy() port.editWtk = None elif widgetName: if port.widget: currentWidget = port.widget oldwname = currentWidget.__class__.__name__ else: currentWidget = None oldwname = None if widgetName != currentWidget.__class__.__name__: # new widget editWidget = self.editWidgetVarTk.get() if editWidget: # Start Widget Editor was check so get wdescr from it form = WidgetEditor( widgetsTable[widgetName].configOpts) wdescr = form.go() wdescr['class'] = widgetName #for k,v in wdescr.items(): # print k, v else: if currentWidget: port._previousWidgetDescr=currentWidget.getDescr() wdescr = createWidgetDescr(widgetName, port._previousWidgetDescr) if currentWidget: wdescr['initialValue'] = port.widget.get() port.deleteWidget() port.createWidget(descr=wdescr) if port.widget.inNode: port.node.hideInNodeWidgets() port.node.showInNodeWidgets() # add edit widget checkbutton to node editor if applicable, # such as if we rebind a widget, or create a new one if port.widget and not port.editWtk and \ hasattr(port.node, 'objEditor') and \ port.node.objEditor is not None: nodeEditor = port.node.objEditor cb = CallbackFunction( nodeEditor.editWidget, port) port.editWtk = Tkinter.Checkbutton( nodeEditor.ipFrame,command=cb) port.editWtk.grid(row=port.number+1, column=3, sticky='ew') self.setMaster(self.widgetMaster.get()) tt = self.tt.get() if tt[-1]=='\n': tt = tt[:-1] if tt != port.balloon and len(tt)>0: opts['balloon'] = tt if len(opts): apply( port.configure, (), opts) port.node.addSaveNodeMenuEntries() def toggleWidgetEditor(self, event=None): port = self.obj if port.widget is None: self.editWidgetVarTk.set(0) return if port.widget.objEditor is None: # open form port.widget.edit() else: # close form port.widget.objEditor.Cancel_cb() def setWidget(self, widgetName): # this method does the following # update the node editor panels # update node.widgetDescr # update the port object port = self.obj node = port.node if widgetName == 'rebind from Macro': # Here we rebind from macro node to original node macroPort = port.widgetInMacro['macroPort'] wdescr = macroPort.node.widgetDescr[macroPort.name] port.widgetInMacro = {} self.rebindWidgetFromMacro(macroPort, wdescr) self.abortApply = 1 #after that the port no longer exists self.Dismiss() return elif widgetName == 'previous Node': # Here we rebind from macro node to original node wdescr = port.node.widgetDescr[port.name] port.node.objEditor.deletePortButtons(port) port.widgetInMacro = {} self.rebindWidgetFromMacro(port, wdescr) self.abortApply = 1 self.Dismiss() return if port.widget is None and port._previousWidgetDescr is None and \ widgetName is None or widgetName == '': return # if the port has a widget and the widget chooser in the panel # has the same type as the current widget we return # this happens when the apply or ok buttons are used and the # the widget combobox has not been changed if port.widget: if widgetName==port.widget.__class__.__name__: return if widgetName=='Unbind': w = 'Unbind' widgetList = ['Rebind'] + widgetsTable.keys() self.widgetType.setlist(widgetList) elif widgetName=='Rebind': w = 'Rebind' widgetList = ['Unbind'] + widgetsTable.keys() self.widgetType.setlist(widgetList) else: # if same widget as already bound: return if port.widget and widgetName==port.widget.__class__.__name__: return # build widget port.createWidget() w = port.widget if isinstance(w, PortWidget): widgetList = ['Unbind'] if port.widget: # current will become previous so widgetList.append('Rebind') widgetList = widgetList + widgetsTable.keys() self.widgetType.setlist(widgetList) port.setWidget(w) wmaster = self.masterTk.get() # and set the master here: self.setMaster(wmaster) # set the master in the widgetDescr so that setMaster does not # rebuild the widget again if Apply is pressed twice or more if wmaster == 'Node': mMaster = 'node' else: mMaster = wmaster # if we unbind, the node has no widgetDescr so we have to test here if node.widgetDescr.has_key(port.name): node.widgetDescr[port.name]['master'] = mMaster # add/remove edit widget button if not there yet if w is not None and port.editWtk is None: # widget but no button nodeEd = node.objEditor cb = CallbackFunction( nodeEd.editWidget, port) port.editWtk = Tkinter.Checkbutton(nodeEd.ipFrame,command=cb) port.editWtk.grid(row=port.number+1, column=3, sticky='ew') elif w is None and port.editWtk is not None: port.editWtk.destroy() port.editWtk = None ## if port.delWtk is None: ## cb = CallbackFunction( nodeEd.deletePort, port) ## port.delWtk = Tkinter.Checkbutton(nodeEd.ipFrame,command=cb) ## port.delWtk.grid(row=port.number+1, column=4, sticky='ew') # def widgetEditor(): # # self.masterTk = kbComboBox( # w, label_text='Master:', labelpos='w', # scrolledlist_items = []) # self.masterTk.grid(row=lastrow, column=0, columnspan=2) # # wmaster = None # if port.widget: # # MacroNodes are special: - the only widget for this port that # # can be selected is the one that has been bound from a node # # inside the macronetwork and the only master that can be # # chosen is the previous node # from NetworkEditor.macros import MacroNode # if isinstance(port.node, MacroNode): # wname = 'previous Node' # self.widgetType.setlist([wname]) # self.widgetType.selectitem(wname) # mas = port.node.widgetDescr[port.name]['origMaster'] # if mas == 'node': # mas = 'Node' # wmaster = mas # else: # name = None # for name,wid in widgetsTable.items(): # if wid==port.widget.__class__: # break # if name: # self.widgetType.selectitem(name) # # # now choose the appropriate master in the scrollbox # if wmaster: # self.updateMasterList(master=wmaster) # else: # self.updateMasterList() # # # is this a port where the widget has been bound to a macro node # elif len(port.widgetInMacro): # wname = 'rebind from Macro' # self.widgetType.setlist([wname]) # self.widgetType.selectitem(wname) # mas = port.widgetInMacro['master'] # if mas == 'node': # mas = 'Node' # self.masterTk.setlist([mas]) # self.masterTk.selectitem(mas) # # # else initialize with previousWidget's master to make sure # # that a triggered by Apply or OK places it back to the right # # panel # # elif port._previousWidgetDescr is not None: # widgetList = ['Rebind'] + widgetsTable.keys() # self.widgetType.setlist(widgetList) # # descr = port._previousWidgetDescr # if descr.has_key('master'): # wmaster = descr['master'] # if wmaster == 'node': # wmaster = 'Node' # else: # else used node's param panel (default) # wmaster = 'ParamPanel' # # self.updateMasterList(master=wmaster) # # else: # # no widget, no master available then # self.updateMasterList(clear=1) def rebindWidgetFromMacro(self, port, wdescr): cfg0 = port.widget.getConstructorOptions() cfg0['master'] = wdescr['origMaster'] # cfg0['visibleInNode'] = wdescr['origvisibleInNode'] cfg0['visibleInNodeByDefault'] = wdescr['origvisibleInNodeByDefault'] cfg1 = port.widget.configure() cfg1['master'] = wdescr['origMaster'] cfg2 = port.widget.get() origport = wdescr['origPort'] orignode = origport.node # 2) unbind widget and delete previousWidget port.setWidget('Unbind') port._previousWidgetDescr = None # 3) add widgetDescr to orignode del wdescr['origPort'] del wdescr['origMaster'] # del wdescr['origvisibleInNode'] del wdescr['origvisibleInNodeByDefault'] # 4) rebind widget to original node orignode.widgetDescr[origport.name] = wdescr origport.savedConfiguration = (cfg0, cfg1, cfg2) origport.createWidget() def setMaster_cb(self, event=None): # update GUI: if we change the master, we select in the widget list # the current widget whose master will be changed (for example, if # we are still in the mode 'unbind' or 'rebind') if self.obj.widget: self.widgetType.set(self.obj.widget.__class__.__name__) def setMaster(self, masterName): port = self.obj if port.widget is None and port._previousWidgetDescr is None: self.widgetMaster.component('entryfield').clear() if port.widget is None: return node = port.node if masterName=='Node': masterName = 'node' # save widget configuration wdescr = port.widget.getDescr() # save widget value wdescr['initialValue'] = port.widget.get() # FIXME, NOTE: IN THIS CURRENT RELEASE, WE DO NOT SUPPORT A MASTER # OTHER THAN NODE OR PARAMPANEL!! THE FOLLOWING CODE IS THEREFORE # NEVER EXECUTED. NOV-29-2003. # WE WILL RE-ACTIVATE THIS IN THE FUTURE # find if master is a Macro node masterlist = ['node', 'ParamPanel'] if len(self.obj.network.userPanels.keys()): masterlist += self.obj.network.userPanels.keys() if masterName not in masterlist: # ok, this is a macro # Note: simply changing the master of a widget does NOT work: # we have to delete the widget and reconstruct it! # Unbind/Rebind from one node to another one DOES NOT work. # 1) save configuration of this widget to the MacroNode # so that the widget can be properly rebuilt oldmaster = wdescr['master'] cfg0 = port.widget.getConstructorOptions() cfg0['master'] = 'ParamPanel' #cfg0['visibleInNode'] = 0 cfg0['visibleInNodeByDefault'] = 0 cfg1 = port.widget.configure() cfg1['master'] = 'ParamPanel' cfg2 = port.widget.get() # 2) unbind widget and delete previousWidgetDescr port.setWidget('Unbind') port._previousWidgetDescr = None self.widgetType.setlist([]) port.node.widgetDescr[port.name] = wdescr del port.node.widgetDescr[port.name] # 3) connect this node to a MacroInputNode and up the 'tree' from NetworkEditor.macros import MacroNetwork net = port.node.network portNumber = port.number origNumber = port.number macroNode = port.node # at first iteration this is of course # port.node, from then on its the macro node while isinstance(net, MacroNetwork): ipnode = net.ipNode c = net.connectNodes(ipnode, macroNode, 0, portNumber) opt = c.deselectOptions opt['stipple'] = 'gray25' apply (c.iconMaster.itemconfigure, (c.iconTag,), opt) macroNode = net.macroNode portNumber = c.port1.number-1 # macroNodes have port#-1 if macroNode.name == masterName: break net = macroNode.network # 4) add saved configuration to MacroNode mip = macroNode.inputPorts[portNumber] mip.savedConfiguration = (cfg0, cfg1, cfg2) # 5) add widget to MacroNode wdescr['master'] = 'ParamPanel' macroNode.widgetDescr[mip.name] = wdescr mip.createWidget() newwidget = macroNode.inputPorts[portNumber].widget # 6) store information in original node and macro node port.widgetInMacro = {'macroNode':macroNode, 'macroPort':mip, 'master':oldmaster, 'wdescr':wdescr} name = mip.name macroNode.widgetDescr[name]['origPort'] = port macroNode.widgetDescr[name]['origMaster'] = oldmaster #macroNode.widgetDescr[name]['origvisibleInNode'] = \ # wdescr['visibleInNode'] macroNode.widgetDescr[name]['origvisibleInNodeByDefault']=\ wdescr['visibleInNodeByDefault'] # 7) close port editor window self.abortApply = 1 self.Dismiss() return changeMaster=1 if wdescr.has_key('master') and wdescr['master'] == masterName: changeMaster = 0 # update the port description in the node oldmaster = wdescr['master'] wdescr['master'] = masterName if changeMaster: port.widget.configure(master=masterName) # unbind widget (this destroys the old widget) #port.unbindWidget() # delete the not needed previousWidgetDescr #port._previousWidgetDescr = None ## if port.node.isExpanded(): # do not bind widget to expanded node, ## # this leads to unpredictable results! ## port.node.toggleNodeExpand_cb() # create new widget #port.createWidget(descr=wdescr) # create new widget #port.node.autoResize() #if port.node.inNodeWidgetsVisibleByDefault and not \ # port.node.isExpanded(): # port.node.toggleNodeExpand_cb() def updateMasterList(self, event=None, master=None, clear=0): """this method is called when a widget is selected and when the editor panel is build""" port = self.obj if master == [] or clear: self.masterTk.component('entryfield').clear() return from NetworkEditor.macros import MacroNetwork, MacroNode mymaster = self.masterTk.get() if not master: master = mymaster master = 'ParamPanel' if not master or master == '': if port.widget: mmaster = port.node.widgetDescr[port.name].get('master', None) if mmaster: master = mmaster masterList = ['ParamPanel', 'Node'] #macronet = 0 # add all macro nodes in a 'macro tree' net = port.node.network while isinstance(net, MacroNetwork): macronode = net.macroNode masterList.append(macronode.name) net = macronode.network #macronet = 1 #if not macronet and master == 'Macro': # master = 'ParamPanel' self.masterTk.setlist(masterList) if master: self.masterTk.selectitem(master) class InputPortWidgetEditor(ObjectEditor): """Widget editor class. """ def __init__(self, widget, master=None): ObjectEditor.__init__(self, port, 'Input Port Widget', master) self.nameGroup.forget() self.createForm(master) def createForm(self, master): ObjectEditor.createForm(self, master) w = Pmw.Group(self.top, 'Widget Options') # datatype class CodeEditorPmw: """Reusable Pmw TextDialog widget to edit source code, fitted for the needs of the various code editors""" def __init__(self, master=None, title='untitled', code=None): if code is None: code = "" lines = code.split('\n') height = (len(lines)+10) * 10 if height > 300: height = 300 width = (max(map(len, lines)) + 1) * 10 if width > 600: width = 600 elif width < 120: width = 120 self.widget = Pmw.TextDialog( master, scrolledtext_labelpos='n', title=title, defaultbutton=None, buttons=(), label_text='Edit Code:', scrolledtext_usehullsize=1, scrolledtext_hull_width=width, scrolledtext_hull_height=height) self.top = self.widget self.settext(code) # add frame frame = Tkinter.Frame(self.widget.interior()) frame.pack(side='bottom', expand=1, fill='both') self.widget.status_bar = frame # add top self.widget.top = self.widget ed.top.protocol("WM_DELETE_WINDOW", self.cancelCmd) def settext(self, text): # settext replaces current text with new one, so no need for a clear() self.top.settext(text) def gettext(self, event=None): return self.top.getvalue() def clear(self): self.top.settext('') class CodeEditorIdle: """Idle code editor window, fitted to the needs for our various editors""" def __init__(self, master=None, title='untitled', code=None, font=None): from idlelib.EditorWindow import EditorWindow if code is None: code = "" lines = code.split('\n') height = len(lines)+5 if height > 24: height = 24 width = max(map(len, lines)) + 15 if width > 80: width = 80 elif width < 40: width = 40 ed = self.widget = EditorWindow(width=width, height=height) self.top = self.widget.top # delete menu entry Close and Exit for entry in [ 'Close','Exit', 'New Window', 'Save As...', 'Save Copy As...']: ed.menubar.children['file'].delete(entry) #ed.menubar.children['run'].delete(0,'end') ed.menubar.delete('Run') # rebind a bunch of methods ed.io.text.bind("<>",self.cancelCmd) ed.top.bind("<>",self.cancelCmd) # unbind a bunch of methods # FIXME: UNBIND SAVE SHORTCUTS: CTRL+SHIFT+S, ALT+SHIFT+S, F5, ALT+x ed.io.text.bind("<>", self.pass_cb) # overwrite top.protocol ed.top.protocol("WM_DELETE_WINDOW", self.cancelCmd) if font is not None: ed.io.text.configure(font=font) self.settext(code) # and set the title of this window ed.top.title(title) def pass_cb(self, event=None): pass def settext(self, text): self.clear() self.widget.io.text.insert("1.0", text) def gettext(self, event=None): return self.widget.io.text.get("1.0", 'end') def clear(self): self.widget.io.text.delete("1.0", "end") def cancelCmd(self, event=None): pass class CodeEditorIdle_python24(CodeEditorIdle): """ Idle code editor window, fitted to the needs for our various editors """ def __init__(self, master=None, title='untitled', code=None, font=None): from idlelib.EditorWindow import EditorWindow if code is None: code = "" lines = code.split('\n') height = len(lines) + 8 if height > 40: height = 40 width = max(map(len, lines)) if width > 80: width = 80 elif width < 40: width = 40 if master: ed = self.widget = EditorWindow(root=master) else: if hasattr(self, 'editor'): self.editor.top.master.inversedict = {} self.editor.top.master.vars = {} self.editor.top.master.close_all_callback = self.cancelCmd self.editor.top.master.root = self.editor.top.master.master ed = self.widget = EditorWindow(root=self.editor.top.master.master, flist=self.editor.top.master) else: self.port.editor.master.inversedict = {} self.port.editor.master.vars = {} self.port.editor.master.close_all_callback = self.cancelCmd ed = self.widget = EditorWindow(root=self.port.editor.master.master, flist=self.port.editor.master) width *= int(ed.top.tk.call("font", "measure", ed.text["font"], "0")) + 2 height *= int(ed.top.tk.call("font", "metrics", ed.text["font"], "-linespace")) ed.top.geometry("%dx%d+0+0" % (width, height)) self.top = self.widget.top # delete menu entry Close and Exit for entry in [ 'Close','Exit', 'New Window', 'Save As...', 'Save Copy As...']: ed.menubar.children['file'].delete(entry) ed.menubar.delete("Options") ed.menubar.delete('Run') # rebind a bunch of methods ed.io.text.bind("<>",self.cancelCmd) ed.top.bind("<>",self.cancelCmd) # unbind a bunch of methods # FIXME: UNBIND SAVE SHORTCUTS: CTRL+SHIFT+S, ALT+SHIFT+S, F5, ALT+x ed.io.text.bind("<>", self.pass_cb) # overwrite top.protocol ed.top.protocol("WM_DELETE_WINDOW", self.cancelCmd) # if font is not None: # ed.io.text.configure(font=font) self.settext(code) # and set the title of this window ed.top.title(title) ### Create CodeEditor class, depending on whether idlelib is available or not try: from idlelib.EditorWindow import EditorWindow # it gets a bit more complicated now. Python may be shipped with idlelib # which is an older version and does not fullfill our needs. Need to test # if this is such an old version. Here, the EditorWindow constructor has # no keyword width and height. We use inspect... from inspect import getargspec args = getargspec(EditorWindow.__init__)[0] assert "width" in args, \ "Older version of idlelib detected! Using Pmw code editor instead." assert "height" in args, \ "Older version of idlelib detected! Using Pmw code editor instead." class CodeEditor(CodeEditorIdle): def __init__(self, master=None, title='untitled', code=None,font=None): CodeEditorIdle.__init__(self, master, title, code, font) except: try: class CodeEditor(CodeEditorIdle_python24): def __init__(self, master=None, title='untitled', code=None,font=None): CodeEditorIdle_python24.__init__(self, master, title, code, font) except: import traceback traceback.print_exc() class CodeEditor(CodeEditorPmw): def __init__(self, master=None, title='untitled', code=None,font=None): CodeEditorPmw.__init__(self, master, title, code) class SourceCodeEditor(CodeEditor): """base class for sourc e code editor windows""" def __init__(self, master=None, title='untitled', node=None, editor=None): self.master = master self.node = node # Network node self.editor = editor # node editor self.top = None # Toplevel window holding editor code = self.node.sourceCode[:-1] if node is not None: title = "Code editor: Node %s"%node.name CodeEditor.__init__(self, master, title, code,font=node.editor.font['Root']) ed = self.widget # add OK and APPLY Button if code if modifiable if node and not node.readOnly: b=Tkinter.Button(master=ed.status_bar, text='Ok', command=self.okCmd) b.pack(side='left') b=Tkinter.Button(master=ed.status_bar, text='Apply', command=self.applyCmd) b.pack(side='left') # cancel is always accesible b=Tkinter.Button(master=ed.status_bar, text='Cancel', command=self.cancelCmd) b.pack(side='left') self.settext(code) ed.top.protocol("WM_DELETE_WINDOW", self.cancelCmd) def okCmd(self, event=None): self.applyCmd() # FIXME: save number of lines and number of chars!! Else if window is # resized and dismissed, size is lost self.cancelCmd() def applyCmd(self, event=None): code = self.gettext() self.node.configure(function=code) self.node.addSaveNodeMenuEntries() def cancelCmd(self, event=None): ed = self.editor ed.nbEditorWindows -= 1 self.node.objEditor.manageEditorButtons() self.node.objEditor.editButtonVarTk.set(0) self.clear() self.settext(self.node.sourceCode) self.hide() def show(self, event=None): # reset title self.top.title("Code editor: Node %s"%self.node.name) # reset source code self.settext(self.node.sourceCode) self.top.deiconify() def hide(self, event=None): self.top.withdraw() class CallbackEditor(CodeEditor): """Edit source code of port callbacks such as 'beforeConnect' or 'afterDisconnect'.""" def __init__(self, master=None, title='untitled', port=None, mode=None): if mode not in port.callbacks.keys(): return self.master = master self.port = port # node's port self.mode = mode # can be 'beforeConnect', 'afterConnect', etc code = port.callbacks[mode][1] if code is None: code = self.getDefaultCode() CodeEditor.__init__(self, master, title, code) ed = self.widget # add OK and APPLY Button b=Tkinter.Button(master=ed.status_bar, text='Ok', command=self.okCmd) b.pack(side='left') b=Tkinter.Button(master=ed.status_bar, text='Apply', command=self.applyCmd) b.pack(side='left') # cancel is always accesible b=Tkinter.Button(master=ed.status_bar, text='Cancel', command=self.cancelCmd) b.pack(side='left') ed.top.protocol( "WM_DELETE_WINDOW", self.cancelCmd) def getDefaultCode(self): mode = self.mode if mode in ['beforeConnect', 'afterDisconnect']: code = """def myFunc(self, p1, p2): pass """ elif mode in ['afterConnect', 'beforeDisconnect']: code = """def myFunc(self, c): pass """ else: code = "" return code def okCmd(self, event=None): self.applyCmd() # FIXME: save number of lines and number of chars!! Else if window is # resized and dismissed, size is lost self.cancelCmd() def applyCmd(self, event=None): code = self.gettext() apply(self.port.configure, (), {self.mode:code} ) def cancelCmd(self, event=None): self.clear() code = self.port.callbacks[self.mode][1] if code is not None: self.settext(code) self.hide() def show(self, event=None): code = self.port.callbacks[self.mode][1] if code is None: code = self.getDefaultCode() self.settext(code) self.top.deiconify() def hide(self, event=None): self.top.withdraw() NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/Glyph.py0000644000175000017500000000442310665620761022747 0ustar moellermoeller# # # #$Id$ from mglutil.util.callback import CallbackFunction class Glyph: def __init__(self,canvas,shape,kw={}): self.canvas = canvas self.shape =shape #if self.shape in ['label','circle','rectangle','polygon','line','arc','image','bitmap']: # if self.shape == 'label': # label = kw['label'] # font = kw['font'] # fill = kw['fill'] # position = kw['position'] # anchor = kw['anchor'] # self.create_label(postion=postion,text=label,font=font,fill=fill,anchor=anchor) # if self.shape == 'circle': # bbox = kw['bbox'] # fill = kw['fill'] # outline = kw['outline'] # anchor = kw['anchor'] # # if self.shape == 'rectangle': # bbox = kw['bbox'] # fill = kw['fill'] # outline = kw['outline'] # anchor = kw['anchor'] # self.create_rectangle(bbox=bbox,fill=fill,outline =outline,anchor=anchor) # if self.shape == 'polygon': # coords = kw['coords'] # fill = kw['fill'] # outline = kw['outline'] # anchor = kw['anchor'] # self.create_polygon(coords=coords,fill=fill,outline =outline,anchor=anchor) #ids = self.canvas.find_withtag("movable") #for id in ids: # cb = CallbackFunction(self.moveCanvasObject, id) # self.canvas.tag_bind(id, '', cb) def create_label( self,label, font, fill,position,anchor): lab = self.canvas.create_text(position=postion,text=label,font=font,fill=fill,anchor=anchor) def create_circle(self,bbox,fill,outline,anchor): cir = self.canvas.create_oval(bbox[0],bbox[1],bbox[2],bbox[3],fill=fill,outline =outline) return cir def create_rectangle(self,bbox,fill,outline,anchor): rec = self.canvas.create_rectangle(bbox[0],bbox[1],bbox[2],bbox[3],fill=fill,outline =outline) return rec def create_polygon(self,coords,fill,outline,anchor): poly = self.canvas.create_polygon(coords=coords,fill=fill,outline =outline,anchor=anchor) NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/ImageNodeEditor.py0000644000175000017500000001012412222642343024645 0ustar moellermoeller######################################################################## # # Date: Nov. 2001 Author: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner and TSRI # ######################################################################### # # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/ImageNodeEditor.py,v 1.2 2013/10/01 21:55:47 sanner Exp $ # # $Id: ImageNodeEditor.py,v 1.2 2013/10/01 21:55:47 sanner Exp $ # # OBSOLETE # import os, Tkinter, Pmw, ImageTk from NetworkEditor.Editor import ObjectEditor from mglutil.gui.BasicWidgets.Tk.colorWidgets import ColorChooser from mglutil.util.packageFilePath import findFilePath from mglutil.util.callback import CallbackFunction ICONPATH = findFilePath('Icons', 'WarpIV') from mglutil.gui.BasicWidgets.Tk.thumbwheel import ThumbWheel class ImageNodeEditor(ObjectEditor): def __init__(self, node, master=None): self.node = node ObjectEditor.__init__(self, node, 'ImageNode', master) def nameEntryChange_cb(self, event=None): """apply the new name to the node and remember it has been modified""" name = self.nameTk.get() self.node.name = name self.node.redrawNode() def Apply(self, event=None): self.nameEntryChange_cb() self.Dismiss() def createForm(self, master): """Create standard editor form and add a Pmw Group for input ports, one for output ports and a check button for viewing the source node's code. """ ObjectEditor.createForm(self, master) frame = Tkinter.Frame(self.top) Tkinter.Label(frame, text="fill color ").grid(row=0, column=0, sticky='ne') photo = ImageTk.PhotoImage( file=os.path.join(ICONPATH, 'colorChooser24.png')) cb = CallbackFunction(self.setColor, 'fillColor') b = Tkinter.Button(frame, command=cb, image=photo) b.photo = photo b.grid(row=0, column=1, sticky='ne') cb = CallbackFunction(self.setOpacity, 'fillColor') fillOpacityThumbwheel = ThumbWheel( frame, labCfg={'text':'Opac.', 'side':'left'}, showLabel=1, width=40, height=14, min=.001, max=1., type=float, value = self.node.nodeStyle.fillColor[3], callback = cb, continuous=True, oneTurn=1., wheelPad=0) fillOpacityThumbwheel.grid(row=0, column=2, sticky='ne') Tkinter.Label(frame, text="outline color ").grid(row=1, column=0, sticky='ne') photo = ImageTk.PhotoImage( file=os.path.join(ICONPATH, 'colorChooser24.png')) cb = CallbackFunction(self.setColor, 'outlineColor') b = Tkinter.Button(frame, command=cb, image=photo) b.photo = photo b.grid(row=1, column=1, sticky='ne') cb = CallbackFunction(self.setOpacity, 'outlineColor') outlineOpacityThumbwheel = ThumbWheel( frame, labCfg={'text':'Opac.', 'side':'left'}, showLabel=1, width=40, height=14, min=.001, max=1., type=float, value = self.node.nodeStyle.fillColor[3], callback = cb, continuous=True, oneTurn=1., wheelPad=0) outlineOpacityThumbwheel.grid(row=1, column=2, sticky='ne') frame.pack() def setColor(self, what): def cb(color): self.node.nodeStyle.configure(**{what:color}) self.node.redrawNode() self.currentNodeStyle = None cc = ColorChooser(immediate=1, commands=cb, title='Node %s color'%what) cc.pack(expand=1, fill='both') def setOpacity(self, what, value): if what=='fillColor': self.node.nodeStyle.fillColor[3] = value elif what=='outlineColor': self.node.nodeStyle.outlineColor[3] = value self.node.redrawNode() self.currentNodeStyle = None NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/LICENSE0000644000175000017500000000436411033235640022307 0ustar moellermoellerThis software is copyrighted by Michel F. Sanner (sanner@scripps.edu) and TSRI. The following terms apply to all files associated with the software unless explicitly disclaimed in individual files. MGLTOOLS SOFTWARE LICENSE AGREEMENT. 1. Grant Of Limited License; Software Use Restrictions. The programs received by you will be used only for NON COMMERCIAL purposes. This license is issued to you as an individual. For COMMERCIAL use done with the software please contact Michel F. Sanner for details about commercial usage license agreements. For any question regarding license agreements, please contact Michel Sanner: TSRI, Molecular Biology Department, TCP 26, 10550 North Torrey Pines Road, La Jolla, CA 92037 sanner@scripps.edu tel (858) 784-7742 fax (858) 784-2341 2. COMMERCIAL USAGE is defined as revenues generating activities. These include using this software for consulting activities and selling applications built on top of, or using this software. Scientific research in an academic environment and teaching are considered NON COMMERCIAL. 3. Copying Restrictions. You will not sell or otherwise distribute commercially these programs or derivatives to any other party, whether with or without consideration. 4. Ownership of Software. You will not obtain, and will not attempt to obtain copyright coverage thereon without the express purpose written consent of The Scripps Research Institute and Dr. Sanner. 5. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/RELNOTES0000644000175000017500000014501611102420464022455 0ustar moellermoellerNetwork Editor Release Notes ============================= ----------------------------- Release 1.5.4 (November 2008) ----------------------------- itemBase.py - fixed some problems in UserPanel related to the No GUI execution of networks - added NoGuiNetworkBuilder, NoGuiVPE and NoGuiExec classes to have an editor object even when we run withjout the gui. This enables the execution of network with macros as standalone programs - added class Communicator to support socket-based communication with network running as independent process - named the network process-`pid` during remote execution - added debug=False to to node.run() method - added select() to read from stdin and execute commands gotten from there when the network runs in a detached process - introduced button "run saved network without gui" - running network can be stopped with user panel items.py - now for macro network we are looking for the main network name - introduce typeManager for network when running without gui - run network withoout gui with separate stop button - saves raw values (r"") for types.StringType - introduced splitratio for connections - repaired blocking feature to enable data loop - corrected small bug when saving nodes inheriting original node - got rid of the reference to global 'Vision.ed' in OpalUtil net.py -now run pause and stop buttons correspond to the current network -corrected saving categories for webservices - added the clientSocket variable in the main scope of the server when command is executed. This variable point to the socket from which the command came from and can be used to specify where results should be sent back. -added test for empty connection list when calling select (necessary on windows) -added class Communicator to support socket-based communication with network running as independent process - named the network process-`pid` during remote execution - added GANTT diagram for time profile and debug capability - added node.nameInlastSavedNetwork to save name of node in network executed in a remote process - introduce typeManager for network when running without gui - added help to run network without gui - default network are in imediate mode - repaired blocking feature to enable data loop - now Network.freeze simply set runOnNewData to False - added arg -w on command line of stand alone network to run with or without vision ports.py -repaired special ports icon position simpleNE.py -added resourceFolder argument which defaults to 'mgltools' -solved rare problem when node name already exists -changed "continuous run" into "immediate run" -quit open in the correct window when several instance of vision are running widgets.py -repaired griding of widget when it moves between node and param panel -now for macro network we are looking for the main network name -added bindToRemoteProcessNode(), def _procTrigger() and def unbindFromRemoteProcess() to support binding GUI to remote process execution ----------------------------- Releases 1.4.6 - 1.5.2 (July 2008) ----------------------------- --Moved from Numeric to numpy Glyph.py - new file LICENSE - -added TSRI to the copyright part. customizedWidgets.py - mousewheel in scrollabled canvas - now widgetsOnBackWindowsCanGrabFocus is in mglutil.gui datatypes.py - added type 'object' as synonym for 'None' - repaired casting to list itemBase.py - better updateGuiCycle and solved re-run of stopped node - when the network stops, the last node hasn't run properly, so it is marked with forceExecution 1 - corrected error on postedmenu when delete connection - introduced "soft run" in vision - now parameter panel otion is immediate by default - first commit of NOGUI execution of networks - set runOnNewData to True while implementing, got rid of autorun - now parameter panel otion is not immediate by default - now networks runs in saved directory items.py - network don't freeze anymore, just the nodes in it - repaired networkDefaultDirectory - now network reloads the opal web categories - set runOnNewData to True while implementing, got rid of autorun - renamed runOnConnect into runOnNewData and generalized it to new data and set it to false - stop network from running when we rebind widget at loading time - in function node pos args can have widgets too - correct rebind widget - now pmv command can be cut and pasted - repaired saving of RunFunction node - added filebrowser for input files in function node - added combobox for selection in function node - now parameter panel option is not immediate by default - now widget's labels can have ballons - repaired saving of pmvviewernode - now FunctionNode can be saved with importString - readded construct parameter - FunctionNode takes function or string - introduced node Split - now FuntionNode has dynamic output ports - started dynamic output ports - selfGeneratingNode is now FunctionNode and eval is applied on the commandString - now selfgeneratingnode is not self-subclassed but use args on addNode() - corrected menu label - corrected bug in adding input port to doit function - made sure adt and pmv run without vision - introduced networkDefaultDirectory - automated reload of modified libraries - correct reload of overwritten nodes in libraries - user library can be reloaded macros.py - solves issue with saving macro nodes introduced buy noGuiRun - now runOnNewData is using reference_value - set runOnNewData to True while implementing, got rid of autorun net.py - better updateGuiCycle and solved re-run of stopped node - when the network stops, the last node hasn't run properly, - so it is marked with forceExecution 1 - introduced "soft run" in vision - now runOnNewData is using reference_value - now the nodes are not ran anymore when we load the network - corrected save for FunctionNode - adjusted menu shortcuts - removed adding of local path to sys.path - corrected filename for self running network ports.py - objects can retain their current position when reparenting in vision - stop network from running when we rebind widget at loading time - corrected save for FunctionNode - corrected getdescr, originalDatatype is returned only for inputport simpleNE.py - better filter for open and save dialogs - repaired networkDefaultDirectory - runOnNewData has an icon - now we can loadNetwork without taking the focus - now fonts are automatically saved in vision - repaired vision shell - now vision can re-run - argument "ins" can be pass to runVision() - added keyboard shortcuts - better font size on mac osx widgets.py - repaired networkDefaultDirectory - now runOnNewData is using reference_value - renamed runOnConnect into runOnNewData and generalized it to new data and set it to false - added combobox for selection in function node - corrected default value for labelBalloon - corrected save for FunctionNode - now widget's labels can have ballons - removed useless NERadioButton - made sure adt and pmv run without vision - corrected widget filename path on win32 - repaired configure of initialValue ----------------------------- Release 1.4.5 (May 2007) ----------------------------- New features, bug fixes: customizedWidgets.py - introduced widgetsOnBackWindowsCanGrabFocus now, on windows, the widget grab the focus only if the window is already activated. items.py - corrected bug when copying node with deleted port - corrected widget delete when port has been deleted and recreated macros.py - changed contextual menu 'Delete' network into 'Close' to solve bug net.py - now we run the nodes before the connection (in the saved network) ports.py - now, default port data is 'no data yet' simpleNE.py - added "Open Recent" menu widgets.py - introduced node ScrolledList - fixed to set _modified =True in NEEntry Button widget. Now entry widgets in saved networks remember values ----------------------------- Release 1.4.4 (December 2006) ----------------------------- New features, bug fixes: - moved user panels to the network and added delete user panel handling - added datatype 'coord2' - introduced self launchable network depending on the existence of a user panel - moved user panels to the network and added delete user panel handling - removed menubar in user panel, made buttons instead - introduced placer in user panels - repaired bug when deleting connections, the callback can destroy some connections in the list - we don't restore the pmvViewer states on first run (only for regular viewer) - moved n.beforeRemovingFromNetwork() after deleteConnections() - introduced node StickImageNE - we don't run automaticaly the saved network anymore - corrected the writing of launchVisionToRunNetworkAsApplication() - moved user panels to the network and added delete user panel handling - introduced self launchable network depending on the existence of a user panel - introduced placer in user panels - revised saving network as application - a safer saveNetwork() ------------------------------ Release 1.4.3 (September 2006) ------------------------------ New features, changes: - Network connections are highlighted in pink when mouse is over it. - node 'show/hide GUI' is deprecated; - reparenting menu is only for geometry nodes; - modified 'Next parenting' to affect any parenting; - introduced new SelectMultipleGeometry; - improved casting and validation; - suppressed unused ObjectType; - now loading of network in pmv shows both regular and pmv networks at the same time; - introduced pmv vision network as _pmvnet.py; - now when you delete a macro, the currentNetwork doesn't change anymore; Bug fixes: - corrected glflabels when several labels are given; - corrected name proposal when saving new node; - corrected saving of nodes for userlib; ----------------------------- Release 1.4.2 (May 2006) ------------------------------ Changes and bug fixes: Editor.py -corrected small bug to allow node saving when we delete a port -now macroNode only permits single connection ports. -corrected bug when deleting output ports -corrected bug when renaming ports datatypes.py -corrected validate in ArrayType -introduced StringType and ObjectType based on anyarraytype -now node save source code generates necessary lines to load synonyms -introduced Types files per library -corrected getTypeFromClass to return the instance of AnyArrayType for None -add indice2 type itemBase.py -corrected bug related to "show legend" and apply. -corrected bug when closing colormap settings -corrected bug when closing colormap settings items.py -cascade menu for FastLibs in vision toobar, smaller icon for diamond ports, smaller and toggable port's icons in the library GUI -now the nodes are drawn with their ports in the GUI as in the networks -better comments and menu lines on "save source code" -mRequiredTypes and mRequiredSynonyms are class variables in the class node -we prepare the save node to load types and widgets when instanciated (we still need to find a solution to load synonyms). Commented code that displays port in library vision gui -corrected bug when saving colormap node source code, -reduced default colormap node to 16 values -corrected bug on saving doit function when saving macros, -corrected bug on automatic loading of nodes in mydefaultlib -object's states are now saved at the end of the networks, - fixed NetworkNode.outputData to allow outputing None -added try: arround connections in saved networks -dependent code for nodes doesn't save doit if not necessary, when the port name changes, it changes in the code as well -corrected bug when saving colormap node source code net.py -corrected bug on saving doit function when saving macros, -corrected bug on automatic loading of nodes in mydefaultlib -now parenting geometry nodes can be done througth macros -added try except around masternet run() in saved network ports.py -corrected getDataStr to accept single values as Numeric array -changed gray to make not required ports more differentiable -adjusted port's display, now not required ports have a gray outline -input ports outline in vision library gui are black, gray or red. -modifications to prepare drawing the port icons in the vision gui simpleNE.py -added self.pyshell.begin() and self.pyshell.close() Release 1.4.1 (March 2006) ------------------------------- What's New since 1.3alpha2: -------------------------- -In datatypes.py: * moved colorRGB and colorsRGB types to NetworkEditor.datatypes; * moved the type InstanceMatricesType from NetworkEditor to DejaVuNodes; * added support of moving widget to macroParamPanel -In itemBase.py: * replaced 2 calls to c.tk.call by a single one in NetworkItems.run * modified port.getData to always print message if editor.verbose is true and data is bad or missing * modified NetworkNodeBase.computeFunction to turn node outline orange if data is missing * made NetworkItem.run check for 'Stop' status to avoid overwriting orange outline with execution failed outline color -In items.py: * added a check for the existance of initialValue in widgetDescr of unbound widgets when generating source code. * added node.ischild(node) method to find out if a given node is a child of another one. This is used in net.runNodes to remove children nodes of nodes who return 'STOP' * added safeName() method to NetworkNodeBase to remove all bad symbols from node name so we can use node name in saving network. * added NetworkNodeBase.getUniqueNodeName() to return a unique name usable as a variable in a saved network. Changed all definitions of nodeName to call this method instead * modified getNodeSourceCodeForWidgetValue to Set widget with run=1. That way Root nodes will present the data on their output port when restored. * modified net.getNetworkCreationSourceCode to freeze network before connecting nodes and unfreeze after the conections are created. This avoids running the network when it is restored. * replaced inputPorts[] by getInputPortByName() and outputPorts[] by getOutputPortByName() -In net.py: * modified scheduling to propagate node return value. Returning 'stop' prevents children nodes from running again * added new accelerator to freeze/unfreeze the current network. * added self.runOnConnect attribute to network to be able to prevent execution of the hold node upon connection. This was causing macros such as rotateScene to execute when restored from a file * made node2 runn upon deletion of a connection only if port2 is not required. * modified net.deleteConnections to set _newdata to True in input port of child node and schedule the child node's execution * check if the mouse moved only a few pixels upon releasing left mouse button. If we are below a threshold we assume we did not move. This is usefull for deselecting selected nodes for people who don't have a steady hand (who move the mouse when releasing the mouse button, or when the mouse pad is very soft and the mouse moves because it is pressed in the pad...) * added self.runOnConnect attribute to network to be able to prevent execution fo the chold node upon connection. This was causing macros such as rotateScene to execute when restored from a file * modified special connection to use stipple gray75 and with 4 by default * added comment in net.runNodes about status==0 .. I am not sure when this happens if at all .. and if we shoudl do the same as for stop or not * modified runNodes method to no longer break out of the loop running nodes when a node returns 'Stop', but rather remove all the children of this node from the list of nodes to be run. * modified getNodeSourceCodeForWidgetValue to Set widget with run=1. That way Root nodes will present the data on their output port when restored. * modified net.getNetworkCreationSourceCode to freeze network before connecting nodes and unfreeze after the conections are created. This avoids running the network when it is restored. -In ports.py: * changed PortsDescr.append to accept eiter a single dict or a list of kwy=values -In widgets.py: * added newValueCallback method to set _newdata when widget is modified so that portHasNewData works properly. This function is now the command bound to the widgets instead of scheduleNode which is called by newValueCallback. * added lockedOnPort key word for widgets allowing a programmer to disable widget unbinding. This facilitates writting the doit() method of the node as it can be expected that the widget is always there -In Editor.py: * added CodeEditorIdle_python24 class for editing files with python2.5 idlelib.EditorWindow -In macros.py: * made InputPortNode the last root in the list to be run by InputPortNode and removed OutputPortNode from this list as it is not a root ! * modified MacroNetwork.getNodeDefinitionSourceCode to also freeze before creating connections and unfreexing after * modified getHeaderBlock, getInitBlock, getBeforeBlock, getAfterBlock in MacroNode to ignore self.library because when we get source code we want to subclass MacroNode and not the original Macro * BugFixes: --------- -In Editor.py: * older versions of idlelib which may be distributed with Python cause problems with the source code editor. Modified the test in Editor.py so that it will fail when an older idlelib is detected and use the Pmw code editor instead. -In itemBase.py: * fixed frozen color when selecting/unselecting and when coloring by Node Library. Even if the node is colored by library, it is colored blue when frozen. -In items.py: * refresh network did not color the node icons correctly. Frozen color or node library color was lost. This was fixed in buildNodeIcon(). * fixed Node's updateCode() method which as assuming there is no space between doit() and the following self. * fixed safeName to avoid names starting with a number -In macros.py: * saving a macro with no connections would raise an exception. The problem was that we defined "macroNetworkName" inside the test if we have len(connections). Moved the definition outside of the test * autoRun=False was not saved for macro nodes. Fixed this. * added macNet._originalNodes = [] and macNet._originalConnections = [] in MacroNode.buildOriginalList to make sure that we build the list starting from an empty list. This caused a bug in the CoarseMolSurf macro which was written to subclass the UT_IsoDecim macro. Its beforeAddingToNetwork method called the on of UT_IsoDecim which put its nodes into the list. then the call to buildOriginalList at the end of beforeAddingToNetwork of the macro appended to the lsit of nodes from UT_IsoDecim -In widgets.py: * fixed ScrolledText widget to save its value, and 'right-click' to pop up menu. * fixed a bug in TkPortWidget: in the configure method we need to test if tkOptions is not None, since this might be None and then we cannot iterate over it Release September 1, 2004 ------------------------- What's New (compared to release Sep 18, 2003) - added a widget editor to modify the appearance of a widget - added a widget grid editor to modify the grid position of a widget and to modify the label of a widget - invisible ports are no longer taken into account when computing the position of ports. Hiding a port (by binding a widget), or deleting a port will cause all ports to the right to be shifted to the left. - displaying a previously hidden port (by unbinding a widget) triggers moving all ports right of it to the right. - added new method updateIconPosition() to ports.py which moves the port icon and its connections - added new port attribute port.visible which is needed for the new setPortPosX() - made widget background colored - addInputPort() and addOutputPort() methods in items.py now generate unique port names and correctly update node.inputPortsDescr/outputPortsDescr - update ports.py buildIcons() method to be more clever when resize the node icon, added support for SpecialPorts, and most importantly using the correct tags for Tkinter - added new method destroyIcon() to ports.py that destroys port menu and port icons - moved startConnectRubberBand, drawRubberBand, createConnection from class OutputPort to Port so that SpecialPorts (who now inherit from Port and not from InputPort or OutputPort still work) - cleaned up SpecialPorts: - they no longer implement their own buildIcon() and createIcon() method - but they implement a computePortPosX() method now - added a new TreeView widget to introspect a node or data and replaced the idlelib introspect GUI with this new widget, which allows us more flexibility - added autoRun attribute to itemBase; moved frozen attribute from NetworkNode to NetworkItems; added frozenTk and autoRunTk variable to NetworkItems; added toggleAutoRun_cb method to NetworkItems; moved toggleFrozen_cb, freeze and unfreeze methods from NetworkNode to NetworkItems; added check to run method to not run nodes with autoRun==False or frozen==True; added autoRun constructor option to NetworkItems - added new method _setModified() to access the attribute _modified - changed all direct access to _modified to go through this new method - completely rewrote the saving of networks: all methods called by getNodeDefinitionSourceCode() were rewritten. Note: older networks can still be loaded! - no code modification, but an organisatory change: moved code blocks that belong together (code for saving a network, code for generating source code) - added a new attribute _id to NetworkNode which is a unique number and used when saving networks - added configure() and getDescr() to NetworkNode which is now used by save/restore, copy/paste; changed getNodeSourceCodeForModifications() to use the new framework with configure() a node - added new attribute hasMoved which is set to True if the node changes its position in a network - show/hide special ports is now handled by the node's configure() method - showSpecialPorts() and hideSpecialPorts() set node._modified=True - changed attribute value self.specialPortsVisible from 0/1 to True/False - added new method resetTags() to node which resets both _modified and _original - changed all getNodeSourceCodeFor() methods to use _original - reworked the getNodeSourceCodeForPorts() method to make it more human-readable - adding or deleting a port does no longer set the node's _modified to True - deleting or unbinding a widget now sets port._modified=True We use this information now when we create code to save a network in getNodeSourceCodeForWidgets() - saving networks no longer sets widget values by default, but only if the widget value is different from the class defined initialValue - setting widget value now sets widget._modified=True This allows to save/restore macro nodes from node libraries with much less code to be saved (no more setting all widget values again) - updated connection.getSourceCode: we now test if the connection goes to a MacroInput/OutputNode, and whether we have to connect to 'new' or to an existing port (to avoid creating new ports) - macros now use again the getNodeDefinitionSourceCode() method to generate a string to save data - when saving, if we hit a macro node, we first recursively loop over all macro networks and add all the macro nodes, then we start filling the leaf macro networks with nodes, and connect them, then move down the tree towards the root macro(s) - added some "smartness" to saving macros: if the macro comes from a node library and we just change a widget value, we might not have a handle to the node. Thus, if we reach such a case we now check if the node has already be declared, if not, we add a line. - deleted all methods in MacroNetwork since we no longer need them (the editLog is gone) - changed signature of method getNodeSourceCode(): we no longer pass a keyword nodeNumber and forLib - added a new base class NetworkItemBase from which all network items inherit - changed attribute .editor to be a weakref and added mechanism to remain backwards compatible (even though this is a weakref, we can still access it as it would be a normal attribute, we do not need to call it -> not object.editor() - changed access to .editor to use the new method getEditor() - moved the method updateCode() from ports.py to items.py, subclassed this in macros, changed code wherever this was accessed - the paramPanel immediate flag can now be set through the node's configure() method with the new keyword paramPanelImmediate. Also, the node's getDescr() method returns this new keyword. This is used to save the status fo the immediate flag of the param Panel. - added new class UserPanel - added support to UserPanel for moving widgets and labels using arrow keys (after middle clicking to select widget to move) - added try/except statments to saved networks allowing to restore partial networks - made connectNodes accept either port numbers or port names - added 2 metods to node class getInputPortByName and getOutputPortByName - double-clicking on nodes with displayed paramPanel now hides the panel (double-clicking again displays panel, etc.) - added new method getSize() that returns a tuple with (width, height) of this node, in pixels - added blocking attribute to connections to allow for cycles - added a new class CodeEditor that inherits from either idlelib if avialable or from Pmw. SourceCodeEditor and CallbackEditor use this new base class - deleted classes SourceCodeEditorIdle and SourceCodeEditorPmw in Editor.py, fixed methods there to work with the new additions and changes - removed show/hide Param. Panel from node menu, replaced this with a new checkbutton entry named "Parameter Panel", added new variable paramPanelTk, double-clicking on node to show Param. Panel also sets this variable correctly - added a checkbutton under Edit menu to togle splined connections on and off - added toggleSplineConnections call back to betwork for handling checkbutton - added setSplineConnections methos to network to set smooth option on and off - added support to move selected nodes with arrow keys. Pressing arrow ke moves selected subgraph 1 pixel. Pressing SHIFT+Arrow key moves 10 pixels - made dynamic inputports of macrooutput nodes not required - added new accelerators for some operations (such as load/save network), create macro, etc. One can now hit CTRL+l to load a network, etc - moved the code to force node execution upon instanciation from Vision.VPE.endDD to NetworkEditor.net.AddNode so that nodes restored from a file behave the same way as when they are created from a library interactively - clarified definition of 'waiting' for execStatus - set execstatus to running and reset to waiting in run method - removed attempt the acquire and release runlock in toggle pause method as this method calls pause and resume which each do that - added runAgain attribute to handle rerunning the network in case a run was requested while te network is already running - added new method deleteConnection() which accepts "node1, port1, node2, port" (versus deleteConnections() which accepts a list of connections) - added new method nodeIdToNumber(): network nodes now have a unique ID. This method allows to get for a given node the current index in network.nodes - modified connectNodes to test singleConnection is True to support 'auto' as a valid value for this attribute - adding a node to a network tags the network modified - added source code to saved networks to restore user panels Bug Fixes - Fixed ParamPanel applyFun (was node.schedule) which would not trigger node to run, new applyFun is node.schedule_cb, which first sets node.forceExecution=True, then calls node.schedule() - deletePort() method now accepts and properly propagates keyword resize used to resize the node icon - rewrote large parts of itemBase.deletePort() method: - special ports are now correctly treated - proper deletion of port icons - proper handling of node.inputPortsDescr and node.outputPortsDescr - proper computing portposx for remaining ports - proper renumbering etc for remaining ports - fixed a bug in items.py when network is refreshed and special ports are shown the node menu correctly displays now 'hide special ports' - fixed items.py buildIcons() method: specialPorts buildIcons() method is now called here instead in addSpecialPorts() method (before, we would build the icons twice) - deleted the Port attribute "tags", which was the source of many strange problems, for example, after refreshing a network and then moving a node would also move port icons of other nodes, SpecialPorts would not move at all, and others... this is now fixed - fixed a bug when exiting with a expanded macro network: Pmw wants to set the current network after a network was deleted which would fail here since the macro's main network was already deleted from editor.networks. - fixed bug that caused node to run when asked to by the used if autoRun was False - commented out checking for new data in run since this will be done anyways in computeFunction called by run - fixed a bug when exiting with a expanded macro network: Pmw wants to set the current network after a network was deleted which would fail here since the macro's main network was already deleted from editor.networks. - compeletly rewrote the node.move() command, which is now using the network.moveSubGraph method, that also updates connections. - fixed moveSubGraph() method to update connections when nodes move, added new keyword absolute=False, that allows to move the subgraph BY dx,dy or TO dx,dy, when absolute=True - added new method updatePosXPosY() that sets the node's posx and posy this method is used in quite a few instances in net.py - bugfix: node.posx and node.posy were not set correctly. Now, when a node is moved, we call node.updatePosXPosY() to set these two attributes correctly. We use the upper left coordinate of node.outerBox as posx, posy - fixed finally the problem we had that nodes move after adding to a network when they have widgets bound. There was some very old code in buildNodeIcon that moved a node after instanciation, but with wrong values. Fixed this, also set initial posx, posy to 0, moved the 'move' statement from buildIcons to buildNodeIcon where it belongs - fixed a bug in determining the needed nodesize: if the last port was invisible, this would not work properly - set port attribute width, height to default None, updated this in all files - changed behaviour of ports so that they now use the new width, height defined in datatypes to build port icon; also updated datatypes; we no longer need port shapes 'rect1', 'rect2', etc, but just 'rect' since now the user can specify width, height and define the shape. Note: currently we continue to support keywords such as 'rect1', but we ask users to update their datatypes to use the new scheme - refreshing a network would add duplicate menu entries to connections pulldown menu. Fixed this by implementing a destroyIcon() method in NetworkConnection, and in net.py deleteNodes() now calls this method properly (instead of just looping over self.connections and set c.id=None) as was done previously - updated special ports to use proper icon size (8x8, not 4x4) - saving and restoring a node with displayed special ports would fail because we did not update the pulldown entry. Fixed this in items.py: we now call directly the two methods show/hide special ports, rather than calling a callback function. The two methods do implement the proper setting of the pulldownmenu; restoring a network with nodes with special ports connected would fail because in net.py the connectSpecialNodes() method was not updated to find ports by names (instead of numbers) - fixed yet another bug with special ports: if there was a connection, refresh network would not properly restore the connection - fixed saving nodes with unbound widgets (we now save "nodeX.unbindWidget(.." instead of "nodeX.deleteWidget(..." - fixed identation bug in saving networks which could cause endless loop - fixed a bug in getNodeSourceCodeForPorts where ports where not properly deleted if more than 1 port in a row were deleted - bugfix in saving widget values: if the value is not of type string, we should not put the value in quotes. - made checking for 'stop' pause' and 'frozen' case insentitive (scheduling nodes) - fixed a bug in node.computeFunction that caused string arguments passed to dynamic function to be lower-cased - changing the data on a port now also updates the Introspect GUI - fixed a bug in setting the source code: if we use " instead of ' the code is no longer loadable. Using string.replace in setFunction() - fixed a bug in saving source code: since port.getDescr() adds a key _previousWidgetDescr if the widget of this port is unbound, this gets passed to addInputPort(). Updated this method to accept _previousWidgetDescr and add this as an attribute to the port. This allows the port to rebind the widget. - saving a node with a deleted widget now saves a lines to delete the widget, not unbind it - saving a node with an unbound widget now first saves a line to set the widget value to the current value, THEN adds a line to unbind the widget. Therefore, after rebinding the widget, one gets the previous value, not the default value - fixed cut/copy/paste problems with nodes inside macro networks that came from a node library: these nodes are set _original=True and therefore did not generate source code upon cut/copy Fixed this problem by adding a new keyword ignoreOriginal=False to the signature of getNodeDefinitionSourceCode() which overrides the tests in there - saving source code of nodes that came from a node libary (like most nodes) we now inherit from the original node. This fixes a bug where we would loose methods that were defined in the original node. - Moved updating the source code editor GUI into the setFunction() method where it actually should be done. Now, configuring the node with new code automatically updates the GUI. - fixed a bug in copy/paste nodes inside a macro network that came from a node library: we copied the nodes but not the connections because we did not pass ignoreOriginal=True to the connection.getSourceCode() Needed to update methods in macros.py, items.py and net.py - bugfix: if only the compute function inside a macro node from a node libary was modified, we forgot to call the checkIfNodeIsDefined() method to add a line to get a handle to this node. This is now fixed. - the temporary editor attribute _tmpListOfSavedNodes is now an attribute of NetworkBuilder. This change was needed to address the issue above. Needed to update items.py, macros.py, net.py and simpleNE.py to use this new attribute properly - made list of children nodes unique in scheduleChildren() - modified node's rename method to avoid quotes in name, which could cause problems for saving/restoring and copy/paste - bugfix: deleting a node with an open widget editor now deletes this editor - fixed method moveSubGraph which did not move connections properly - fixed a bug in refreshing a network with nodes with widgets bound to node, and the node was expanded. Prior, this would cause the node icon to be redrawn compeletly wrong. Fixed this by hiding the widgets in node before destroying the node. - when we delete nodes, we do not want to update the node code signature. Fixed this in deleteNodes() - fixed a bug in deleteNodes() method: We were looping over the ports and inside the loop we would delete ports from the list we were looping over. This caused all kinds of unpredictable results: for example, deleting a node with open editor windows (such as port editor) would not always delete these windows. - net.py: connectNodes now calls port's afterConnect callback BEFORE the node is scheduled if data is present on the port - before and after connection callbacks can now return a new connection. This allows these callbacks to replace the connection that has been made - check for valid callback name when editing port callback - fixed a bug in deleteConnection(): we could reach a condition where 'conn' was undefined. Set conn=None by default. - prevented macro nodes and MacroInput nodes from executing when they are added to a network (they would execute because they have no inpout ports before connections are created). - made input ports of MacroOutputNode singleConnection='auto' to prevent creating a list of objects when there is a single parent - made stop, run set the execStatus in all macro networks in addition to the current network. This fixes the bug in which stoping a network with an iterate in a Macro node would not stop the iteration Backwards incompatibilities 1) Implemented much better handling of connect and disconnect events. This is incompatible with nodes that implement a beforeConnect(), etc method: Deleted the methods beforeConnect(), afterConnect(), beforeDisconnect() and afterDisconnect(). Added four new keywords to Port constructor: beforeConnect, afterConnect, beforeDisconnect, afterDisconnect. The value for these keywords is a textstring describing the source code for the method to be carried out (similar to the node source code). Updated configure() method of ports to handle these new keywords. Also, fixed configure() methods of ports: the class OutputPort did not use the configure method of the base class. Updated connectNodes() and disconnectNodes() methods in net.py to work with these new changes. Exposed these new features in the port editor: one can now use idlelib or a Pmw text editor to edit the function. Nodes which implement a beforeConnect() etc. method need to be rewritten. For an example, please have a look at Vision/StandardNodes.py class ListOf Release Dezember 17, 2003 ------------------------- What's New (compared to release Sep 18, 2003) - added try except statments around node instanciation code in saved networks This allows restoring networks with problems. - made connectNodes accept either port numbers or port names. The network source code generation now uses the port's naem by default - added 2 metods to node class getInputPortfromPortFromName and getOutputPortfromPortFromName - added a new getDescr() method for port description - added a getSourceCode() method in class NetworkConnection - commented out main menu entries to add/delete categories, to create libraries, to add nodes to libraries. This will be re-activated in a later release (once these things work properly) - added optional argument updateSignature to deletePort method. Was needed for runPmvCommand node - added capability to delete a node proxy icon in a category frame: added deleteNodeFromCategoryFrame() method to class NodeLibrary This method deletes the icon and calls a new rebuildNodesInCategoryFrame() method that first deletes all icons and rebuilds the icons - added new method deleteSmallIcon() to items.py which deletes the node proxy icon in a node library - added capability to change font of various GUI components: - changing the font for node proxies (node icons in node library windows) - changing network node fonts now works (using refresh to rebuild icons) Note: I do not change balloon font for ports. Don't know if I should or not - exposed this functionality in a Form accesible through the Edit menu - added a ChangeFont form to Forms.py - editor.font is now a dictionary that contains the fonts descriptions of the various gui components, also removed attribute editor.libraryFont because this is now stored in editor.font - added kw 'net=None' to refreshNet_cb() method in items.py - added new method getLeafNode() to find leaf node in a nested macro - added a couple more regression tests for load/save macros, delete networks with macros - made network canvas grey75 so white arrows for None type can be seen - added methods isModified() and resetModifiedTag() to items.py - users can no longer save a network if it is an instance of a MacroNetwork to prevent problems (i.e. saving the macro network but not the main net) - changed 'Color Node By Library' so that it will also color/uncolor all nodes already present in all networks - changing the datatype of a port now sets the flag self._modifed=True - changed balloon help in ViPEr so that every GUI element has its own attribute 'balloons'. This fixes some problems where we have to re-create Pmw.Balloon in order to move the yoffset, also this allows us to change the font individually - toolbarbutton.py needed to be updated after this change - turned multi threading off by default - enabled changing widget master in Editor - fixed ParamsForm (widget editor) to only return modified values - fixed ParamsForm to handle boolean properly - allow None as a valid entry for int and float type (need for dial and thumbwheel min and max) - added 'Cancel', 'Apply' buttons to widget editor - made widget editor destroy widget when new widget is built - added dict of currentValues to widget editor so we only add options that are no default values - added a file browser button to the right of the Entry in the WithFileBrowser widgets. This was done because on some Linux machines double-click event are somehow lost on the Entry ! - added menu entry 'Unbind Widget' to port menu. Unbinding a widget will add menu entry 'Rebind Widget' to port icon. This makes un- & rebinding widgets so much easier! - added type boolean to TypeManager - modified validation function of data type to return True instead of 1 - made widget background colored - made widget's label background colored - added beforeConnect(port1, port2) and afterConnect(conn) methods to node - changed frozen color to light cyan which helps reading text in nodes - modified loadNetwork to replace underscores by minus - added borderwidth=3, relief='sunken' , bg='#c3d0a6' to node.iconMaster Bug Fixes - fixed missing applyCmd method in PMW-based code editor - many fixes in saving macro networks. Fixed indentation of nested macros - many fixes for saving widgets - fixed some issues with _previousWidgetDescr - deleting a port now updates the node source code properly - fixed problem in saving code for nodes that come from different files such as done in the regression tests - fixed a bug in setting fonts on Windows machines - fixed deletion of nested macros - fixed a bug in getLibraryImportCode that would fail if a node has no library - fixed a bug in the exit_cb that would fail if a macro network is present (i.e. red ink when exiting ViPEr) - fixed a bug in macros where a macro could not be saved/restored if the macro name had a space in the name - fixed import net from NetworkEditor rather than simpleNE in macros.py - deleting a network with a macro would not delete the macro-subnetwork - fixed creating user libraries and adding nodes to these libraries (Note: this functionality is currently not exposed to users, wait for next release) - fixed a bug in deleting networks where sometimes a macro would not delete its macro network. The problem was that in the Network.delete() method we looped over self.nodes, but subsequently would delete an item from self.nodes, which would result in unpredictable results. Simple fix was to loop over a copy of this list - fixed a bug in copy/paste nodes which occured after changes in the getLibraryImportCode() method (the signature of this method changed) - fixed some bugs in scaling nodes: - we are now using the current node font rather than a system font when scaling (which looked really ugly on some operating systems) - show/hide widgets in node works again (sort of, this needs more work, at least now it does show and hide the widget rather than raise an exception) - resolved many conflicts with tags mixed up: we assigned node.iconMaster to ports (port.iconMaster = node.iconMaster). This caused almost unreproducible problems after 'refresh' was hit: port icons of nodes would move when other nodes were moved, etc - 'refresh' is supposed to reset scaling. However, we would use a wrong relposx so port icons would end up in wrong places after refresh. This was solved by calling setPortPosX() in createIcon() in ports.py - 'refresh' would not reset the scaled port icons. This was because the node's attribute scaleSum was not reset to 1.0 - creating a macro while the option 'Color Node by Library' was turned on would fail. This is now fixed - fixed unbind / rebind widget in class InputPort. Fixed bugs and made methods more clever (show/hide node widgets if specified previously, etc) - fixed default values for NEDial and NEThumbwheel in widgets.py - fixed many things in ParamsForm (the widget editor) - fixed bug which caused specified configuration options to be lost when widget was rebuilt. For instance (size=60, type='int') would loose type - fixed a bug in copy/paste nodes inside a macro network: in the method pasteNetwork_cb() where we execute the code object, we have to pass 'net' for self, rather than 'self' - fixed a bug in cutting selected nodes, where the Cut and Copy buttons would remain active and thus give the user the ability to overwrite the current paste_buffer with an empty list. Cutting now disables the Cut and Copy buttons - fixed a bug in pasting nodes when all networks were deleted. The Paste button still remains active if there is data in the paste buffer, but no exception will be raised if the button is pressed and no network is present. - fixed a bug in cut© nodes to prevent these operations on Macro Input- and Output Nodes of Macro Networks. One can now safely "Select All" nodes in a macro network and cut/copy them without copying these two nodes (which will get automatically deselected now upon cut/copy) Cut/Copy/Paste of Macro nodes of course still works as before - fixed bug that caused new nodes added to a frozen network not to be frozen - fixed a bug in widthFirstChildren - fixed bug in connectNodes. Port descr was assumed to have datatype key. Backwards compatible Changes (renaming of methods, attributes) - renamed method setPortPos() to setPortPosX() - renamed attribute modified to _modified, changed value to Boolean - changed in macro networks how macroinput and macrooutput node are saved - changed method getNodeDefinitionSourceCode() in MacroNode, split this in two halfes, main work is now done in MacroNode.getNodeSourceCode() - major overhaul of saving networks: changed the signature of all methods involved in producing source code for saving networks - changed again how networks are saved. Everything is much easier now, because we add the code to import all necessary libraries at the very beginning. Macro Nodes need no longer to save a beforeAddingToNetwork() method, they no longer have to loop over all ports to import libraries that are needed for port data types, and they don't have to import the libraries again in their afterAddingToNetwork() methods since this is now done at the beginning etc, etc, etc. Removed getLibraryImportCodeHeader method, removed self.libraryImportCache - moved deletePort() method from items to itemBase. Subclass this method in items and in macros Backwards INCOMPATIBLE Changes - split addLibrary method of Network into addLibraryFromName and addLibraryInstance. Addind an instance now requires to provide the module where the instance is defined and the name (both as strings). This was needed to generate the network descdription source code with the correct imports of libraries. - getLibraryImportCode was fixed in net.py and added to MacroNetwork (macros.py) - updated node libraries to reflect these changes Suggested Fix for users: Search and replace 'addLibrary' in existing saved networks with either 'addLibraryFromName' or addLibraryInstance' ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Release November 25, 2002 ------------------------- What's New - every node now has its own param. panel. The param. panel has now an 'immediate' checkbutton (on by default: this triggers node execution upon value changes of widgets in the param. panel), an apply button, which forces node execution, and a dismiss button to hide the param. panel - added multi-threaded execution model and added a lock and condition to the network object such that iterate nodes can schedule the excution of sub-networks for each value in their list - added support for cut, copy and paste nodes and connections - added selectAll (nodes) functionality - added mechanism to dynamically load libaries - StandardNodes are now part of the NetworkEditor - added new nodes to StandardNodes: ScrolledText, Multi-Checkbuttons, ChangeBackground (to change the background color of the ViPEr canvas, call method, getattr, setattr, len, iterate - added more documentation to many nodes - added support for moving nodes by clicking and dragging with middle button. When middle button is pressed on top of a node, this node becomes temporarly selected and can be moved until the button is released. When the button is released, the selection is restored. - added the ability to show/hide widgets in nodes by double clicking on the node. - added ability to reshape connections using left mouse button - runNodes checks whether new data is provided on at least 1 input port before executing node. This can be overruled by setting the new forceExecution flag. Inputports now know whether they have new data. - scaling nodes scales the label and hides widgets when node is too small - added eventHandler - added new menus to simpleNE, changed old ones - regression tests were added Bug Fixes - data viewer windows are now destroyed when the corresponding node is deleted - loading and saving networks was completely reworked - many issues in Editor.py were adressed and fixed - nodes now resize according to the ports, the label and the widgets - picking on node has now priority over picking on connections - refresh now restores the highlighting of the selected nodes - fixed bug that was not putting widgets in node to the proper location - moved ballons help down 30 pixels, to avoid flashing when balloon appeared over node - fixed bug that caused widget value to disappear after show/hide/show. Compatibility Information - multi-threaded execution on Silicon Graphics platforms can lead to core dump. Therefore, the user can choose to run single-threaded execution which works fine on SGI. Known Issues - saving networks with macro nodes does not work currently. This will be fixed as soon as possible - Undo is disabled since it is currently broken - Hyperbolic Scaling was removed from the menu since this was never working properly - removed show special ports entry from menu nodes as it is broken NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/__init__.py0000644000175000017500000000061312055234301023402 0ustar moellermoeller######################################################################### # # Date: Nov. 2001 Author: Michel Sanner # # Copyright: Michel Sanner and TSRI # ######################################################################### CRITICAL_DEPENDENCIES = ['Pmw', 'mglutil','numpy'] NONCRITICAL_DEPENDENCIES = ['idlelib', 'DejaVu','Vision','Support', 'PIL', 'cairo'] NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/customizedWidgets.py0000644000175000017500000004543211216031314025365 0ustar moellermoeller######################################################################### # # Date: July 2003 Authors: Daniel Stoffler, Michel Sanner # # stoffler@scripps.edu # sanner@scripps.edu # # Copyright: Daniel Stoffler, Michel Sanner, and TSRI # ######################################################################### """This module subclasses widgets such as the Pmw Notebook """ import sys, os import Pmw import Tkinter from mglutil.gui.BasicWidgets.Tk.customizedWidgets import KeySelectableScrolledCanvas from mglutil.gui import widgetsOnBackWindowsCanGrabFocus class kbScrolledCanvas(Pmw.ScrolledCanvas, KeySelectableScrolledCanvas): """Pmw.ScrolledCanvas with support to type the name of an item which will scroll the canvas to the requested item. """ def __init__(self, *args, **kw): if kw.has_key('itemNames'): itemNames = kw['itemNames'] del kw['itemNames'] else: itemNames = [''] # Pmw widgets are very delicate when it comes to subclassing! apply( Pmw.ScrolledCanvas.__init__, (self,)+args, kw) # now remove the initialization only keywords and configure the widget if kw.has_key('borderframe'): del kw['borderframe'] if kw.has_key('labelpos'): del kw['labelpos'] if kw.has_key('usehullsize'): del kw['usehullsize'] apply( self.configure, (self,), kw) myWidget = self KeySelectableScrolledCanvas.__init__(self, myWidget, itemNames) if os.name == 'nt': #sys.platform == 'win32': self.component('canvas').bind("", self.scrollUpDown) else: self.component('canvas').bind("", self.scrollUp) self.component('canvas').bind("", self.scrollDown) self.component('canvas').bind("", self.enter_cb) def scrollUp(self, event): self.component('canvas').yview_scroll(-1, "units") def scrollDown(self, event): self.component('canvas').yview_scroll(1, "units") def scrollUpDown(self, event): if event.delta < 0: self.scrollDown(event) else: self.scrollUp(event) def enter_cb(self, event=None): if widgetsOnBackWindowsCanGrabFocus is False: lActiveWindow = self.widget.focus_get() if lActiveWindow is not None \ and ( lActiveWindow.winfo_toplevel() != self.widget.winfo_toplevel() ): return self.component('canvas').focus_set() class mglNoteBook(Pmw.NoteBook): """This notebook implements a showpage and hidepage method that allows to show/hide notebook pages without destroying them.""" def __init__(self, *args, **kw): # Pmw widgets are very delicate when it comes to subclassing! apply( Pmw.NoteBook.__init__, (self,)+args, kw) def hidepage(self, pageName): """New method hidepage""" # hide is not possible if only one page present if len(self._pageNames) == 1: return pageInfo = self._pageAttrs[pageName] # return, if already hidden if pageInfo['visible'] == 0: return pageInfo['visible'] = 0 pageIndex = self.index(pageName) if pageIndex == 0: newTopIndex = 1 else: newTopIndex = pageIndex - 1 if newTopIndex >= 0: newTopPage = self._pageNames[newTopIndex] self.selectpage(newTopPage) if self._withTabs: self._pending['tabs'] = 1 self._layout() def showpage(self, pageName): """new method showpage""" pageInfo = self._pageAttrs[pageName] # return, if already visible if pageInfo['visible'] == 1: return pageInfo['visible'] = 1 if self._withTabs: self._pending['tabs'] = 1 self._layout() self.selectpage(pageName) def selectpage(self, page): """overwrite the original method to take in account hidden pages""" pageName = self._pageNames[self.index(page)] # return if page is not visible if self._pageAttrs[pageName]['visible'] == 0: return oldTopPage = self.getcurselection() if pageName != oldTopPage: self._pending['topPage'] = pageName if oldTopPage == self._topPageName: self._hull.delete(self._topPageItem) cmd = self['lowercommand'] if cmd is not None: cmd(oldTopPage) self._raiseNewTop(pageName) self._layout() # Set focus to the tab of new top page: if self._withTabs and self['arrownavigation']: if widgetsOnBackWindowsCanGrabFocus is False: lActiveWindow =self._pageAttrs[pageName]['tabbutton'].focus_get() if lActiveWindow is not None \ and ( lActiveWindow.winfo_toplevel() != \ self._pageAttrs[pageName]['tabbutton'].winfo_toplevel() ): return self._pageAttrs[pageName]['tabbutton'].focus_set() def previouspage(self, pageIndex = None): """overwrite original method""" if pageIndex is None: curpage = self.index(Pmw.SELECT) else: curpage = self.index(pageIndex) i = 1 while 1: if curpage > 0: prevpage = self._pageNames[curpage-i] if self._pageAttrs[prevpage]['visible'] == 0: i = i + 1 continue else: self.selectpage(curpage - i) break else: break def nextpage(self, pageIndex = None): """overwrite original method""" if pageIndex is None: curpage = self.index(Pmw.SELECT) else: curpage = self.index(pageIndex) i = 1 while 1: if curpage+i < len(self._pageNames): nextpage = self._pageNames[curpage+i] if self._pageAttrs[nextpage]['visible'] == 0: i = i + 1 continue else: self.selectpage(curpage + i) break else: break def _layout(self): """overwrite original method""" if not self.winfo_ismapped() or self._canvasSize is None: # Don't layout if the window is not displayed, or we # haven't yet received a event. return hullWidth, hullHeight = self._canvasSize borderWidth = self._borderWidth canvasBorder = int(self._hull['borderwidth']) + \ int(self._hull['highlightthickness']) if not self._withTabs: self.tabBottom = canvasBorder oldTabBottom = self.tabBottom if self._pending.has_key('borderColor'): self._lightBorderColor, self._darkBorderColor = \ Pmw.Color.bordercolors(self, self['hull_background']) # Draw all the tabs. if self._withTabs and (self._pending.has_key('tabs') or self._pending.has_key('size')): # Find total requested width and maximum requested height # of tabs. sumTabReqWidth = 0 maxTabHeight = 0 for pageInfo in self._pageAttrs.values(): sumTabReqWidth = sumTabReqWidth + pageInfo['tabreqwidth'] if maxTabHeight < pageInfo['tabreqheight']: maxTabHeight = pageInfo['tabreqheight'] if maxTabHeight != 0: # Add the top tab border plus a bit for the angled corners self.tabBottom = canvasBorder + maxTabHeight + borderWidth * 1.5 # Prepare for drawing the border around each tab button. tabTop = canvasBorder tabTop2 = tabTop + borderWidth tabTop3 = tabTop + borderWidth * 1.5 tabBottom2 = self.tabBottom tabBottom = self.tabBottom + borderWidth numTabs = 0 for v in self._pageAttrs.values(): if v['visible'] == 1: numTabs = numTabs + 1 availableWidth = hullWidth - 2 * canvasBorder - \ numTabs * 2 * borderWidth x = canvasBorder cumTabReqWidth = 0 cumTabWidth = 0 # Position all the tabs. for pageName in self._pageNames: pageInfo = self._pageAttrs[pageName] (windowitem, lightshadow, darkshadow) = pageInfo['tabitems'] if sumTabReqWidth <= availableWidth: tabwidth = pageInfo['tabreqwidth'] else: # This ugly calculation ensures that, when the # notebook is not wide enough for the requested # widths of the tabs, the total width given to # the tabs exactly equals the available width, # without rounding errors. cumTabReqWidth = cumTabReqWidth + pageInfo['tabreqwidth'] tmp = (2*cumTabReqWidth*availableWidth + sumTabReqWidth) \ / (2 * sumTabReqWidth) tabwidth = tmp - cumTabWidth cumTabWidth = tmp # Position the tab's button canvas item. self.coords(windowitem, x + borderWidth, tabTop3) # Tabs that are not shown are drawn as a single line if pageInfo['visible'] == 0: tabwidth = 1 self.itemconfigure(windowitem, width = tabwidth, height = maxTabHeight) # Make a beautiful border around the tab. left = x left2 = left + borderWidth left3 = left + borderWidth * 1.5 right = left + tabwidth + 2 * borderWidth right2 = left + tabwidth + borderWidth right3 = left + tabwidth + borderWidth * 0.5 self.coords(lightshadow, left, tabBottom2, left, tabTop2, left2, tabTop, right2, tabTop, right3, tabTop2, left3, tabTop2, left2, tabTop3, left2, tabBottom, ) self.coords(darkshadow, right2, tabTop, right, tabTop2, right, tabBottom2, right2, tabBottom, right2, tabTop3, right3, tabTop2, ) pageInfo['left'] = left pageInfo['right'] = right x = x + tabwidth + 2 * borderWidth # Redraw shadow under tabs so that it appears that tab for old # top page is lowered and that tab for new top page is raised. if self._withTabs and (self._pending.has_key('topPage') or self._pending.has_key('tabs') or self._pending.has_key('size')): if self.getcurselection() is None: # No pages, so draw line across top of page area. self.coords(self._pageTop1Border, canvasBorder, self.tabBottom, hullWidth - canvasBorder, self.tabBottom, hullWidth - canvasBorder - borderWidth, self.tabBottom + borderWidth, borderWidth + canvasBorder, self.tabBottom + borderWidth, ) # Ignore second top border. self.coords(self._pageTop2Border, 0, 0, 0, 0, 0, 0) else: # Draw two lines, one on each side of the tab for the # top page, so that the tab appears to be raised. pageInfo = self._pageAttrs[self.getcurselection()] left = pageInfo['left'] right = pageInfo['right'] self.coords(self._pageTop1Border, canvasBorder, self.tabBottom, left, self.tabBottom, left + borderWidth, self.tabBottom + borderWidth, canvasBorder + borderWidth, self.tabBottom + borderWidth, ) self.coords(self._pageTop2Border, right, self.tabBottom, hullWidth - canvasBorder, self.tabBottom, hullWidth - canvasBorder - borderWidth, self.tabBottom + borderWidth, right - borderWidth, self.tabBottom + borderWidth, ) # Prevent bottom of dark border of tabs appearing over # page top border. self.tag_raise(self._pageTop1Border) self.tag_raise(self._pageTop2Border) # Position the page border shadows. if self._pending.has_key('size') or oldTabBottom != self.tabBottom: self.coords(self._pageLeftBorder, canvasBorder, self.tabBottom, borderWidth + canvasBorder, self.tabBottom + borderWidth, borderWidth + canvasBorder, hullHeight - canvasBorder - borderWidth, canvasBorder, hullHeight - canvasBorder, ) self.coords(self._pageBottomRightBorder, hullWidth - canvasBorder, self.tabBottom, hullWidth - canvasBorder, hullHeight - canvasBorder, canvasBorder, hullHeight - canvasBorder, borderWidth + canvasBorder, hullHeight - canvasBorder - borderWidth, hullWidth - canvasBorder - borderWidth, hullHeight - canvasBorder - borderWidth, hullWidth - canvasBorder - borderWidth, self.tabBottom + borderWidth, ) if not self._withTabs: self.coords(self._pageTopBorder, canvasBorder, self.tabBottom, hullWidth - canvasBorder, self.tabBottom, hullWidth - canvasBorder - borderWidth, self.tabBottom + borderWidth, borderWidth + canvasBorder, self.tabBottom + borderWidth, ) # Color borders. if self._pending.has_key('borderColor'): self.itemconfigure('lighttag', fill = self._lightBorderColor) self.itemconfigure('darktag', fill = self._darkBorderColor) newTopPage = self._pending.get('topPage') pageBorder = borderWidth + self._pageMargin # Raise new top page. if newTopPage is not None: self._topPageName = newTopPage self._topPageItem = self.create_window( pageBorder + canvasBorder, self.tabBottom + pageBorder, window = self._pageAttrs[newTopPage]['page'], anchor = 'nw', ) # Change position of top page if tab height has changed. if self._topPageName is not None and oldTabBottom != self.tabBottom: self.coords(self._topPageItem, pageBorder + canvasBorder, self.tabBottom + pageBorder) # Change size of top page if, # 1) there is a new top page. # 2) canvas size has changed, but not if there is no top # page (eg: initially or when all pages deleted). # 3) tab height has changed, due to difference in the height of a tab if (newTopPage is not None or \ self._pending.has_key('size') and self._topPageName is not None or oldTabBottom != self.tabBottom): self.itemconfigure(self._topPageItem, width = hullWidth - 2 * canvasBorder - pageBorder * 2, height = hullHeight - 2 * canvasBorder - pageBorder * 2 - (self.tabBottom - canvasBorder), ) self._pending = {} def insert(self, pageName, before = 0, **kw): """overwrite original method""" if self._pageAttrs.has_key(pageName): msg = 'Page "%s" already exists.' % pageName raise ValueError, msg # Do this early to catch bad spec before creating any items. beforeIndex = self.index(before, 1) pageOptions = {} if self._withTabs: # Default tab button options. tabOptions = { 'text' : pageName, 'borderwidth' : 0, } # Divide the keyword options into the 'page_' and 'tab_' options. for key in kw.keys(): if key[:5] == 'page_': pageOptions[key[5:]] = kw[key] del kw[key] elif self._withTabs and key[:4] == 'tab_': tabOptions[key[4:]] = kw[key] del kw[key] else: raise KeyError, 'Unknown option "' + key + '"' # Create the frame to contain the page. page = apply(self.createcomponent, (pageName, (), 'Page', Tkinter.Frame, self._hull), pageOptions) attributes = {} attributes['page'] = page attributes['created'] = 0 attributes['visible'] = 1 if self._withTabs: # Create the button for the tab. def raiseThisPage(self = self, pageName = pageName): self.selectpage(pageName) tabOptions['command'] = raiseThisPage tab = apply(self.createcomponent, (pageName + '-tab', (), 'Tab', Tkinter.Button, self._hull), tabOptions) if self['arrownavigation']: # Allow the use of the arrow keys for Tab navigation: def next(event, self = self, pageName = pageName): self.nextpage(pageName) def prev(event, self = self, pageName = pageName): self.previouspage(pageName) tab.bind('', prev) tab.bind('', next) attributes['tabbutton'] = tab attributes['tabreqwidth'] = tab.winfo_reqwidth() attributes['tabreqheight'] = tab.winfo_reqheight() # Create the canvas item to manage the tab's button and the items # for the tab's shadow. windowitem = self.create_window(0, 0, window = tab, anchor = 'nw') lightshadow = self.create_polygon(0, 0, 0, 0, 0, 0, tags = 'lighttag', fill = self._lightBorderColor) darkshadow = self.create_polygon(0, 0, 0, 0, 0, 0, tags = 'darktag', fill = self._darkBorderColor) attributes['tabitems'] = (windowitem, lightshadow, darkshadow) self._pending['tabs'] = 1 self._pageAttrs[pageName] = attributes self._pageNames.insert(beforeIndex, pageName) # If this is the first page added, make it the new top page # and call the create and raise callbacks. if self.getcurselection() is None: self._pending['topPage'] = pageName self._raiseNewTop(pageName) self._layout() return page NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/datatypes.py0000644000175000017500000006740111263207306023656 0ustar moellermoeller## Automatically adapted for numpy.oldnumeric Jul 23, 2007 by ######################################################################## # # Date: Nov. 2001 Author: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner, Daniel Stoffler and TSRI # # Revision: Guillaume Vareille # ######################################################################### import types import numpy.oldnumeric as Numeric import warnings import array class AnyArrayType(dict): """Base class for defining types of Ports which can handle single values or arrays of arbitrary shape. this class must be inherited only !!!! get inspired by class IntType below the constructor arguments are: name: string used to identify an instance of a AnyArrayType object ex: float you can add the datashape to the name ex: float(0,>=3,4,0) defines a 4D array with constrains on the extent of each dimension. A value of 0 means there is not required extent Multi dimensional arrays can be specified by passing a datashape in parenthesis in the form of a string of comma separated expressions. The number of expressions is the number of dimensions. Valid expressions are : 0 : meaning there is no condition on this dimension. (in addition, the leading 0 dimensions are optional) x : length of this dimension must be x >x : length of this dimension must be > x =x: length of this dimension must be >= x <=x: length of this dimension must be <= x for instance a list of vertices can be represented by float(0,3) float: describe the smallest elementary datum (one coordinate). you can introduce your own elementary type (generally inheriting basic types), this allow you to easily define color and shape for this type (see InstancematType below) 3: because there are 3 coordinates per vertex. 0: because we don't have any limitation on the number of vertices. It is a leading 0, therefore this dimension is optional (meaning: if you're passing a single vertex [x,y,z], your are not oblige to put it in a list. ex: you can give [x,y,z] or [[x,y,z]]) """ def __init__(self, # used as a key the datatype manager name='None', datashape=None, # describe (0,>=3) klass=None, # class of Python object passing through port. # This is used for data type propagation dataDescr=None, # string describing data in tool tip color=None, # color of port icon shape='diamond', # shape used for port icon width=None, height=None, # width and height of port icon ): #import traceback;traceback.print_stack() #print "AnyArrayType __init__ name datashape", name, datashape self['name'] = name self['datashape'] = datashape self['class'] = klass self['dataDescr'] = dataDescr if color is None: color = 'white' self['color'] = color # port icon color if shape is None: shape = self._shape(datashape) self['shape'] = shape # port icon if width is None: width = 12 self['width'] = width # port icon width if height is None: if shape == 'circle' or shape == 'square': height = self['width'] else: height = 8 self['height'] = height # port icon height # list of lambda functions used to check dimension of incoming data self.dimensionTest = [] # compile dimension checking functions that will be used in validate if datashape is None: self.lenDimensionTest = 0 else: for dimlim in datashape[1:-1].split(','): # this case should be handled using >=1, i.e. required # dimension with no constriaint #if dimlim=='': # no restirction on this dimension # self.dimensionTest.append( None ) if dimlim.isdigit(): # we have a fixed extent for this dimension if int(dimlim)!=0: self.dimensionTest.append( lambda x: x==int(dimlim) ) else: # no restriction on this dimension self.dimensionTest.append( None ) else: strg = '' for c in dimlim: if c in ['>', '<']: strg += 'x '+c else: strg += c self.dimensionTest.append( eval('lambda x: %s'%strg) ) self.lenDimensionTest = len(self.dimensionTest) def validateDims(self, data): """data comes in as a numeric array, and returns True if required shapes pass tests, returns False is tests fail, or returns number of missing dimensions if test pass but optional dims are missing """ missingDimensions = self.lenDimensionTest - len(data.shape) for i in range(missingDimensions): data.shape = (1,) + data.shape for dim,f in zip(data.shape, self.dimensionTest): if f is not None: if not f(dim): return False, data if missingDimensions > 0: return missingDimensions, data else: return True, data def _shape(self, datashape): """This function is called by the constructor to return the number of edges used to draw the port icon based on the number of dimensions of the type. NOTE: this function is only used if the icon shape is not specified as an argument to the constructor. """ if datashape is not None: lDimensions = datashape[1:-1].split(',') lShape = len(lDimensions) * 2 - 1 if lDimensions[0] == '0': lShape -= 1 else: lShape = 0 #print "icon shape:", lShape return lShape def cast(self, data): return False, None def validate(self, data): #print "validate", data if self['class'] is None: return True, data if self['datashape'] is None: if isinstance(data, self['class']): return True, data else: return False, None try: lArray = Numeric.array(data) lArray0 = lArray.ravel()[0] while hasattr(lArray0,'shape'): lArray0 = lArray0.ravel()[0] if isinstance(lArray0, self['class']): return True, lArray else: return False, None except: return False, None # DEPRECATED CLASS: use AnyArrayType instead from UserDict import UserDict class AnyType( UserDict # , # AnyArrayType ): def __init__(self, name='Old None', color='white', shape='diamond', width=12, height=8, klass=None, dataDescr=None # , datashape=None ): UserDict.__init__(self) # AnyArrayType.__init__(self, name=name, color=color, # shape=shape, width=width, height=height, # klass=klass, dataDescr=dataDescr, # datashape=datashape) # warnings.warn('AnyType is deprecated, use AnyArrayType instead', # DeprecationWarning, stacklevel=2) # Note: use EVEN numbers for width, height, since they will be # divided by 2 for halfPortWidth, halfPortHeight self.data['name'] = name self.data['dataDescr'] = dataDescr self.data['shape'] = shape # port icon self.data['width'] = width # port icon width self.data['height'] = height # port icon height self.data['color'] = color # port icon color self.data['class'] = klass self.data['datashape'] = None # added to be compatible with def cast(self, data): """returns a success status (true, false) and the coerced data""" assert True, data # to silent pychecker return False, None def validate(self, data): """returns true if data if of the proper type""" return True class FloatType(AnyArrayType): def __init__(self, name='float', datashape=None, color='green', shape='circle', width=None, height=None): AnyArrayType.__init__(self, name=name, color=color, shape=shape, width=width, height=height, klass=float, datashape=datashape) def validate(self, data): if self['datashape'] is None: if isinstance(data, types.FloatType): return True, data else: return False, None try: lArray = Numeric.array(data, 'f') return True, lArray except: return False, None def cast(self, data): if self['datashape'] is None: if isinstance(data, types.FloatType): return True, data try: data = float(data) return True, data except: return False, data try: lArray = (Numeric.array(data)).astype('f') return True, lArray except: return False, None class IntType(AnyArrayType): def __init__(self, name='int', datashape=None,color='yellow', shape='circle', width=None, height=None): AnyArrayType.__init__(self, name=name, color=color, shape=shape, width=width, height=height, klass=int, datashape=datashape) def validate(self, data): if self['datashape'] is None: if isinstance(data, types.IntType): return True, data else: return False, None try: lArray = Numeric.array(data, 'i') return True, lArray except: return False, None def cast(self, data): if self['datashape'] is None: if isinstance(data, types.IntType): return True, data try: data = int(data) return True, data except: return False, data try: lArray = (Numeric.array(data)).astype('i') return True, lArray except: return False, None class BooleanType(AnyArrayType): def __init__(self, name='boolean', datashape=None, color='yellow', shape='rect', width=None, height=None): AnyArrayType.__init__(self, name=name, color=color, shape=shape, width=width, height=height, klass=bool, datashape=datashape) def validate(self, data): if self['datashape'] is None: if type(data) == types.BooleanType: return True, data else: return False, None try: lArray = Numeric.array(data, 'B') return True, lArray except: return False, None def cast(self, data): if self['datashape'] is None: if type(data) == types.BooleanType: return True, data try: data = bool(data) return True, data except: return False, data try: #FIXME: this 'b' stands for binary not boolean lArray = (Numeric.array(data)).astype('B') return True, lArray except: return False, None class StringType(AnyArrayType): def __init__(self, name='string', datashape=None, color='white', shape='oval', width=None, height=None): AnyArrayType.__init__(self, name=name, color=color, shape=shape, width=width, height=height, klass=str, datashape=datashape) def validate(self, data): if self['datashape'] is None: if type(data) == types.StringType: return True, data else: return False, None try: lArray = Numeric.array(data, 'O') #lShape = array.shape #lArray = map(str, array) #array.shape = lShape return True, lArray except: return False, None def cast(self, data): if self['datashape'] is None: if type(data) == types.StringType: return True, data try: data = str(data) return True, data except: return False, data try: lArray = (Numeric.array(data)).astype('O') return True, lArray except: return False, None #class StringType(AnyType): # # def __init__(self): # AnyType.__init__(self) # self.data['name'] = 'string' # self.data['color'] = 'white' # self.data['shape'] = 'oval' # self.data['width'] = 12 # circle # self.data['height'] = 12 # self.data['class'] = str # # def validate(self, data): # return type(data)==types.StringType # # def cast(self, data): # try: # return True, str(data) # except: # return False, data class ListType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'list' self.data['color'] = 'cyan' self.data['shape'] = 'oval' self.data['width'] = 12 # circle self.data['height'] = 12 self.data['class'] = list def validate(self, data): return type(data)==types.ListType def cast(self, data): try: if type(data)==types.StringType: try: lData = eval(data) except: lData = data else: lData = data try: lData = list(lData) except: lData = [lData] return True, lData except: return False, data class TupleType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'tuple' self.data['color'] = 'cyan' self.data['shape'] = 'oval' self.data['width'] = 12 # circle self.data['height'] = 12 self.data['class'] = tuple def validate(self, data): return type(data)==types.TupleType def cast(self, data): try: data = tuple(data) return True, data except: return False, data class DictType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'dict' self.data['color'] = 'cyan' self.data['shape'] = 'oval' self.data['width'] = 12 # circle self.data['height'] = 12 self.data['class'] = dict def validate(self, data): return type(data)==types.DictType class ArrayType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'array' self.data['color'] = 'cyan' self.data['shape'] = 'oval' self.data['width'] = 12 # circle self.data['height'] = 12 self.data['class'] = array.array def validate(self, data): return isinstance(data, array.ArrayType) class NumericArrayType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'NumericArray' self.data['color'] = 'orange' self.data['shape'] = 'pentagon' self.data['class'] = Numeric.array def validate(self, data): return isinstance(data, Numeric.ArrayType) def cast(self, data): try: data = Numeric.array(data) return True, data except: return False, data class VectorType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'vector' self.data['color'] = 'cyan' self.data['shape'] = 'oval' self.data['width'] = 12 # circle self.data['height'] = 12 self.data['class'] = None def validate(self, data): try: if type(data) != types.StringType: len(data) return True else: return False except: return False def cast(self, data): """returns a success status (true, false) and the coerced data """ if type(data) == types.StringType: return True, (data, ) return False, None class TriggerOut(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'triggerOut' self.data['color'] = 'orange' self.data['shape'] = 'square' del self.data['class'] class TriggerIn(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'triggerIn' self.data['color'] = 'orange' self.data['shape'] = 'square' del self.data['class'] class TkColorType(AnyType): def __init__(self): AnyType.__init__(self) self.data['name'] = 'tkcolor' self.data['color'] = 'orange' self.data['shape'] = 'square' del self.data['class'] def validate(self, data): import Tkinter try: Tkinter._default_root.winfo_rgb(data) return True finally: return False class TypeManager: """Port type object manager This object is used to register port type instances which are associated with port. new types can be registered by passing an instance of port type object to the .addType(instance) method. The .portTypeInstances dictionary provides a mapping between the a name used to describe the data acceptable for a port and an instance of a subclass of AnyArrayType. This name can be The .reverseLookUp dictionary is used to obtain a port type from the class of and object. This is used top propagate data types to ports that are mutatable """ def __init__(self): self.portTypeInstances = {} # key: name+datashape, value: instance self.reverseLookUp = {} # key:class, value:typeObject self.addType( AnyArrayType() ) # basic types self.addType( FloatType() ) self.addType( IntType() ) #self.addType( ObjectType() ) self.addType( BooleanType() ) self.addType( StringType() ) # for backward compatibility: self.portTypeInstances['str'] = self.portTypeInstances['string'] # synonyms self.addSynonym('coord2', 'float', '(2)', color='green', shape='oval') self.addSynonym('coord3', 'float', '(3)', color='green', shape='rect') self.addSynonym('normal3', 'float', '(3)', color='blue', shape='rect') self.addSynonym('colorfloat3or4', 'float', '(>=3 and <=4)', color='orange', shape='rect') self.addSynonym('instancemat', 'float', '(4,4)', color='cyan', shape='rect') self.addSynonym('indice2', 'int', '(2)', color='purple', shape='rect') self.addSynonym('indice2+', 'int', '(>=2)', color='purple', shape='rect') self.addSynonym('indice3or4', 'int', '(>=3 and <=4)', color='purple', shape='rect') self.addSynonym('2Darray', 'float', '(>0,>0)', color='cyan') self.addSynonym('object', existingTypeName='None') self.addSynonym('colorRGB', existingTypeName='colorfloat3or4') self.addSynonym('colorsRGB', existingTypeName='colorRGB',datashape='(0)') self.addSynonym('coordinates3D', existingTypeName='coord3',datashape='(0)') self.addSynonym('faceIndices', existingTypeName='indice3or4',datashape='(0)') self.addSynonym('normals3D', existingTypeName='normal3',datashape='(0)') self.addType( AnyType() ) self.addType( DictType() ) self.addType( ListType() ) self.addType( TkColorType() ) self.addType( TupleType() ) self.addType( ArrayType() ) self.addType( NumericArrayType() ) self.addType( VectorType() ) self.addType( TriggerIn() ) self.addType( TriggerOut() ) def addSynonym(self, synonymName, existingTypeName=None, datashape=None, color=None, shape=None, width=None, height=None): """ method to create synonym types synonymName: can be an existing name with a diferent datashape: (existingTypeName must be None, and the basename 'coord3' must be registered) self.addSynonym('coord3(3,4)') existingTypeName: must be a registered name (it can be datatshaped, if it is already registered like that) self.addSynonym('coordinates3D', existingTypeName='coord3',datashape='(3,4)') self.addSynonym('coordinates3D', existingTypeName='coord3(4)',datashape='(3)') self.addSynonym('coordinates3D', existingTypeName='coord3(3,4)') """ if existingTypeName is None: assert datashape is None lSplitName = synonymName.split('(') existingTypeName = lSplitName[0] if len(lSplitName) == 2: datashape = '(' + lSplitName[1] basicInstance = self.portTypeInstances[existingTypeName] innerDatashape = basicInstance['datashape'] if innerDatashape is not None: if datashape is None: datashape = innerDatashape else: innerDatashape = innerDatashape.split('(')[1] datashape = datashape.split(')')[0] datashape = datashape + ',' + innerDatashape if color is None: color = basicInstance['color'] if shape is None: shape = basicInstance['shape'] if width is None: width = basicInstance['width'] if height is None: if shape == 'circle' or shape == 'square': height = width else: height = 2 * width / 3 instance = basicInstance.__class__( name=synonymName, datashape=datashape, color=color, shape=shape, width=width, height=height) self.addType( instance ) def getSynonymDict(self, synonymName): lDict = {} typeInstance = self.portTypeInstances[synonymName] lDict['existingTypeName'] = typeInstance.__class__()['name'] if lDict['existingTypeName'] == synonymName: return None lDict['synonymName'] = synonymName lDict['datashape'] = typeInstance['datashape'] lDict['color'] = typeInstance['color'] lDict['shape'] = typeInstance['shape'] lDict['width'] = typeInstance['width'] lDict['height'] = typeInstance['height'] return lDict def addSynonymDict(self, aDict): if aDict.has_key('existingTypeName') is False: aDict['existingTypeName'] = None if aDict.has_key('datashape') is False: aDict['datashape'] = None if aDict.has_key('color') is False: aDict['color'] = None if aDict.has_key('shape') is False: aDict['shape'] = None if aDict.has_key('width') is False: aDict['width'] = None if aDict.has_key('height') is False: aDict['height'] = None self.addSynonym(aDict['synonymName'], existingTypeName=aDict['existingTypeName'], datashape=aDict['datashape'], color=aDict['color'], shape=aDict['shape'], width=aDict['width'], height=aDict['height']) def addType(self, dtypeinstance): """register a port type instance """ #print "addType dtypeinstance", dtypeinstance['name'], dtypeinstance['datashape'] #import pdb;pdb.set_trace() if isinstance(dtypeinstance, AnyType) or \ isinstance(dtypeinstance, AnyArrayType): # we reduce dtypeinstance to its base class name splittype = dtypeinstance['name'].split('(') basetypename = splittype[0] if dtypeinstance['datashape'] is not None: if dtypeinstance['name'].endswith(dtypeinstance['datashape']): # make sure the base type exists if self.portTypeInstances.has_key(basetypename) is None: msg = 'base datatype '+basetypename msg += ' not found in types table when adding %s'%str(dtypeinstance) warnings.warn(msg) return storagename = dtypeinstance['name'] else: storagename = basetypename if self.portTypeInstances.has_key(storagename): if isinstance(dtypeinstance, AnyType): if not ( \ isinstance(self.portTypeInstances[storagename], AnyType) \ and \ (dtypeinstance.data == self.portTypeInstances[storagename].data) \ ): msg = 'Warning! datatype '+storagename+' already registered differently' warnings.warn(msg) return elif isinstance(dtypeinstance, AnyArrayType): if not ( \ isinstance(self.portTypeInstances[storagename], AnyArrayType) \ and \ (dtypeinstance == self.portTypeInstances[storagename]) \ ): msg = 'Warning! datatype '+storagename+' already registered differently' warnings.warn(msg) return self.portTypeInstances[storagename] = dtypeinstance if dtypeinstance.has_key('class'): if not self.reverseLookUp.has_key(dtypeinstance['class']): self.reverseLookUp[dtypeinstance['class']] = dtypeinstance else: raise RuntimeError('bad dtypeinstance argument') def getType(self, fullName): #print "getType fullName", fullName #import traceback;traceback.print_stack() #import pdb;pdb.set_trace() splittype = fullName.split('(') typename = splittype[0] datashape = None if len(splittype)==2: datashape = '(' + splittype[1] if not self.portTypeInstances.has_key(typename): msg = 'base datatype ' + typename msg += ' not found in types table' warnings.warn(msg) #import traceback;traceback.print_stack() return self.portTypeInstances['None'] if self.portTypeInstances.has_key(fullName) is False: # base class exist but not with this datashape, we add it self.addSynonym(fullName, typename, datashape) return self.portTypeInstances[fullName] def getTypeFromClass(self, klass): """ return the data type object for types that can be looked up i.e. have a class attribute """ return self.reverseLookUp.get(klass, self.reverseLookUp.get(None)) def get(self, key, default=None): return self.portTypeInstances.get(key, default) NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/drawNode.py0000644000175000017500000003711212205223567023423 0ustar moellermoeller######################################################################## # # Date: Sept 2012 Author: Michel Sanner # # sanner@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner and TSRI # ######################################################################### # # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/drawNode.py,v 1.3 2013/08/21 20:47:51 sanner Exp $ # # $Id: drawNode.py,v 1.3 2013/08/21 20:47:51 sanner Exp $ # import cairo, os from PIL import Image, ImageFilter from math import sqrt, pi def dropShadow( image, offset=(5,5), background=0x00ffffff, shadow=0xff000000, border=8, iterations=3): # taken from http://code.activestate.com/recipes/474116-drop-shadows-with-pil/ # modified by Michel Sanner to work with transparency, using masks # # adds offset + 2*border padding # the original image is at (border - min(offset[0], 0), border - min(offset[1], 0)) """ Add a gaussian blur drop shadow to an image. image - The image to overlay on top of the shadow. offset - Offset of the shadow from the image as an (x,y) tuple. Can be positive or negative. background - Background colour behind the image. shadow - Shadow colour (darkness). border - Width of the border around the image. This must be wide enough to account for the blurring of the shadow. iterations - Number of times to apply the filter. More iterations produce a more blurred shadow, but increase processing time. """ # to fix bug in 1.1.7 http://hg.effbot.org/pil-2009-raclette/changeset/fb7ce579f5f9 image.load() r,g,b,a = image.split() # Create the backdrop image -- a box in the background colour with a # shadow on it. totalWidth = image.size[0] + abs(offset[0]) + 2*border totalHeight = image.size[1] + abs(offset[1]) + 2*border back = Image.new(image.mode, (totalWidth, totalHeight), background) # Place the shadow, taking into account the offset from the image shadowLeft = border + max(offset[0], 0) shadowTop = border + max(offset[1], 0) back.paste(shadow, [shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]], mask=a ) # Apply the filter to blur the edges of the shadow. Since a small kernel # is used, the filter must be applied repeatedly to get a decent blur. n = 0 while n < iterations: back = back.filter(ImageFilter.BLUR) n += 1 # Paste the input image onto the shadow backdrop imageLeft = border - min(offset[0], 0) imageTop = border - min(offset[1], 0) back.paste(image, (imageLeft, imageTop), mask=a) return back, (imageLeft,imageTop) # return offset of original image in new image class CairoNodeRenderer: def __init__(self): self.nodeOutLineWidth = 4 self.border = 10 self.bbox = [99999, 99999, 0,0] # node icon bbox def drawSquareFlatBox(self, width, height, outline, fill, macro=False): # draw the box # old version that drew flat boxes (no shadow) # fill the rectangle with 0.5 alpha self.ctx.rectangle( ulx, uly, width, height) self.ctx.set_source_rgba(*fill) self.ctx.fill() # update bbox if ulxself.bbox[2]: self.bbox[2] = ulx+width if uly+height>self.bbox[3]: self.bbox[3] = uly+height # set color self.ctx.set_source_rgba(*outline) # build a path for a rectangle with 5 pixels padding self.ctx.rectangle( ulx, uly, width, height) self.ctx.set_line_width(4) # the draw the rectangle out line self.ctx.stroke() if macro: # draw outter box self.ctx.set_source_rgba(*outline) # build a path for a rectangle with 5 pixels padding self.ctx.rectangle( ulx+8, uly+8, width-16, height-16) self.ctx.set_line_width(4) # the draw the rectangle out line self.ctx.stroke() def roundedRectangle(self, x, y, width, height): ctx = self.ctx aspect = width / height # aspect ratio corner_radius = height / 5.0 # and corner curvature radius radius = corner_radius / aspect degrees = pi / 180.0 ctx.new_sub_path() ctx.arc(x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees) ctx.arc(x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees) ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees) ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees) ctx.close_path() def roundedRectangleEdge(self, x, y, width, height, thickness=4): ctx = self.ctx aspect = width / height # aspect ratio corner_radius = height / 5.0 # and corner curvature radius radius = corner_radius / aspect degrees = pi / 180.0 ctx.new_sub_path() ctx.arc(x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees) ctx.arc(x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees) ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees) ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees) x += thickness y += thickness width -= 2*thickness height -= 2*thickness aspect = width / float(height) # aspect ratio corner_radius = height / 5.0 # and corner curvature radius radius = corner_radius / aspect degrees = pi / 180.0 ctx.new_sub_path() ctx.arc(x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees) ctx.arc(x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees) ctx.arc(x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees) ctx.arc(x + radius, y + radius, radius, 180 * degrees, 270 * degrees) ctx.close_path() def draw3DFrame(self, x, y, width, height, outline, fill, thickness): ctx = self.ctx ctx.new_path() # first 2 rectangles (ramp going up of width thickness) self.roundedRectangleEdge(x, y, width, height, thickness) size = max(width, height)/2 cx = width/2 cy = height/2 pat = cairo.LinearGradient(cx-size, cy-size, cx+size, cy+size) pat.add_color_stop_rgba(0, *outline) pat.add_color_stop_rgba(1, 0.2, 0.2, 0.2, 1) ctx.set_source(pat) ctx.set_fill_rule(cairo.FILL_RULE_EVEN_ODD) ctx.fill() #ctx.set_source_rgb(0,0,0) #ctx.stroke() thick2 = 2*thickness thick3 = 3*thickness thick4 = 4*thickness thick6 = 6*thickness # second set of 2 rectangles flat color (top of ridge) of witdh thickness self.roundedRectangleEdge(x+thickness, y+thickness, width-thick2, height-thick2, thickness) ctx.set_source_rgb( outline[0]-0.01, outline[1]+0.01, outline[2] ) ctx.fill() # third set of 2 rectangles flat color (top of ridge) self.roundedRectangleEdge(x+thick2, y+thick2, width-thick4, height-thick4, thickness) pat = cairo.LinearGradient(cx-size, cy-size, cx+size, cy+size) pat.add_color_stop_rgba(1, *outline) pat.add_color_stop_rgba(0, 0.2, 0.2, 0.2, 1) ctx.set_source(pat) ctx.fill() # add fill self.roundedRectangle(x+thick3, y+thick3, width-thick6, height-thick6) #ctx.set_source_rgba(0,0,0,1) #ctx.stroke() ctx.set_source_rgba(*fill) ctx.fill() def getPilImage(self): buf = self.surface.get_data() width = self.width+2*self.border height = self.height+2*self.border return Image.frombuffer('RGBA', (width, height), buf, 'raw', 'BGRA', 0, 1) def addDropShadow(self): image = self.getPilImage() return dropShadow(image) def makeCircleNodeImage(self, width, height, outline, fill, macro=False): self.width = int(width) self.height = int(height) # upper left corner of the box #self.ul = ulx, uly = self.size/2-width/2, self.size/2-height/2 self.ul = ulx, uly = self.border, self.border self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width+2*self.border, self.height+2*self.border) self.ctx = cairo.Context (self.surface) self.ctx.save() self.ctx.translate(self.border+self.width/2., self.border+self.height/2) self.ctx.scale (self.width, self.height) #pat = cairo.RadialGradient (0.35, 0.3, 0.1, # 0.5, 0.5, .8) #self.ctx.set_source (pat) #pat.add_color_stop_rgba (0, 1, 1, 1, 1) #pat.add_color_stop_rgba (1, 0, 0, 0, 1) self.ctx.arc (0., 0., (width-10)/(2*width), 0, 2*pi) self.ctx.set_line_width(.03) self.ctx.set_source_rgba(*outline) self.ctx.stroke() self.ctx.arc (0., 0., (width-15)/(2*width), 0, 2*pi) self.ctx.set_source_rgba(*fill) self.ctx.fill() self.ctx.restore() #self.drawSquareFlatBox(width, height, outline, fill, macro) def makeNodeImage(self, width, height, outline, fill, macro=False): self.width = int(width) self.height = int(height) #self.size = int(sqrt(self.width*self.width + self.height*self.height)) # upper left corner of the box #self.ul = ulx, uly = self.size/2-width/2, self.size/2-height/2 self.ul = ulx, uly = self.border, self.border self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width+2*self.border, self.height+2*self.border) self.ctx = cairo.Context (self.surface) #self.drawSquareFlatBox(width, height, outline, fill, macro) if macro: self.draw3DFrame(ulx, uly, self.width, self.height, outline, fill, 2) else: self.draw3DFrame(ulx, uly, self.width, self.height, outline, fill, 1.5) def drawIcon(self, filename): iconImage = cairo.ImageSurface.create_from_png(filename) imwidth = iconImage.get_width() imheight = iconImage.get_height() ulx, uly = self.ul[0]+5, self.ul[1]+5 # define upper left corner self.ctx.set_source_surface(iconImage, ulx, uly) self.ctx.rectangle(ulx, uly, ulx+imwidth, uly+imheight) self.ctx.fill() #def drawPort(self, ptype, x, y, size, vector, line, fill, outline, label, edge): def drawPort(self, ptype, x, y, descr): vector = descr.get('vector') # flip y as cairo origin is upper left corner vector = [vector[0], -vector[1]] size = descr.get('size', 10) fill = descr.get('fill', (1,1,1,1)) line = descr.get('line', (0,0,0,1)) outline = descr.get('outline', (0,0,0,1)) label = descr.get('label', None) edge = descr['edge'] # draw a Port # set color halfSize = size/2 self.ctx.set_source_rgba(*fill) self.ctx.rectangle( x-halfSize, y-halfSize, size, size) self.ctx.fill() self.ctx.set_source_rgba(*outline) self.ctx.rectangle( x-halfSize, y-halfSize, size, size) self.ctx.set_line_width(2) self.ctx.stroke() # update bbox if x-halfSizeself.bbox[2]: self.bbox[2] = x+halfSize if y+halfSize>self.bbox[3]: self.bbox[3] = y+halfSize # draw the arrow head if ptype=='in': vx, vy = -vector[0]*halfSize, -vector[1]*halfSize else: vx, vy = vector[0]*halfSize, vector[1]*halfSize px, py = -vy*.5, vx*.5 # orthogonal vector self.ctx.set_source_rgba(*line) self.ctx.set_line_width(4) self.ctx.set_line_join(cairo.LINE_JOIN_BEVEL) self.ctx.move_to(x-vx*.5-px, y-vy*.5-py) self.ctx.line_to(x+vx*.8, y+vy*.8) #arrow tip self.ctx.line_to(x-vx*.5+px, y-vy*.5+py) self.ctx.stroke() # draw the arrow line self.ctx.set_line_width(1) self.ctx.move_to(x+vx, y+vy) self.ctx.line_to(x-vx, y-vy) self.ctx.stroke() # draw port name if label: self.ctx.set_source_rgb(0, 0, 0) self.ctx.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL) self.ctx.set_font_size(10.) x_bearing, y_bearing, width, height = self.ctx.text_extents(label)[:4] if edge=='top': self.ctx.move_to( x - width/2, y + halfSize + 4 + height) elif edge=='bottom': self.ctx.move_to( x - width/2, y - halfSize - 4 ) elif edge=='left': self.ctx.move_to( x + halfSize + 4, y + height/2.) elif edge=='right': self.ctx.move_to( x - width - x_bearing - 10, y + height/2.) self.ctx.show_text(label) def drawLabel(self, label, padding): self.ctx.set_source_rgb(0, 0, 0) self.ctx.select_font_face("Sans", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) self.ctx.set_font_size(18.) x_bearing, y_bearing, width, height = self.ctx.text_extents(label)[:4] cx = self.border + padding['left'] + (self.width-padding['left']-padding['right'])/2 cy = self.border + padding['top'] + (self.height-padding['top']-padding['bottom'])/2 self.ctx.move_to(cx - width/2 - x_bearing, cy-height/2 - y_bearing) self.ctx.show_text(label) def getLabelSize(self, label, font='Sans', size=18, slant=cairo.FONT_SLANT_NORMAL, weight=cairo.FONT_WEIGHT_BOLD): surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 800, 800) ctx = cairo.Context(surface) ctx.select_font_face(font, slant, weight) ctx.set_font_size(size) x_bearing, y_bearing, width, height = ctx.text_extents(label)[:4] return x_bearing, y_bearing, width, height #ctx.scale (WIDTH, HEIGHT) # Normalizing the canvas ## pat = cairo.LinearGradient (0.0, 0.0, 0.0, 1.0) ## pat.add_color_stop_rgba (1, 0.7, 0, 0, 0.5) # First stop, 50% opacity ## pat.add_color_stop_rgba (0, 0.9, 0.7, 0.2, 1) # Last stop, 100% opacity ## ctx.rectangle (0, 0, 1, 1) # Rectangle(x0, y0, x1, y1) ## ctx.set_source (pat) ## ctx.fill () ## ctx.translate (0.1, 0.1) # Changing the current transformation matrix ## ctx.move_to (0, 0) ## ctx.arc (0.2, 0.1, 0.1, -math.pi/2, 0) # Arc(cx, cy, radius, start_angle, stop_angle) ## ctx.line_to (0.5, 0.1) # Line to (x,y) ## ctx.curve_to (0.5, 0.2, 0.5, 0.4, 0.2, 0.8) # Curve(x1, y1, x2, y2, x3, y3) ## ctx.close_path () ## ctx.set_source_rgb (0.3, 0.2, 0.5) # Solid color ## ctx.set_line_width (0.02) ## ctx.stroke () ## fill = (0.82, 0.88, 0.95, 0.5) ## line = (0.28, 0.45, 0.6, 1.) ## from math import pi ## renderer = CairoNodeRenderer() ## renderer.makeNodeImage(200, 100, line, fill) ## ulx, uly = renderer.ul ## renderer.drawPort(ulx+60, uly, 'input', line, (1,1,1,1)) ## renderer.drawPort(ulx+120, uly, 'output', line, (1,1,1,1)) ## renderer.drawPort(ulx+60, uly+100, 'output', line, (1,1,1,1)) ## renderer.drawPort(ulx+120, uly+100, 'input', line, (1,1,1,1)) ## renderer.drawLabel('Node 1') ## renderer.surface.write_to_png ("node1.png") # Output to PNG NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/flow.py0000644000175000017500000004404410440106057022622 0ustar moellermoeller######################################################################### # # Date: Nov. 2001 Author: Michel Sanner # # Copyright: Michel Sanner and TSRI # ######################################################################### import threading, traceback, time class ExecutionThread(threading.Thread, threading._Verbose): """ class used to schedule a list of nodes in a separate thread. """ def __init__(self, net, nodes): threading.Thread.__init__(self, name='scheduler') threading._Verbose.__init__(self) self.net = net self.nodes = nodes def run(self): """run all nodes in the order they appear in the list""" net = self.net net.execStatusLock.acquire() net.execStatus = 'running' net.execStatusLock.release() net.RunLock.acquire() t1 = time.time() net.runNodes(self.nodes) t2 = time.time() net.execStatusLock.acquire() net.execStatus = 'waiting' net.execStatusLock.release() #print 'ran in:', t2-t1 net.RunLock.release() class AfterExecution(threading.Thread, threading._Verbose): """ class used to wait for the end of an execution and call a fucntion """ def __init__(self, net, func=None): threading.Thread.__init__(self, name='scheduler') self.net = net if func is not None: assert callable(func) self.func = func def run(self): """run all nodes in the order they appear in the list""" net = self.net # get the lock net.RunLock.acquire() # release lock and go to sleep until notified net.RunLockCond.wait() # call the function self.func(net) net.RunLock.release() ## class MTScheduler(threading.Thread, threading._Verbose): ## """ class used to schedule the multi-threaded execution of the nodes ## in the subtrees specified by a set of root nodes""" ## # FIXME we might want to add a limit number of threads to be started ## # simultaneousley ## def __init__(self, roots): ## threading.Thread.__init__(self, name='scheduler') ## threading._Verbose.__init__(self) ## self.mtstatelock = threading.RLock() ## self.waitLock = threading.RLock() ## self.waitCond = threading.Condition(self.waitLock) ## self.threadObjects = [] ## ## USED TO FLASH NETWORK ONCE ONLY ## ## self.allNodes = [] # list of nodes to be run ## self.roots = roots ## self.done = 0 # will be set to 1 after all nodes have run ## for node in roots: ## node.mtstate = 0 ## self.iterateNodes = [] # list to store iterate nodes in substrees ## # with roots self.roots ## def tagNodes(self, node, iterateSubTree): ## """tag all nodes in subtree. mtstate will correspond to the number ## of times a given node was seen in the set of all sub-trees, ## iterateSubTree is used to tag all nodes below an iterate node""" ## ## USED TO FLASH NETWORK ONCE ONLY ## ## if node.mtstate==0: ## ## self.allNodes.append(node) ## ## # if we flash the network turn node red ## ## if node.editor.flashNetworkWhenRun: ## ## lock = node.editor.RunNodeLock ## ## lock.acquire() ## ## node._tmp_prevCol = node.setColor('red') ## ## lock.release() ## node.mtstate = node.mtstate + 1 ## # if the node has been seen before (from another root) do not ## # overwrite the fact that it is a child of an iterate node (if it is) ## if hasattr(node, '_tmp_childOfIterate'): ## if node._tmp_childOfIterate < iterateSubTree: ## node._tmp_childOfIterate = iterateSubTree ## else: ## node._tmp_childOfIterate = iterateSubTree ## #print 'TAG', node.name, node._tmp_childOfIterate ## # has to be imported here because of cross imports with ## # items and standardNodes ## from StandardNodes import Iterate ## if isinstance(node, Iterate): ## self.iterateNodes.append(node) ## node._tmp_isIterateNode = 1 ## iterateSubTree = 1 ## #print 'TAG', node.name, node.mtstate ## if hasattr(node, 'temp_uniqueFloodIndex'): ## if node.temp_uniqueFloodIndex == self.uniqueFloodIndex: ## return ## node.temp_uniqueFloodIndex = self.uniqueFloodIndex ## for child in node.children: ## self.tagNodes(child, iterateSubTree) ## def startChildren(self, node): ## """recursively start threaded execution of all non-root nodes""" ## # FIXME: we should builtin a limit number of threads to be started ## # at the same time ## # children of iterate node need not be scheduled ## if hasattr(node, '_tmp_isIterateNode'): ## return ## for child in node.children: ## # if node not yet associated with a thread object ## if child._tmp_childOfIterate==1: ## #print 'NOT starting node for _tmp_childOfIterate:', child.name ## continue ## if child.mtstate > 0: ## #print 'starting node:', child.name ## child.thread = NodeThread(child, self, root=0) ## self.threadObjects.append(child.thread) ## # force mtstate to -1 to prevent creating more thread object ## # threadobject's run method checks mtstate==-1 to decide if ## # it has to wait for a parent's completion ## child.mtstate = -1 ## #else: ## #print 'NOT starting node for mtstate:', child.name ## self.startChildren(child) ## def deleteTmp(self, node): ## del node.temp_uniqueFloodIndex ## if hasattr(node, '_tmp_childOfIterate'): ## del node._tmp_childOfIterate ## for child in node.children: ## self.deleteTmp(child) ## def run(self): ## """ schedule MT execution of all nodes in trees for a given list ## of root nodes""" ## if len(self.roots)==0: ## return ## if self.roots[0].editor.networkRunning==0: ## return ## self.roots[0].network.RunNetLock.acquire() ## self.mtstatelock.acquire() ## # list of ThreadObjects for all children (i.e. non root) nodes ## # node.mtstate is used to add each child node only once to the list ## # It is also used in the ThreadNode object.run() method ## # to decide if a child node has to wait for a given parent ## self.threadObjects = [] ## # used to prevent endless loop in tagging of nodes involved in ## # this run ## self.uniqueFloodIndex = 0 ## # first tag all nodes involved in this run using mtstate ## for root in self.roots: ## self.tagNodes(root, 0) ## self.uniqueFloodIndex = self.uniqueFloodIndex + 1 ## if root.mtstate > 1: # this root is also a child of another root ## #print 'REMOVING root', root.name ## self.roots.remove(root) # we remove from the list of roots ## else: ## # force mtstate to -1 so all children will wait once started ## root.mtstate = -1 ## # walk the subtrees and add a ThreadNode object to the list for ## # each child node and force mtstate to -1 on all of them ## for root in self.roots: ## self.startChildren(root) ## for node in self.iterateNodes: ## del node._tmp_isIterateNode ## for node in self.roots: ## self.deleteTmp(node) ## # At this point, all nodes involved have mtstate == -1 ## # and self.threadObjects contains a NodeThread for each child node ## # now start all children nodes ## # they will sit waiting to grab the mtstatelock and then wait for ## # their parent's condition ## for to in self.threadObjects: ## to.start() ## self.mtstatelock.release() ## # now execute all root nodes and trigger all the executions ## for root in self.roots: ## #print 'starting node:', root.name ## # root=1 means this node will not wait for parents to start ## # running ## root.thread = NodeThread(root, self, root=1) ## root.thread.start() ## #self.mtstatelock.release() ## # has to be imported here because of cross imports with ## # items and standardNodes ## from StandardNodes import Iterate ## # lock run lock until this run is completed ## self.waitLock.acquire() ## while 1: ## self.done = 1 ## # check if there is a thread started by an MTScheduler object ## # (they can be recognized because their name starts with ## # the '_&&_mtScheduler_&&_thread_' string). ## # if we find a thread with such a name which does not ## # correspond to an iterate node we have to wait. ## # else, all children started have run and we can release the ## # lock that was set for this run and give another MTscheduler ## # (for instance started by an iterate node) to grab it and start ## # running. ## # ## for thread in threading.enumerate(): ## # if this thread was started by an MTscheduler ## if thread.getName()[:26]=='_&&_mtScheduler_&&_thread_': ## # if it is not an iterate node and it is not ## # a child of an iterate node that is scheduled ## # we have to wait. ## if not isinstance(thread.node, Iterate): ## self.done = 0 ## break ## if self.done==1: ## net = self.roots[0].network ## net.release() ## net.iterateLock.acquire() ## net.iterateCond.notifyAll() # wakeup all iterate nodes ## net.iterateLock.release() ## break ## else: ## # self.waitCond is notified after completed of each thread ## # corresponding to a node. ## # We check every second but this wakes up every time a node ## # is done running anyways. We still need the timeout of ## # 1 second in case we miss a notifocation or an iterate node ## # is still an active thread but not done running. (not 100% ## # sure we need the timeout) ## self.waitCond.wait(1) ## ## USED TO FLASH NETWORK ONCE ONLY ## # if we flash the network only once go back to previous color ## ## if self.roots[0].editor.flashNetworkWhenRun: ## ## for node in self.allNodes: ## ## lock = node.editor.RunNodeLock ## ## lock.acquire() ## ## node._tmp_prevCol = node.setColor(node._tmp_prevCol) ## ## del node._tmp_prevCol ## ## lock.release() ## self.waitLock.release() ## class NodeThread(threading.Thread): ## def __init__(self, node, scheduler, root=0): ## threading.Thread.__init__(self, name='_&&_mtScheduler_&&_thread_'+node.name)#, verbose=1) ## self.node = node ## self.scheduler = scheduler ## self.root = root ## def run(self): ## node = self.node ## #self._note('running %s', node.name) ## # acquire global lock (only one node can run at any time) ## lock = node.network.RunNodeLock ## lock.acquire() ## #print 'start running', node.name ## # has to be imported here because of cross imports with ## # items and standardNodes ## from StandardNodes import Iterate ## try: ## # make sure all parents are done ## if self.root==0: ## for parentNode in node.parents: ## if isinstance(parentNode, Iterate): ## continue ## self.scheduler.mtstatelock.acquire() ## if parentNode.mtstate == -1: ## self.scheduler.mtstatelock.release() ## parentNode.condition.wait() ## #print node.name, 'wake up sent by', parentNode.name ## else: ## self.scheduler.mtstatelock.release() ## if node.editor.flashNodesWhenRun: ## col = node.setColor('red') ## #node.editor.update_idletasks() ## # once all the parents are done we can run this node ## #print 'running', node.name ## status = node.computeFunction() ## if node.editor.flashNodesWhenRun: ## if col=='#557700': # after successfull run of failed node ## col = node.colorBeforeFail ## node.setColor(col) ## except: ## print ## print "***********************************************************" ## print "*** ERROR while executing node: ", node.name ## print "***********************************************************" ## traceback.print_exc() ## if node.editor.flashNodesWhenRun: ## if col != '#557700': ## node.colorBeforeFail = col ## node.setColor('#557700') ## self.scheduler.mtstatelock.acquire() ## node.mtstate = 0 # done ## #print 'mtstate', node.name, node.mtstate ## self.scheduler.mtstatelock.release() ## # once this node has run we want to tell all threads of ## # children nodes ## #print 'notify from', node.name ## node.condition.notifyAll() ## #print 'Thread: acquireing waitLock by', node.name ## self.scheduler.waitLock.acquire() ## self.scheduler.waitCond.notify() ## #print 'Thread: releasinging waitLock by', node.name ## self.scheduler.waitLock.release() ## # finally release the lock ## #print 'release global lock', node.name ## del node.thread ## lock.release() ## class Queue(threading._Verbose): ## def __init__(self, limit): ## threading._Verbose.__init__(self) ## self.mon = threading.RLock() ## self.rc = threading.Condition(self.mon) ## self.wc = threading.Condition(self.mon) ## self.limit = limit ## self.queue = [] # list of nodes in trees except for root nodes ## self.roots = [] # list of nodes at root of trees ## def printQueue(self): ## self.mon.acquire() ## self._note('queue :************************') ## for entry in self.queue: ## self._note("%s", entry.name) ## self.mon.release() ## def put(self, item): ## self.mon.acquire() ## # we only add nodes to the queue if they are not queued or scheduled ## # if a node is running, it needs to be added tot he queue in order to ## # run again with the new input ## if item.mtstate is not 0: ## print '-------- already in queue or schedule', item.name ## self.mon.release() ## return ## while len(self.queue) >= self.limit: ## #self._note("put(%s): queue full", item) ## self.wc.wait() ## print 'queued ',item.name ## #import traceback ## #print traceback.print_stack() ## self.queue.append(item) ## item.mtstate = 3 # inQueue ## #self._note("put(%s): appended, length now %d", ## # item, len(self.queue)) ## self.rc.notify() ## self.mon.release() ## def get(self): ## self.mon.acquire() ## while not self.queue: ## #self._note("get(): queue empty") ## print '************************ queue empty' ## self.rc.wait() ## item = self.queue[0] ## print 'scheduled ',item.name ## item.mtstate = 2 # scheduled ## del self.queue[0] ## #self._note("get(): got %s, %d left", item, len(self.queue)) ## self.wc.notify() ## self.mon.release() ## return item ## # objects reading the queue ## class QueueHandler(threading.Thread): ## def __init__(self, queue, verbose=None): ## threading.Thread.__init__(self, name="handler", verbose=verbose) ## self.queue = queue ## self.finish = 1 ## def stop(self): ## self._Thread__started = 0 ## self.finish = 1 ## def run(self): ## self.finish = 0 ## while not self.finish: ## node = self.queue.get() ## if node.frozen: ## continue ## #if __debug__: ## # self._note('running %s', node.name) ## node.thread = NodeThread(node, self.queue) ## node.thread.start() #def _test(): # import time # _sleep = time.sleep # # NP = 3 # QL = 4 # NI = 5 # # Q = Queue(QL) # P = [] # C = ConsumerThread(Q, NI*NP) # C.start() # for i in range(NP): # t = ProducerThread(Q, NI) # t.setName("Producer-%d" % (i+1)) # P.append(t) # t.start() # _sleep(1.) # #for t in P: # # t.join() # #C.join() # #if __name__ == '__main__': # _test() # NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/gridEditor.py0000644000175000017500000004224010112745311023741 0ustar moellermoeller######################################################################### # # Date: Jan 2004 Author: Daniel Stoffler # # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Daniel Stoffler and TSRI # ######################################################################### import Tkinter, Pmw import warnings import types import string, types, weakref from mglutil.util.callback import CallBackFunction class OneGrid: """Basic object that builds a grid editor GUI and provides a get() and set() method""" def __init__(self, root=None, port=None, label=False, callback=None): # set root if root is None: self.root = self.panel = Tkinter.Toplevel() else: self.root = root self.callback = callback # callback function that is called when # is pressed in a given Entry # set weakref to port if port is not None: self.port = weakref.ref(port) else: self.port = None # set the flag self.label: self.label = label # if set to True, entries to specify label name and # side are created # now define the Tkinter variables needed to set/get values self.rowTk = Tkinter.IntVar() self.rowspanTk = Tkinter.IntVar() self.columnTk = Tkinter.IntVar() self.columnspanTk = Tkinter.IntVar() self.stickyTk = Tkinter.StringVar() self.labelNameTk = Tkinter.StringVar() self.labelSideTk = Tkinter.StringVar() self.padxTk = Tkinter.IntVar() self.padyTk = Tkinter.IntVar() self.ipadxTk = Tkinter.IntVar() self.ipadyTk = Tkinter.IntVar() # initialize with default values that are different from '0' self.rowspanTk.set(1) self.columnspanTk.set(1) self.stickyTk.set('w') # variable for checkutton: self.cbVar = Tkinter.IntVar() self.cbVar.set(0) # and finally, create the GUI self.createForm() def createForm(self): """this method builds the GUI""" mainFrame = Tkinter.Frame(self.root) mainFrame.pack(side='top', expand=1, fill='both') cb = Tkinter.Checkbutton(mainFrame, text='more options', command=self.toggleOpts_cb, var=self.cbVar) cb.pack() gridFrame = Tkinter.Frame(mainFrame, borderwidth=2, relief='sunken', padx=5, pady=5) gridFrame.pack(side='top', expand=1, fill='both') self.topGrid = Tkinter.Frame(gridFrame) self.topGrid.grid(row=0, column=0, sticky='wens') self.bottomGrid = Tkinter.Frame(gridFrame) self.topGrid.grid(row=1, column=0, sticky='wens') row = 0 ## Build the widgets which are visible by default (one line): topGrid if self.label: self.l1 = Tkinter.Label(self.topGrid, text='name:') self.l1.grid(row=row, column=0, sticky='w') self.b1 = Pmw.Balloon(self.topGrid) self.b1.bind(self.l1, "Change the name of the Label") self.e1 = Tkinter.Entry(self.topGrid, width=10, textvariable=self.labelNameTk) self.e1.grid(row=row, column=1, sticky='we') self.l2 = Tkinter.Label(self.topGrid, text='side:') self.l2.grid(row=row, column=2, sticky='w') self.b2 = Pmw.Balloon(self.topGrid) self.b2.bind(self.l2, "Change the side of the Label") self.e2 = Tkinter.Entry(self.topGrid, width=5, textvariable=self.labelSideTk) self.e2.grid(row=row, column=3, sticky='we') row += 1 else: self.l1 = Tkinter.Label(self.topGrid, text="row:") self.l1.grid(row=row, column=0, sticky='w') self.b1 = Pmw.Balloon(self.topGrid) self.b1.bind(self.l1, "Insert the widget at this row. Row numbers start with\n"+ "0. If omitted, defaults to the first empty row in the\n"+ "grid.") self.e1 = Tkinter.Entry(self.topGrid, width=3, textvariable=self.rowTk) self.e1.grid(row=row, column=1) self.l2 = Tkinter.Label(self.topGrid, text="column:") self.l2.grid(row=row, column=2, sticky='w') self.b2 = Pmw.Balloon(self.topGrid) self.b2.bind(self.l2, "Insert the widget at this column. Column numbers\n"+ "start with 0. If omitted, defaults to 0.") self.e2 = Tkinter.Entry(self.topGrid, width=3, textvariable=self.columnTk) self.e2.grid(row=row, column=3) ## now build the widgets which are hidden by default: bottomGrid row += 1 self.l3 = Tkinter.Label(self.bottomGrid, text="rowspan:") self.l3.grid(row=row, column=0, sticky='w') self.b3 = Pmw.Balloon(self.bottomGrid) self.b3.bind(self.l3, "If given, indicates that the widget cell should span\n"+ "more than one row.") self.e3 = Tkinter.Entry(self.bottomGrid, width=3, textvariable=self.rowspanTk) self.e3.grid(row=row, column=1) self.l4 = Tkinter.Label(self.bottomGrid, text="columnspan:") self.l4.grid(row=row, column=2, sticky='w') self.b4 = Pmw.Balloon(self.bottomGrid) self.b4.bind(self.l4, "If given, indicates that the widget cell should span\n"+ "more than one column.") self.e4 = Tkinter.Entry( self.bottomGrid, width=3, textvariable=self.columnspanTk) self.e4.grid(row=row, column=3) row += 1 self.l5 = Tkinter.Label(self.bottomGrid, text='sticky:') self.l5.grid(row=row, column=0, sticky='w') self.b5 = Pmw.Balloon(self.bottomGrid) self.b5.bind( self.l5, "Defines how to expand the widget if the resulting cell\n"+ "is larger than the widget itself. This can be any\n"+ "combination of the constants S, N, E, and W, or NW, NE,\n"+ "SW, and SE. For example, W (west) means that the\n"+ "widget should be aligned to the left cell border. W+E\n"+ "means that the widget should be stretched horizontally\n"+ "to fill the whole cell. W+E+N+S means that the widget\n"+ "should be expanded in both directions. Default is to\n"+ "center the widget in the cell.") self.e5 = Tkinter.Entry(self.bottomGrid, width=3, textvariable=self.stickyTk) self.e5.grid(row=row, column=1, sticky='w') row += 1 self.l6 = Tkinter.Label(self.bottomGrid, text="padx:") self.l6.grid(row=row, column=0, sticky='w') self.b6 = Pmw.Balloon(self.bottomGrid) self.b6.bind(self.l6, "Optional padding to place around the widget in a cell.\n"+ "Default is 0.") self.e6 = Tkinter.Entry(self.bottomGrid, width=3, textvariable=self.padxTk) self.e6.grid(row=row, column=1) self.l7 = Tkinter.Label(self.bottomGrid, text="pady:") self.l7.grid(row=row, column=2, sticky='w') self.b7 = Pmw.Balloon(self.bottomGrid) self.b7.bind(self.l7, "Optional padding to place around the widget in a cell.\n"+ "Default is 0.") self.e7 = Tkinter.Entry(self.bottomGrid, width=3, textvariable=self.padyTk) self.e7.grid(row=row, column=3) row += 1 self.l8 = Tkinter.Label(self.bottomGrid, text="ipadx:") self.l8.grid(row=row, column=0, sticky='w') self.b8 = Pmw.Balloon(self.bottomGrid) self.b8.bind(self.l8, "Optional internal padding. Works like padx and\n"+ "pady, but the padding is added inside the widget\n"+ "borders. Default is 0.") self.e8 = Tkinter.Entry(self.bottomGrid, width=3, textvariable=self.ipadxTk) self.e8.grid(row=row, column=1) self.l9 = Tkinter.Label(self.bottomGrid, text="ipady:") self.l9.grid(row=row, column=2, sticky='w') self.b9 = Pmw.Balloon(self.bottomGrid) self.b9.bind(self.l9, "Optional internal padding. Works like padx and\n"+ "pady, but the padding is added inside the widget\n"+ "borders. Default is 0.") self.e9 = Tkinter.Entry(self.bottomGrid, width=3, textvariable=self.ipadyTk) self.e9.grid(row=row, column=3) # bind callback for pressing in Entry widget self.e1.bind('', self.callCallback) self.e2.bind('', self.callCallback) self.e3.bind('', self.callCallback) self.e4.bind('', self.callCallback) self.e5.bind('', self.callCallback) self.e6.bind('', self.callCallback) self.e7.bind('', self.callCallback) self.e8.bind('', self.callCallback) self.e9.bind('', self.callCallback) # by default, we do not show all options self.removeOptions() def set(self, gridcfg): """inputs a dict describing a Tkinter grid configuration""" keysLabel = ['labelName', 'labelSide', 'rowspan', 'column', 'columnspan', 'sticky', 'padx', 'pady', 'ipadx', 'ipady'] keysWidget = ['row', 'rowspan', 'column', 'columnspan', 'sticky', 'padx', 'pady', 'ipadx', 'ipady'] if self.label: keys = keysLabel else: keys = keysWidget for k,v in gridcfg.items(): if k not in keys: continue attr = eval('self.'+k+'Tk') if attr == self.stickyTk: attr.set(string.lower(str(v))) else: attr.set(str(v)) def get(self): """returns a dict describing a Tkinter grid configuration""" cfg = {} keysLabel = ['labelName', 'labelSide', 'rowspan', 'column', 'columnspan', 'sticky', 'padx', 'pady', 'ipadx', 'ipady'] keysWidget = ['row', 'rowspan', 'column', 'columnspan', 'sticky', 'padx', 'pady', 'ipadx', 'ipady'] if self.label: keys = keysLabel else: keys = keysWidget for k in keys: attr = eval('self.'+k+'Tk') val = attr.get() if val is not None and val != '': if k == 'sticky': val = string.lower(val) if len(val)>4: val = 'w' for i in range(len(val)): if val[i] not in ['w', 'e', 'n', 's']: val = 'w' break elif k == 'rowspan': if val < 1: val = 1 elif k == 'columnspan': if val < 1: val = 1 elif k == 'labelSide': if val not in ['left', 'right', 'top', 'bottom']: val = 'left' elif k in ['row', 'column', 'padx', 'pady', 'ipadx', 'ipady']: if val < 0: val = 0 attr.set(val) cfg[k] = val return cfg def toggleOpts_cb(self, event=None): """toggle between displaying more or fewer options""" if self.cbVar.get() == 0: self.removeOptions() else: self.addOptions() def removeOptions(self): """remove certain entries from GUI""" self.bottomGrid.grid_remove() def addOptions(self): """add entries back to GUI""" self.bottomGrid.grid() def callCallback(self, event=None): if self.callback is not None and callable(self.callback): self.callback() class GridEditor: def __init__(self, node=None): if node is not None: self.node = weakref.ref(node) # weak reference to Vision node else: self.node = None self.top = Tkinter.Toplevel() # root/top of this GUI self.top.title("Widget Grid Editor") self.grids = [] # storing all the grid objects self.gridCounter = 0 # used to set callback for in each grid # entry widget to call ApplyToOneGrid() instead # calling Apply_cb() which would rebuild all # widgets. # create two frames, one to hold all the grid GUIs, one for the buttons self.gridFrame = Tkinter.Frame(self.top) self.gridFrame.pack(side='top', expand=1, fill='both') self.buttonFrame = Tkinter.Frame(self.top) self.buttonFrame.pack(side='bottom', expand=1, fill='both') # and now build the form self.buildForm() self.top.protocol("WM_DELETE_WINDOW", self.Cancel_cb) def buildForm(self): if self.node is None: return row = 0 column = 0 l1 = Tkinter.Label(#group.interior(), self.gridFrame, text='Label Conf') l2 = Tkinter.Label(#group.interior(), self.gridFrame, text='Widget Conf') l1.grid(row=0, column=0) l2.grid(row=0, column=1) row = row + 1 for i in range(len(self.node().inputPorts)): port = self.node().inputPorts[i] if port.widget is None: continue widgetDescr = port.widget.getDescr() name = port.name group = Pmw.Group( self.gridFrame, tag_text='Widget '+\ #widgetDescr['class']+\ ' '+name) group.grid(row=row, column=column, columnspan=2, padx=6, pady=6, sticky='wens') f1 = Tkinter.Frame(group.interior()) f2 = Tkinter.Frame(group.interior()) cb = CallBackFunction(self.ApplyToOneGrid, self.gridCounter) g1 = OneGrid(f1, port=port, label=True, callback=cb) g2 = OneGrid(f2, port=port, label=False, callback=cb) self.gridCounter += 1 # append to self.grids self.grids.append((g1, g2)) # get current grid_info and set gui accordingly # we do not use widgetDescr, because this might not be up do date! g1.set(port.widget.tklabel.grid_info()) g1.set({'labelName':widgetDescr['labelCfg']['text'], 'labelSide':widgetDescr['widgetGridCfg']['labelSide']}) g2.set(port.widget.widgetFrame.grid_info()) #l1.grid(row=0, column=0) f1.grid(row=2, column=0, padx=6, pady=6) #l2.grid(row=0, column=1) f2.grid(row=2, column=1, padx=6, pady=6) row = row + 1 # add buttons ok = Tkinter.Button(self.buttonFrame, text='Ok', command=self.OK_cb) ok.pack(side='left', expand=1, fill='x') apply = Tkinter.Button(self.buttonFrame, text='Apply', command=self.Apply_cb) apply.pack(side='left', expand=1, fill='x') cancel = Tkinter.Button(self.buttonFrame, text='Cancel', command=self.Cancel_cb) cancel.pack(side='left', expand=1, fill='x') def OK_cb(self, event=None): self.Apply_cb() self.Cancel_cb() def ApplyToOneGrid(self, number, autoResize=True): """Apply to a specified grid pair""" lgrid, wgrid = self.grids[number] # get the pairs of label&widget grids ldescr = lgrid.get() wdescr = wgrid.get() if lgrid.port is None or wgrid.port is None: return widget = wgrid.port().widget node = widget.port.node labName = ldescr.pop('labelName','') labSide = ldescr.pop('labelSide', 'left') wdescr['labelSide'] = labSide applyDict = dict(labelCfg=dict(text=labName), labelGridCfg=ldescr) applyDict['widgetGridCfg%s'%widget.master] = wdescr apply(widget.configure, (),applyDict) if autoResize: self.node().autoResize() def Apply_cb(self, event=None): for i in range(len(self.grids)): self.ApplyToOneGrid(i, autoResize=False) self.node().autoResize() def Cancel_cb(self, event=None): if self.node is not None and self.node().objEditor is not None: ed = self.node().objEditor ed.editGridVarTk.set(0) ed.nbEditorWindows -= 1 ed.manageEditorButtons() self.top.destroy() NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/itemBase.py0000644000175000017500000007461312141050326023406 0ustar moellermoeller######################################################################### # # Date: Nov. 2001 Author: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner, Daniel Stoffler and TSRI # ######################################################################### # # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/itemBase.py,v 1.113 2013/05/04 00:05:42 sanner Exp $ # # $Id: itemBase.py,v 1.113 2013/05/04 00:05:42 sanner Exp $ # import sys, os import types import Tkinter import threading import traceback import warnings import weakref import tkMessageBox import Pmw from mglutil.gui.BasicWidgets.Tk.TreeWidget.objectBrowser import ObjectBrowser from mglutil.util.callback import CallbackManager, CallBackFunction class UserPanel: """Tk frame onto which a user can place selected widgets""" def __init__(self, name, master=None, network=None, width=120, height=90): #print "UserPanel.__init__" editor = network.getEditor() self.editor = editor self.network = network self.master = master self.widgets = [] # list of widget placed onto this panel if not editor.hasGUI: return if master is None: self.master = Tkinter.Toplevel() self.master.title(name) self.master.protocol('WM_DELETE_WINDOW', self.withdraw_cb) ## build menu if master is None: buttonsFrame = Tkinter.Frame(self.master) self.buttonRun = Tkinter.Button(buttonsFrame, text='Run / Stop', command=self.editor.toggleNetworkRun_cb) self.buttonRun.pack(side='left', expand=1, fill='x') if hasattr(network, 'isApplication') is True: self.buttonQuit = Tkinter.Button(buttonsFrame, text='Quit', command=self.editor.stopNetworkAndQuit_cb) self.buttonQuit.pack(side='left', expand=1, fill='x') else: self.buttonShowHideNetwork = Tkinter.Button(buttonsFrame, text='Show / Hide Vision', command=self.editor.toggleGuiVisibility) self.buttonShowHideNetwork.pack(side='left', expand=1, fill='x') buttonsFrame.pack(side='top', fill='x') ## build frame for widgets self.frame = Tkinter.Frame( self.master, relief='sunken', bd=3, bg='#c3d0a6') self.frame.configure(width=width, height=height) self.frame.pack(side='top', expand=1, fill='both') def withdraw_cb(self, event=None): """ """ #print "withdraw_cb" if hasattr(self.network, 'isApplication') is True: if len(self.network.userPanels) == 1: self.master.quit() else: self.master.iconify() else: self.master.iconify() self.editor.master.master.master.deiconify() def placeWidgetAndLabel(self, widget, relx, rely): #print "UserPanel.placeWidgetAndLabel", widget.widgetFrame.winfo_reqwidth() labelSide = widget.widgetGridCfg['labelSide'] if labelSide is None: labelSide = 'left' widget.widgetFrame.place(relx=relx, rely=rely, anchor=Tkinter.CENTER) if labelSide == 'left': labelx = .5 * widget.widgetFrame.winfo_width() \ / widget.widgetFrame.master.winfo_width() widget.tklabel.place(relx=relx-labelx, rely=rely, anchor='e') elif labelSide == 'right': labelx = .5 * widget.widgetFrame.winfo_width() \ / widget.widgetFrame.master.winfo_width() widget.tklabel.place(relx=relx+labelx, rely=rely, anchor='w') elif labelSide == 'top': labely = .5 * widget.widgetFrame.winfo_height() \ / widget.widgetFrame.master.winfo_height() widget.tklabel.place(relx=relx, rely=rely-labely, anchor='s') elif labelSide == 'bottom': labely = .5 * widget.widgetFrame.winfo_height() \ / widget.widgetFrame.master.winfo_height() widget.tklabel.place(relx=relx, rely=rely+labely, anchor='n') else: warnings.warn('Wrong side for label') def rePlaceWidget(self, widget, labelSide=None): #print "UserPanel.rePlaceWidget" relx = float( widget.widgetFrame.winfo_x() \ + .5*widget.widgetFrame.winfo_width() ) \ / widget.widgetFrame.master.winfo_width() rely = float(widget.widgetFrame.winfo_y() \ + .5*widget.widgetFrame.winfo_height() ) \ / widget.widgetFrame.master.winfo_height() self.placeWidgetAndLabel(widget, relx, rely) def motionWidget_cb(self, widget, event=None): #print "UserPanel.motionWidget_cb" relx = float(event.x_root - widget.widgetFrame.master.winfo_rootx()) \ /widget.widgetFrame.master.winfo_width() rely = float(event.y_root - widget.widgetFrame.master.winfo_rooty()) \ /widget.widgetFrame.master.winfo_height() self.placeWidgetAndLabel(widget, relx, rely) def postWidgetMenu(self, widget, event=None): #print widget widget.menu.post(event.x_root, event.y_root) def addWidget(self, widget, widgetPlacerCfg=None): #print "UserPanel.addWidget" self.widgets.append(widget) if hasattr(self.network, 'isApplication') is True: widget.menu.delete(1,'end') cbpost = CallBackFunction(self.postWidgetMenu, widget) widget.tklabel.bind("", cbpost) if hasattr(widget.widget, 'canvas'): widget.widget.canvas.bind("", cbpost) else: widget.widget.bind("", cbpost) cb = CallBackFunction(self.motionWidget_cb, widget) widget.tklabel.bind("", cb) if hasattr(widget.widget, 'canvas'): widget.widget.canvas.bind("", cb) else: widget.widget.bind("", cb) if widgetPlacerCfg is not None: relx = eval(widgetPlacerCfg['relx']) rely = eval(widgetPlacerCfg['rely']) else: relx = .5 rely = .5 self.placeWidgetAndLabel(widget, relx, rely) # we need this otherwise the widget.widgetFrame.winfo_width # is not set correctly the first time (so the label is hidden behind the widget) widget.port.node.network.canvas.update_idletasks() self.placeWidgetAndLabel(widget, relx, rely) def deleteWidget(self, widget): #print "UserPanel.deleteWidget" if widget in self.widgets: self.widgets.remove(widget) widget.tklabel.unbind("") if hasattr(widget.widget, 'canvas'): widget.widget.canvas.unbind("") #widget.widget.valueLabel.bind("", self.moveWidget_cb) else: widget.widget.unbind("") class ParamPanel: """This class provides show / hide for all parameter panels """ def __init__(self, node, master=None, title=None, applyFun=None, dismissFun=None): self.node = weakref.ref(node) self.visible = 0 # visible=1 means panel is displayed self.applyFun = applyFun self.dismissFun = dismissFun if self.dismissFun is None: self.dismissFun = self.hide if master is None: master=Tkinter.Toplevel() self.master = master if title: self.master.title(title) self.master.withdraw() self.master.protocol('WM_DELETE_WINDOW', self.dismissFun ) else: self.master = master # the mainFrame holds the widgetFrame plus some buttons self.mainFrame = Tkinter.Frame(self.master) self.mainFrame.pack(fill='both',expand=1) # add menu entries self.createMenus(self.mainFrame) # the widgetFrame holds the widgets bound to this panel self.widgetFrame = Tkinter.Frame(self.mainFrame, borderwidth=3, bg='#c3d0a6', relief='ridge') self.widgetFrame.pack(fill='both', expand=1) self.applyButton = Tkinter.Button(self.mainFrame, text="Run node", command=self.forceRun) Pmw.Balloon(self.mainFrame).bind(self.applyButton,"Run node and subnetwork") #tooltip self.applyButton.pack(side='left', fill='x', expand=1) #self.dismissButton = Tkinter.Button(self.mainFrame, text="Dismiss", # command=self.dismissFun) #Pmw.Balloon(self.mainFrame).bind(self.dismissButton,"keep visible values") #tooltip #self.dismissButton.pack(side='left') def createMenus(self, parent): self.mBar = Tkinter.Frame(parent, relief=Tkinter.RAISED, borderwidth=2) self.mBar.pack(side='top',fill='x') self.menuButtons = {} self.makeOptionsMenu(self.mBar) apply( self.mBar.tk_menuBar, self.menuButtons.values() ) def makeOptionsMenu(self, parent): Options_button = Tkinter.Menubutton(parent, text='Options', underline=0) self.menuButtons['Options'] = Options_button Options_button.pack(side=Tkinter.LEFT, padx="1m") # got rid of useless tearoff Options_button.menu = Tkinter.Menu(Options_button, tearoff=False) self.immediateTk = Tkinter.IntVar() self.immediateTk.set(1) Options_button.menu.add_checkbutton(label='Immediate', variable=self.immediateTk, command=self.setImmediate) Options_button.menu.add_separator() Options_button.menu.add_command(label='Dismiss', underline=0, command=self.dismissFun) Options_button['menu'] = Options_button.menu def setImmediate(self, event=None, immediate=None, tagModified=True): if tagModified is True: self.node()._setModified(True) if immediate is not None: assert immediate in [0,1,True,False] self.immediateTk.set(immediate) def show(self): """ show paramter panel """ if self.master.winfo_ismapped() == 0: self.master.deiconify() self.master.lift() # we make sure these are on self.node().paramPanelTk.set(1) self.visible = 1 def hide(self, event=None): if self.master.winfo_ismapped() == 1: self.master.withdraw() # we make sure these are off self.node().paramPanelTk.set(0) self.visible = 0 def toggleVisibility(self, event=None): if self.master.winfo_ismapped() == 1: self.hide() else: self.show() def destroy(self, event=None): if isinstance(self.master, Tkinter.Toplevel): self.master.destroy() else: self.mainFrame.destroy() def run(self, event=None): # only run if immediate==1. This method is called by the widget's set # method if self.immediateTk.get(): if self.applyFun is not None: self.applyFun() def forceRun(self, event=None): # always call the apply fun. This method is called by the 'apply' # button at the bottom of this param panel if self.applyFun is not None: self.applyFun() class NetworkItemsBase(object): """Base class for all objects that can be added to a Canvas.""" # the following 3 methods are used to maintain backwards compatibility # because we changed certain attributes to weakref def getEditor(self): if self.vEditor is None: return None return self.vEditor() def setEditor(self, editor): self.vEditor = weakref.ref(editor) def delEditor(self): del self.vEditor # "self.editor" has to be created here. because we use the property mechan. editor = property(getEditor, setEditor, delEditor, "Used for backwards compatibility because we changed this to a weakref.") def __init__(self, name): self.name = name # item's name # for backwards-compatibility we created the following attribute: # vEditor will be a weakref to editor and only a network will keep # the attribute ".editor" in order to be able to load old networks. # newer saved networks will use the method getEditor() to get a handle # to the editor self.vEditor = None # comment: see above self._modified = False # if the item is instanciated, or if certain # attributes are changed, for example, # through configure(), this will be set 'True' self._original = True # used for saving networks: if set to False # indicates this object was created by the # user (such as adding a node to a network # or adding a port to a node using the node # editor def _setModified(self, modified): """set the _modified attribute. Users should not call this method""" assert modified in [True, False] self._modified = modified def _setOriginal(self, original): """set the _original attribute. Users should not call this method""" assert original in [True, False] self._original = original class NetworkItems(NetworkItemsBase): """Base class for objects that can be added to a Canvas. Network items sublassing this have to extend the buildIcons method to create a Canvas geometry representing this item. """ def __init__(self, name='NoName'): NetworkItemsBase.__init__(self, name) self.paramPanel = None self.iconMaster = None # canvas on which this item is placed self.id = None # Tkinter.Canvas primitive ID that will correspond # to this node self.iconTag = None# unique tag shared by all primitives used to # draw this node's icon self.naviMapID = None # editor and network are set in the addNode method of Network self.network = None # NetworkObject to which this node belongs self.objEditor = None self.dynamicComputeFunction = None self.selected = 0 self.nodeScaleFactor = 1.0 self.distScaleFactor = 1.0 self.posx = 0 self.posy = 0 self.menu = None self.highlightOptions = {'highlightbackground':'red'} self.unhighlightOptions = {'highlightbackground':'gray50'} self.selectOptions = {'background':'yellow'} self.deselectOptions = {'background':'gray85'} self.objectBrowser = None # introspect object window self.editFuncWindowWidth = 500 # width and height of source code editor self.editFuncWindowHeight = 200 self.netSwitchOriginal = None self.frozen = False # set to True to prevent node from running self.isSchedulingNode = False # if set to 1, this node and its children # will not be scheduled. Example: the # Iterate Node in Vision.StandardNodes # lock that needs to be acquired before node can be modified # FIXME: we have to update all methods operating on nodes to make # them thread safe self.runLock = threading.RLock() # table of functions to call for specific mouse events on canvas self.mouseButtonFlag = 0 self.mouseActionEvents = [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ] self.mouseAction = {} for event in self.mouseActionEvents: self.mouseAction[event] = None self.mouseAction[''] = self.postItemMenu def toggleFrozen_cb(self, event=None): if self.frozen: self.unfreeze() else: self.freeze() self.frozen = not self.frozen self.frozenTk.set(self.frozen) self._setModified(True) def freeze(self): # Note: whether color by library is on or off, we always need to color # the node back to the frozen color after deselecting this node col = '#b6d3f6' # light blue color self.unfreezeCol = self.setColor(col) self.deselectOptions["fill"] = col def unfreeze(self): # here we have to be more careful and check which mode is currently # on: color node by library 0/1. Depending on the status we need # to set a different deselect color #self.setColor(self.unfreezeCol) if self.editor.colorNodeByLibraryTk.get() == 1: if self.library is not None: col = self.library.color else: col = "gray85" else: col = "gray85" self.deselectOptions['fill'] = col self.setColor(col) #self.schedule() def run(self, force=0, debug=False): """run this node. if force is set, the node will run regardless of the availability of new data. return 0 if run failed. (Thread safe)""" #print "NetworkItems.run", self.name, force, self.forceExecution, self.network.forceExecution if force or self.forceExecution: self.forceExecution = 1 elif self.frozen: return # acquire the node's lock before doing anything to it #print " noderun acquire runLock" ed = self.getEditor() self.runLock.acquire() ## import traceback ## print '******************', self.name ## traceback.print_stack() try: ## FIXME ... this is necessary here but is also done in self.computeFunction() # if forceExecute is set, we run the node regardless of newdata # else we test that at least one parent present new data before # running the node from NetworkEditor.macros import MacroNode, MacroInputNode, MacroOutputNode if self.forceExecution == 0 \ and self.network.forceExecution == 0 \ and isinstance(self, MacroInputNode) is False \ and isinstance(self, MacroOutputNode) is False \ and len(self.inputPorts) > 0: newdata = 0 for p in self.inputPorts: # here we don't check if the data are valid # because they are not on the input port yet if p.hasNewData(): newdata = 1 break if newdata == 0: #print 'No new data for', self.name self.runLock.release() return 1 self.forceExecution = 1 if isinstance(self, MacroNode) and not self.hasRun: self.macroNetwork.forceExecution = 1 if isinstance(self, MacroInputNode) is False: for lOutputPort in self.outputPorts: lOutputPort.outputData('no data yet') if self.isSchedulingNode: self.network.setChildrenOutputToNoDataYet(self) if ed.hasGUI and ed.flashNodesWhenRun: c = self.iconMaster if c is not None: c.tk.call((c._w, 'itemconfigure', self.innerBox, '-outline', '#FF0000', '-width', 4)) col = '#000000' if (os.name != 'nt') \ or not ed.withThreads: ed.update_idletasks() if not self.editor.hasGUI: # detached process if hasattr(self.network, 'communicator'): # for macro networks self.network.communicator.handleCommunications() ## connid = net.socket.fileno() ## ifd, ofd, efd = select([connid], [], [], 0) ## if connid in ifd: ## conn, addr = self.network.socket.accept() ## net.socketConnections.append(conn) ## print 'Connected by', addr ## inids = map( lambda x: x.fileno(), net.socketConnections ) ## ifd, ofd, efd = select(inids, [], [], 0) ## for conn in net.socketConnections: ## if conn.fileno() in ifd: ## input = conn.recv(4096) #os.read(_pid, 16384) ## if len(input)==0: # client diconnected ## net.connections.remove(conn) ## else: ## if debug: ## procid = os.getpid() ## print "process %d input"%procid, repr(input) ## mod = __import__('__main__') ## exec(input, mod.__dict__) if debug: print 'in run', self.name, debug net = self.network net.debugCmd = None # FIXME .. not sure this is the proper way to make a variable # known in the python shell d = sys.modules['__main__'].__dict__ d['node'] = self debugCmd = net.waitForDebugCommand() stat = self.computeFunction() if ed.hasGUI and ed.flashNodesWhenRun and stat!='Stop': if col=='#557700': # after successfull run of failed node col = self.colorBeforeFail #self.dirty = 1 #else: #self.dirty = 0 if c is not None: c.tk.call((c._w, 'itemconfigure', self.innerBox, '-outline', col, '-width', 1)) if self.network.execStatus != 'stop': self.forceExecution = 0 self.runLock.release() if stat == 'Go': # force execution of children nodes of trigger output port if len(self.specialOutputPorts) \ and len(self.specialOutputPorts[0].children): self.scheduleChildren(portList=[self.specialOutputPorts[0]]) if stat is None: return 1 else: return stat except KeyboardInterrupt, e: raise(KeyboardInterrupt, e) except: print print "***********************************************************" print "*** ERROR while executing node: ", self.name print "***********************************************************" self.forceExecution = 0 traceback.print_exc() if ed.hasGUI and ed.flashNodesWhenRun: if col != '#557700': self.colorBeforeFail = col c.tk.call((c._w, 'itemconfigure', self.innerBox, '-outline', '#557700')) #self.setColor('#557700') self.runLock.release() return 'stop' def postItemMenu(self, event): if self.network.postedMenu: self.network.postedMenu.unpost() self.network.postedMenu = None elif self.menu: self.menu.post(event.x_root, event.y_root) self.network.postedMenu = self.menu def editNodeMenu(self, type, *args, **kw): apply( eval("self.menu."+type), args, kw ) def __repr__(self): return '<%s %s>'%(self.__class__.__name__, self.name) def rename(self, name, tagModified=True): self.name=name if tagModified is True: self._setModified(True) def beforeAddingToNetwork(self, network): # get's called when a node is instanciated in a network #print 'beforeAddingToNetwork:', self.name pass def afterAddingToNetwork(self): # get's called when a node is instanciated in a network #print 'afterAddingToNetwork:', self.name pass def beforeRemovingFromNetwork(self): # get's called when a node is deleted from a network #print 'beforeRemovingFromNetwork:', self.name pass def afterRemovingFromNetwork(self): # get's called when a node is deleted from a network #print 'afterRemovingFromNetwork:', self.name pass def highlight(self, event=None): pass def unhighlight(self, event=None): pass def select(self): self.selected = 1 if self.iconMaster: self.iconMaster.addtag_withtag('selected', self.iconTag) def deselect(self): self.selected = 0 if self.iconMaster: self.iconMaster.dtag(self.iconTag, 'selected') def deleteIcon(self): # delete what the item has to delete ed = self.getEditor() if self.iconMaster: self.iconMaster.delete(self.iconTag) self.iconTag = None # Note: I am not sure if we have to destroy the menu at all # to avoid memory leaks). But if we do destroy, we have to set # postedMenu to None if menu == postedMenu if ed.currentNetwork is None: return if self.menu: self.menu.unpost() self.network.postedMenu = None ## MS when the code below is enabled, after deleteing a connection the mouse ## button event are missing the Shift key in event. ## self.menu.destroy() self.menu = None def gettags(self): if self.iconMaster and self.iconTag: return self.iconMaster.gettags(self.iconTag) def buildIcons(self, canvas): """method to be implmented by subclass this method should create the geometric primitives for this item and tag them all with self.iconTag define its uniqueTag set its tags """ ed = self.getEditor() self.frozenTk = Tkinter.IntVar() self.frozenTk.set(self.frozen) from items import ImageNode if issubclass(self.__class__, ImageNode): master = ed.libTree.paramPanelMaster else: master = None self.paramPanel = ParamPanel(self, master=master, title=self.name, applyFun=self.schedule_cb, dismissFun=self.hideParamPanel) self.paramPanel.mainFrame.forget() if self.menu is None: # got rid of useless tearoff self.menu = Tkinter.Menu(ed, title=self.name, tearoff=False) self.menu.add_separator() if ed.hasGUI: self.menu.bind("", ed.myButtonPress, '+') self.iconMaster = canvas def hideParamPanel(self, event=None): self.paramPanel.hide() def schedule_cb(self): # virtual method, has to be implemented by subclass pass def introspect(self): if self.objectBrowser is None: self.objectBrowser = ObjectBrowser( self, rootName=self.name, title='Introspect Node: %s'%self.name, refresh=self._introspectCB) else: self.objectBrowser.show() def _introspectCB(self): """helper method used for the introspect data GUI""" return self def deletePort(self, p, resize=True): """Delete a port from a node. Removes it from the list of ports, updates the portsDescr list and return the descriptin of the deleted port, renumber all ports after this ports and mark them modified. Update code. """ # NOTE: this method moved here because we want to subclass it in # NetworkNode and in MacroBase net = self.network # save configuration of port about to be deleted #cfg = p.configure() from ports import InputPort, OutputPort, SpecialInputPort, \ SpecialOutputPort # delete port Icon if p.visible: p.deleteIcon() # remove port from list ports = None if isinstance(p, SpecialInputPort): self.specialInputPorts.remove(p) elif isinstance(p, SpecialOutputPort): self.specialOutputPorts.remove(p) elif isinstance(p, InputPort): ports = self.inputPorts if p.widget and self.getEditor() is not None: p.deleteWidget() p.deleteIcon() self.inputPorts.remove(p) del self.inputPortsDescr[p.number] elif isinstance(p, OutputPort): ports = self.outputPorts self.outputPorts.remove(p) del self.outputPortsDescr[p.number] else: print "FAILED. Port is neither of class InputPort or OuputPort!" return # remove all connections from this port self.network.deleteConnections(p.connections, 0) if self.getEditor() is not None: p.relposx = p.computePortPosX() # renumber the ports if ports: for i in range(p.number, len(ports)): port = ports[i] port.number = i if port.visible: port.updateIconPosition() # delete circular referneces del p.data del p.node del p.network del p.vEditor del p if resize and self.getEditor() is not None: self.autoResizeX() NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/items.py0000644000175000017500000064520012315360544023003 0ustar moellermoeller# Automatically adapted for numpy.oldnumeric Jul 23, 2007 by ######################################################################## # # Date: Nov. 2001 Author: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner, Daniel Stoffler and TSRI # # revision: Guillaume Vareille # ######################################################################### # # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/items.py,v 1.441 2014/03/28 20:53:56 sanner Exp $ # # $Id: items.py,v 1.441 2014/03/28 20:53:56 sanner Exp $ # import warnings, re, sys, os, user, inspect, copy, cmath, math, types, weakref import copy, random import string, threading, traceback import Tkinter, Pmw, tkFileDialog, Image, ImageTk import numpy.oldnumeric as Numeric import numpy from tkSimpleDialog import askstring from mglutil.util.packageFilePath import getResourceFolderWithVersion from NetworkEditor.ports import InputPort, OutputPort, RunNodeInputPort, \ TriggerOutputPort, SpecialOutputPort from mglutil.util.callback import CallBackFunction from mglutil.util.uniq import uniq from mglutil.util.misc import ensureFontCase from mglutil.util.misc import suppressMultipleQuotes from NetworkEditor.widgets import widgetsTable from NetworkEditor.Editor import NodeEditor from NetworkEditor.ports import InputPortsDescr, OutputPortsDescr, Port # FIXME .. dependency on Vision is bad here from mglutil.util.packageFilePath import findFilePath ICONPATH = findFilePath('Icons', 'Vision') # namespace note: node's compute function get compiled using their origin # module's __dict__ as global name space from itemBase import NetworkItems class NetworkNodeBase(NetworkItems): """Base class for a network editor Node """ def __init__(self, name='NoName', sourceCode=None, originalClass=None, constrkw=None, library=None, progbar=0, **kw): NetworkItems.__init__(self, name) self.objEditor = None # will be an instance of an Editor object self.network = None # VPE network self.originalClass = originalClass if originalClass is None: self.originalClass = self.__class__ if library is not None: assert library.modName is not None, "ERROR: node %s create with library %s that has no modname"%(name, library.name) assert library.varName is not None, "ERROR: node %s create with library %s that has no varName"%(name, library.name) self.library = library self.options = kw if constrkw is None: constrkw = {} self.constrkw = constrkw # dictionary of name:values to be added to # the call of the constructor when node # is instanciated from a saved network # used in getNodeSourceCode and # getNodesCreationSourceCode if not sourceCode: sourceCode = """def doit(self):\n\tpass\n""" self.setFunction(sourceCode) self.readOnly = False #self.mtstate = 0 # used by MTScheduler to schedule nodes in sub-tree #self.thread = None # will hold a ThreadNode object self.newData = 0 # set to 1 by outputData of parent node self.widthFirstTag = 0 # tag used by widthFirstTraversal self.isRootNode = 1 # when a node is created it isnot yet a child self.forceExecution = 0 # if the forceExecution flag is set # we will always run, else we check if new data is available self.expandedIcon = False # True when widgets in node are shown self.widgetsHiddenForScale = False # will be set to true if node scales # below 1.0 while widget are seen # does the node have a progrs bar ? self.hasProgBar = progbar if self.hasProgBar: self.progBarH = 3 # Progress bar width else: self.progBarH = 0 self.scaleSum = 1.0 # scale factor for this node's icon self.highlightOptions = {'highlightbackground':'red'} self.unhighlightOptions = {'highlightbackground':'gray50'} self.selectOptions = {'background':'yellow'} self.deselectOptions = {'background':'gray85'} self.posx = 0 # position where node will be placed on canvas self.posy = 0 # these vales are set in the addNode method of # the canvas to which this node is added # the are the upper left corner if self.innerBox self.center = [0,0] # center of innerBox self.inputPorts = [] # will hold a list of InputPort objects self.outputPorts = [] # will hold a list of OutputPort objects self._inputPortsID = 0 # used to assign InputPorts a unique number self._outputPortsID = 0 # used to assign OutputPorts a unique number self._id = None # a unique node number, which is assigned in # network.addNodes() #self.widgets = {} # {widgetName: PortWidget object } # # Used to save the widget when it is unbound self.specialInputPorts = [] self.specialOutputPorts = [] self.specialPortsVisible = False self.children = [] # list of children nodes self.parents = [] # list of parent nodes self.nodesToRunCache = [] # list of nodes to be run when this node # triggers self.condition = None self.funcEditorDialog = None # ports description, these dictionaries are used to create ports # at node's instanciation self.inputPortsDescr = InputPortsDescr(self) # [{optionName:optionValue}] self.outputPortsDescr = OutputPortsDescr(self) # [{optionName:optionValue}] self.widgetDescr = {} # {widgetName: {optionName:optionValue}} self.mouseAction[''] = self.showParams_cb self.mouseAction[''] = self.toggleNodeExpand_cb self.mouseAction[''] = self.startMoveOneNode self.hasMoved = False # set to True in net.moveSubGraph() def customizeConnectionCode(self, conn, name, indent=''): return [] def showParams_cb(self, event=None): ed = self.getEditor() ed.libTree.showNodeParameters(self.paramPanel.mainFrame) def resize(self, event): return def onStoppingExecution(self): pass def beforeAddingToNetwork(self, network): NetworkItems.beforeAddingToNetwork(self, network) def safeName(self, name): """remove all weird symbols from node name so that it becomes a regular string usabel as a Python variable in a saved network """ name = name.replace(' ', '_') # name cannot contain spaces if name[0].isdigit(): # first letter cannot be a number name = '_'+name if name.isalnum(): return name if name.isdigit(): return name # replace weird characters by '_' newname = '' for c in name: if c.isalnum(): newname += c else:# if c in ['/', '\\', '~', '$', '!']: newname+= '_' return newname def getUniqueNodeName(self): return '%s_%d'%(self.safeName(self.name), self._id) def configure(self, **kw): """Configure a NetworkNode object. Going through this framework tags the node modified. Supports the following keywords: name: node name (string) position: node position on canvas. Must be a tuple of (x,y) coords function: the computational method of this node expanded: True or False. If True: expand the node specialPortsVisible: True or False. If True: show the special ports paramPanelImmediate: True or False. This sets the node's paramPanel immediate state """ ed = self.getEditor() for k,v in kw.items(): if k == 'function': #solves some \n issues when loading saved networks v = v.replace('\'\'\'', '\'') v = v.replace('\"\"\"', '\'') v = v.replace('\'', '\'\'\'') #v = v.replace('\"', '\'\'\'') kw[k] = v self.setFunction(v, tagModified=True) elif ed is not None and ed.hasGUI: if k == 'name': self.rename(v, tagModified=True) elif k == 'position': self.move(v[0], v[1], absolute=True, tagModified=True) elif k == 'expanded': if self.isExpanded() and v is False: self.toggleNodeExpand_cb() elif not self.isExpanded() and v is True: self.toggleNodeExpand_cb() elif k == 'specialPortsVisible': if self.specialPortsVisible and v is False: self.hideSpecialPorts(tagModified=True) elif not self.specialPortsVisible and v is True: self.showSpecialPorts(tagModified=True) elif k == 'paramPanelImmediate': self.paramPanel.setImmediate(immediate=v, tagModified=True) elif k == 'frozen': if self.frozen is True and v is False: self.toggleFrozen_cb() elif self.frozen is False and v is True: self.toggleFrozen_cb() def getDescr(self): """returns a dict with the current configuration of this node""" cfg = {} cfg['name'] = self.name cfg['position'] = (self.posx, self.posy) cfg['function'] = self.sourceCode cfg['expanded'] = self.isExpanded() cfg['specialPortsVisible'] = self.specialPortsVisible cfg['paramPanelImmediate'] = self.paramPanel.immediateTk.get() cfg['frozen'] = self.frozen return cfg def rename(self, name, tagModified=True): """Rename a node. remember the name has changed, resize the node if necessary""" if name == self.name or name is None or len(name)==0: return # if name contains ' " remove them name = name.replace("'", "") name = name.replace('"', "") self.name=name if self.iconMaster is None: return canvas = self.iconMaster canvas.itemconfigure(self.textId, text=self.name) self.autoResizeX() if tagModified is True: self._setModified(True) def displayName(self, displayedName, tagModified=True): """display the displyed node name. remember the name has changed, resize the node if necessary""" if displayedName is None or len(displayedName)==0: return # if name contains ' " remove them displayedName = displayedName.replace("'", "") displayedName = displayedName.replace('"', "") if self.iconMaster is None: return canvas = self.iconMaster canvas.itemconfigure(self.textId, text=displayedName) self.autoResizeX() if tagModified is True: self._setModified(True) def ischild(self, node): """returns True is self is a child node of node """ conn = self.getInConnections() for c in conn: if c.blocking is True: node2 = c.port1.node if node2 == node: return True else: return node2.ischild(node) return False def isMacro(self): """Returns False if this node is not a MacroNode, returns True if MacroNode""" return False def startMoveOneNode(self, event): # get a handle to the network of this node net = self.network # save the current selection if len(net.selectedNodes): self.tempo_curSel = net.selectedNodes[:] # clear the current selection net.clearSelection() # select this node so we can move it net.selectNodes([self], undo=0) # call the function to register functions for moving selected nodes net.moveSelectedNodesStart(event) # register an additional function to deselect this node # and restore the original selection num = event.num # FIXME looks like I am binding this many times ! net.canvas.bind(""%num, self.moveSelectedNodeEnd,'+') def moveSelectedNodeEnd(self, event): # get a handle to the network of this node net = self.network # clear the selection (made of this node) net.clearSelection() # if we saved a selection when we started moving this node, restore it if hasattr(self, 'tempo_curSel'): net.selectNodes(self.tempo_curSel, undo=0) del self.tempo_curSel net.canvas.unbind(""%event.num) self.updateCenter() def updateCenter(self): canvas = self.network.canvas if canvas is None: return bb = canvas.bbox(self.innerBox) cx = self.posx + (bb[2]-bb[0])/2 cy = self.posy + (bb[3]-bb[1])/2 self.center = [cx,cy] def isModified(self): # loop over all input ports, all widgets, all outputports, and report # if anything has been modified modified = False if self._modified: return True # input ports and widgets for p in self.inputPorts: if p._modified: modified = True break if p.widget: if p.widget._modified: modified = True break if modified is True: return modified # output ports for p in self.outputPorts: if p._modified: modified = True break return modified def resetModifiedTag(self): """set _modified attribute to False in node, ports, widgets.""" self._modified = False for p in self.inputPorts: p._modified = False if p.widget: p.widget._modified = False for p in self.outputPorts: p._modified = False def resetTags(self): """set _modified attribute to False in node, ports, widgets. Also, sets _original attribute to True in node, ports, widgets And we also reset the two flags in all connections from and to ports""" self._modified = False self._original = True for p in self.inputPorts: p._modified = False p._original = True if p.widget: p.widget._modified = False p.widget._original = True for c in p.connections: c._modified = False c._original = True for p in self.outputPorts: p._modified = False p._original = True for c in p.connections: c._modified = False c._original = True def getInputPortByName(self, name): # return the an input port given its name for p in self.inputPorts: if p.name==name: return p warnings.warn( 'WARNING: input port "%s" not found in node %s'%(name, self.name)) def getOutputPortByName(self, name): # return the an output port given its name for p in self.outputPorts: if p.name==name: return p warnings.warn( 'WARNING: output port "%s" not found in node %s'%(name, self.name)) def getOutputPortByType(self, type, name=None): # return the matching or first output port given its type if len(self.outputPorts) == 0: return None lDatatypeObject = \ self.outputPorts[0].getDatatypeObjectFromDatatype(type) lPort = None for p in self.outputPorts: if p.datatypeObject == lDatatypeObject: if p.name == name: return p elif p is None: return p elif lPort is None: lPort = p if lPort is not None: return lPort return None def getSpecialInputPortByName(self, name): # return the an input port given its name for p in self.specialInputPorts: if p.name==name: return p warnings.warn( 'WARNING: special input port "%s" not found in node %s'%(name, self.name)) def getSpecialOutputPortByName(self, name): # return the an output port given its name for p in self.specialOutputPorts: if p.name==name: return p warnings.warn( 'WARNING: special output port "%s" not found in node %s'%(name, self.name)) def getInConnections(self): l = [] for p in self.inputPorts: l.extend(p.connections) for p in self.specialInputPorts: l.extend(p.connections) return l def getOutConnections(self): l = [] for p in self.outputPorts: l.extend(p.connections) for p in self.specialOutputPorts: l.extend(p.connections) return l def getConnections(self): return self.getInConnections()+self.getOutConnections() def getWidgetByName(self, name): port = self.inputPortByName[name] if port: if port.widget: return port.widget ############################################################################## # The following methods are needed to save a network # getNodeDefinitionSourceCode() is called by net.getNodesCreationSourceCode() ############################################################################## def getNodeDefinitionSourceCode(self, networkName, indent="", ignoreOriginal=False): """This method builds the text-string to describe a network node in a saved file. networkName: string holding the networkName indent: string of whitespaces for code indentation. Default: '' ignoreOriginal: True/False. Default: False. If set to True, the node's attr _original is ignored (used in cut/copy/paste nodes inside a macro that came from a node library where nodes are marked original This method is called by net.getNodesCreationSourceCode() NOTE: macros.py MacroNode re-implements this method!""" lines = [] nodeName = self.getUniqueNodeName() self.nameInSavedFile = nodeName ################################################################## # add lines to import node from library, instanciate node, and # add node to network ################################################################## indent, l = self.getNodeSourceCodeForInstanciation( networkName, indent=indent, ignoreOriginal=ignoreOriginal) lines.extend(l) ################################################################## # fetch code that desccribes the changes done to this node compared # to the base class node ################################################################## txt = self.getNodeSourceCodeForModifications( networkName, indent=indent, ignoreOriginal=ignoreOriginal) lines.extend(txt) txt = self.getStateDefinitionCode(nodeName=nodeName,indent=indent) lines.extend(txt) return lines def getStateDefinitionCode(self, nodeName, indent=''): #print "getStateDefinitionCode" return '' def getNodeSourceCodeForModifications(self, networkName, indent="", ignoreOriginal=False): """Return the code that describes node modifications compared to the original node (as described in a node library)""" lines = [] ################################################################## # add lines for ports if they changed compared to the base class ################################################################## indent, l = self.getNodeSourceCodeForPorts(networkName, indent , ignoreOriginal) lines.extend(l) ################################################################## # add lines for widgets: add/delete/configure/unbind, and set value ################################################################## indent, l = self.getNodeSourceCodeForWidgets(networkName, indent, ignoreOriginal) lines.extend(l) ################################################################## # add lines for node changes (name, expanded, etc) ################################################################## indent, l = self.getNodeSourceCodeForNode(networkName, indent, ignoreOriginal) lines.extend(l) return lines def getNodeSourceCodeForInstanciation(self, networkName="masterNet", indent="", ignoreOriginal=False, full=0): """This method is called when saving a network. Here, code is generated to import a node from a library, instanciate the node, and adding it to the network.""" lines = [] ed = self.getEditor() nodeName = self.getUniqueNodeName() ################################################################## # Abort if this node is original (for example, inside a macro node) ################################################################## if self._original is True and not full and not ignoreOriginal: return indent, lines ed._tmpListOfSavedNodes[nodeName] = self k = self.__class__ ################################################################## # add line to import node from the library ################################################################## if self.library is not None: libName = self.library.varName lines.append(indent + "klass = %s.nodeClassFromName['%s']\n"%(libName, k.__name__)) else: pathStr = str(k)[8:-2] # i.e. 'NetworkEditor.Tests.nodes.PassNode' klass = pathStr.split('.')[-1] # i.e. PassNode path = pathStr[:-len(klass)-1] # i.e. NetworkEditor.Tests.nodes libName = None lines.append(indent + "from %s import %s\n"%(path, klass)) lines.append(indent + "klass = %s\n"%klass) #if self.library.file is not None: # user defined library ## if False: # skip this test!! ## l = "from mglutil.util.packageFilePath import "+\ ## "getObjectFromFile\n" ## lines.append(indent+l) ## lines.append(indent+"%s = getObjectFromFile( '%s', '%s')\n"%( ## k.__name__, self.library.file, k.__name__)) ## else: ## line = "from "+k.__module__+" import "+k.__name__+"\n" ## lines.append(indent+line) ## else: # generate from EwSignal import EwSignalCollector #line = "from "+k.__module__+" import "+k.__name__+"\n" #lines.append(indent+line) # generate klass = EwSignal.nodeClassFromName['EwSignalCollector'] #line = "klass = %s.nodeClassFromName['%s']\n"%(self.library.varName, # k.__name__) #lines.append(indent+line) ################################################################## # add line with constructor keywords if needed ################################################################## constrkw = '' for name, value in self.constrkw.items(): constrkw = constrkw + name+'='+str(value)+', ' # this line seems redondant, # but is usefull when the network is saved and resaved # especially with pmv nodes constrkw = constrkw + "constrkw="+str(self.constrkw)+', ' ################################################################## # add line to instanciate the node ################################################################## #line=nodeName+" = "+k.__name__+"("+constrkw+"name='"+\ # self.name+"'" line=nodeName+" = klass("+constrkw+"name='"+\ self.name+"'" if libName is not None: line = line + ", library="+libName line = line + ")\n" lines.append(indent+line) ################################################################## # add line to add the node to the network ################################################################## txt = networkName+".addNode("+nodeName+","+str(self.posx)+","+\ str(self.posy)+")\n" lines.append(indent+txt) return indent, lines def getNodeSourceCodeForPorts(self, networkName, indent="", ignoreOriginal=False,full=0, dummyNode=None, nodeName=None): """Create code used to save a network which reflects changes of ports compared to the port definitions in a given network node of a node library. We create text to configure a port with changes, adding a port or deleting a port. If optional keyword 'full' is set to 1, we will append text to configure unchanged ports""" lines = [] ed = self.getEditor() if dummyNode is None: # we need the base class node if issubclass(self.__class__, FunctionNode): lKw = {'masternet':self.network} lKw.update(self.constrkw) dummyNode = apply(self.__class__,(), lKw) else: dummyNode = self.__class__() if nodeName is None: nodeName = self.getUniqueNodeName() ############################################################### # created save strings for inputPorts ############################################################### i = 0 lDeleted = 0 for index in range(len(dummyNode.inputPortsDescr)): # Delete remaining input ports if necessary if i >= len(self.inputPorts): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "%s.deletePort(%s.inputPortByName['%s'])\n"%( #txt = "%s.deletePort(%s.getInputPortByName('%s'))\n"%( nodeName, nodeName, dummyNode.inputPortsDescr[index]['name']) lines.append(indent+txt) continue ip = self.inputPorts[i] # delete input port if ip._id != index: if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "%s.deletePort(%s.inputPortByName['%s'])\n"%( #txt = "%s.deletePort(%s.getInputPortByName('%s'))\n"%( nodeName, nodeName, dummyNode.inputPortsDescr[index]['name']) lines.append(indent+txt) lDeleted += 1 continue # modify input port else: if ip._modified is True or ignoreOriginal is True: if full: changes = ip.getDescr() else: changes = ip.compareToOrigPortDescr() if len(changes): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "apply(%s.inputPortByName['%s'].configure, (), %s)\n"%( nodeName, dummyNode.inputPortsDescr[ip._id]['name'], str(changes) ) #txt = "apply(%s.inputPorts[%s].configure, (), %s)\n"%( # nodeName, ip.number, str(changes) ) lines.append(indent+txt) i = i + 1 continue # check if we have to add additional input ports for p in self.inputPorts[len(dummyNode.inputPortsDescr) - lDeleted:]: if p._modified is True or ignoreOriginal is True: descr = p.getDescr() if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "apply(%s.addInputPort, (), %s)\n"%( nodeName, str(descr) ) lines.append(indent+txt) ############################################################### # created save strings for outputPorts ############################################################### i = 0 for index in range(len(dummyNode.outputPortsDescr)): # Delete remaining output ports if necessary if i >= len(self.outputPorts): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "%s.deletePort(%s.outputPortByName['%s'])\n"%( nodeName, nodeName, dummyNode.outputPortsDescr[index]['name']) lines.append(indent+txt) continue op = self.outputPorts[i] # delete output port if not op._id == index: if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "%s.deletePort(%s.outputPortByName['%s'])\n"%( nodeName, nodeName, dummyNode.outputPortsDescr[index]['name']) lines.append(indent+txt) continue # modify output port else: if op._modified is True or ignoreOriginal is True: if full: changes = op.getDescr() else: changes = op.compareToOrigPortDescr() if len(changes): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "apply(%s.outputPortByName['%s'].configure, (), %s)\n"%( nodeName, dummyNode.outputPortsDescr[op._id]['name'], str(changes) ) #txt = "apply(%s.outputPorts[%s].configure, (), %s)\n"%( # nodeName, op.number, str(changes) ) lines.append(indent+txt) i = i + 1 continue # check if we have to add additional output ports for p in self.outputPorts[len(dummyNode.outputPortsDescr):]: if p._modified is True or ignoreOriginal is True: descr = p.getDescr() if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "apply(%s.addOutputPort, (), %s)\n"%( nodeName, str(descr) ) lines.append(indent+txt) # makes the specials ports visible if necessary if self.specialPortsVisible: if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt = "apply(%s.configure, (), {'specialPortsVisible': True})\n"%(nodeName) lines.append(indent+txt) return indent, lines def getNodeSourceCodeForWidgets(self, networkName, indent="", ignoreOriginal=False, full=0, dummyNode=None, nodeName=None): """Create code used to save a network which reflects changes of widgets compared to the widget definitions in a given network node of a node library. We create text to configure a widget with changes, adding a widget or deleting a widget. If optional keyword 'full' is set to 1, we will append text to configure unchanged widgets.""" lines = [] ed = self.getEditor() if dummyNode is None: # we need the base class node if issubclass(self.__class__, FunctionNode): lKw = {'masternet':self.network} lKw.update(self.constrkw) dummyNode = apply(self.__class__,(), lKw) else: dummyNode = self.__class__() if nodeName is None: nodeName = self.getUniqueNodeName() for i in range(len(self.inputPorts)): p = self.inputPorts[i] if p._id >= len(dummyNode.inputPortsDescr): origDescr = None elif p.name != dummyNode.inputPortsDescr[p._id]['name']: origDescr = None else: try: origDescr = dummyNode.widgetDescr[p.name] except: origDescr = None w = p.widget try: ownDescr = w.getDescr() except: ownDescr = None ############################################################# # if current port has no widget and orig port had no widget: # continue ############################################################# if ownDescr is None and origDescr is None: pass ############################################################# # if current port has no widget and orig port had a widget: # unbind the widget. Also, check if the port was modified: # unbinding and deleting a widget sets the port._modifed=True ############################################################# elif ownDescr is None and origDescr is not None: if (p._modified is True) or (ignoreOriginal is True): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) ## distinguish between "delete" and "unbind": # 1) Delete event (we don't have _previousWidgetDescr) if p._previousWidgetDescr is None: txt = "%s.inputPortByName['%s'].deleteWidget()\n"%( nodeName, self.inputPortsDescr[i]['name']) #txt = "%s.inputPorts[%d].deleteWidget()\n"%( # nodeName, i) lines.append(indent+txt) # 2) unbind event (we have _previousWidgetDescr) else: # first, set widget to current value txt1 = self.getNodeSourceCodeForWidgetValue( networkName, i, indent, ignoreOriginal, full) lines.extend(txt1) # then unbind widget txt2 = "%s.inputPortByName['%s'].unbindWidget()\n"%( nodeName, self.inputPortsDescr[i]['name']) #txt2 = "%s.inputPorts[%d].unbindWidget()\n"%( # nodeName, i) lines.append(indent+txt2) ############################################################# # if current port has widget and orig port had no widget: # create the widget ############################################################# elif ownDescr is not None and origDescr is None: if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) # create widget txt = \ "apply(%s.inputPortByName['%s'].createWidget, (), {'descr':%s})\n"%( nodeName, self.inputPortsDescr[i]['name'], str(ownDescr ) ) #txt = \ # "apply(%s.inputPorts[%d].createWidget, (), {'descr':%s})\n"%( # nodeName, i, str(ownDescr ) ) lines.append(indent+txt) # Hack to set widget. This fixes the ill sized nodes # when new widgets have been added to a node (MS) wmaster = ownDescr.get('master', None) if wmaster=='node': txt = "%s.inputPortByName['%s'].widget.configure(master='node')\n"%(nodeName, self.inputPortsDescr[i]['name']) lines.append(indent+txt) # set widget value txt = self.getNodeSourceCodeForWidgetValue( networkName, i, indent, ignoreOriginal, full, nodeName) lines.extend(txt) ############################################################# # if current port has widget and orig port has widget: # check if both widgets are the same, then check if changes # occured. # If widgets are not the same, delete old widget, create new ############################################################# elif ownDescr is not None and origDescr is not None: if ownDescr['class'] == origDescr['class']: if p.widget._modified is True or ignoreOriginal is True: if full: changes = ownDescr else: changes = w.compareToOrigWidgetDescr() if len(changes): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) if changes.has_key('command'): # extract and build the correct CB function name lCommand = changes['command'] lCommandStr = str(lCommand) lCbIndex = lCommandStr.find('.') lCbFuncName = nodeName + lCommandStr[lCbIndex:] lCbIndex = lCbFuncName.find(' ') lCbFuncName = lCbFuncName[:lCbIndex] changes['command'] = lCbFuncName # the changes['command'] is now a string # so, we need to get rid of the quote # that comes with the output lChangesStr = str(changes) lQuoteIndex = lChangesStr.find(lCbFuncName) lChanges = lChangesStr[:lQuoteIndex-1] + \ lCbFuncName + \ lChangesStr[lQuoteIndex+len(lCbFuncName)+1:] else: lChanges = str(changes) txt = \ "apply(%s.inputPortByName['%s'].widget.configure, (), %s)\n"%( nodeName, self.inputPortsDescr[i]['name'], lChanges) #txt = \ #"apply(%s.inputPorts[%d].widget.configure, (), %s)\n"%( # nodeName, i, str(changes)) lines.append(indent+txt) else: if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) txt1 = "%s.inputPortByName['%s'].deleteWidget()\n"%( nodeName, self.inputPortsDescr[i]['name']) #txt1 = "%s.inputPorts[%d].deleteWidget()\n"%( # nodeName,i) txt2 = \ "apply(%s.inputPortByName['%s'].createWidget, (), {'descr':%s})\n"%( nodeName, self.inputPortsDescr[i]['name'], str(ownDescr) ) #txt2 = \ #"apply(%s.inputPorts[%d].createWidget, (), {'descr':%s})\n"%( # nodeName, i, str(ownDescr) ) lines.append(indent+txt1) lines.append(indent+txt2) # and set widget value txt = self.getNodeSourceCodeForWidgetValue( networkName, i, indent, ignoreOriginal, full, nodeName) lines.extend(txt) return indent, lines def getNodeSourceCodeForWidgetValue(self, networkName, portIndex, indent="", ignoreOriginal=False, full=0, nodeName=None): """Returns code to set the widget value. Note: here we have to take unbound widgets into account.""" ############################################################# # Setting widget value sets widget _modified=True ############################################################# lines = [] returnPattern = re.compile('\n') # used when data is type(string) p = self.inputPorts[portIndex] # we need the base class node if issubclass(self.__class__, FunctionNode): lKw = {'masternet':self.network} lKw.update(self.constrkw) dummyNode = apply(self.__class__,(), lKw) else: dummyNode = self.__class__() if nodeName is None: nodeName = self.getUniqueNodeName() ############################################################# # Get data and original widget description to check if value # changed ############################################################# ## do we have a widget ? if p.widget: ## is it an original widget? try: origDescr = dummyNode.widgetDescr[p.name] except: ## or a new widget origDescr = {} val = p.widget.getDataForSaving() ## do we have an unbound widget ? elif p.widget is None and p._previousWidgetDescr is not None: origDescr = p._previousWidgetDescr val = p._previousWidgetDescr['initialValue'] ## no widget ? else: return lines ############################################################# # Compare data to default value, return if values are the same ############################################################# ## ## CASE 1: BOUND WIDGET: ## if p.widget: ## # MS WHY ignor original when cut and copy??? ## # ignoreOriginal is set True when cut|copy ## #if not p.widget._modified and not ignoreOriginal: ## # return lines ## # 1) compare value to initial value of widget descr ## wdescr = p.widget.getDescr() ## if wdescr.has_key('initialValue'): ## if val==wdescr['initialValue']: # value is initial value ## return lines ## # 2) else: compare to initialValue in node base class definition ## else: ## # 3) if the widget's original description has an initialValue ## if origDescr.has_key('initialValue'): ## if val == origDescr['initialValue']: ## return lines ## # 4) else, compare to widget base class defined initialValue ## else: ## origWidgetDescr = p.widget.__class__.configOpts ## if val == origWidgetDescr['initialValue']['defaultValue']: ## return lines ## ## CASE 2: UNBOUND WIDGET: ## else: ## descr = dummyNode.widgetDescr[p.name] ## #if descr.has_key('initialValue') and val == descr['initialValue']: ## # return lines ############################################################# # Create text to save widget value ############################################################# if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) if p.widget is None: #widget has been unbinded in the before or after adding to network #as it will be unbinded later we can safely rebind it to set the widget datatxt = '%s.inputPortByName[\'%s\'].rebindWidget()\n'%( nodeName, self.inputPortsDescr[portIndex]['name']) lines.append(indent+datatxt) if type(val)==types.StringType: if returnPattern.search(val): #multi - line data datatxt = \ '%s.inputPortByName[\'%s\'].widget.set(r"""%s""", run=False)\n'%( nodeName, self.inputPortsDescr[portIndex]['name'], val) else: datatxt = '%s.inputPortByName[\'%s\'].widget.set(r"%s", run=False)\n'%( nodeName, self.inputPortsDescr[portIndex]['name'], val) else: if hasattr(val, 'getDescr'): datatxt = '%s.inputPortByName[\'%s\'].widget.set(%s, run=False)\n'%( nodeName, self.inputPortsDescr[portIndex]['name'], val.getDescr() ) else: datatxt = '%s.inputPortByName[\'%s\'].widget.set(%s, run=False)\n'%( nodeName, self.inputPortsDescr[portIndex]['name'], val) lines.append(indent+datatxt) return lines def getNodeSourceCodeForNode(self, networkName, indent="", ignoreOriginal=False, full=0, nodeName=None): """return code to configure a node with modifications compared to the node definition in a node library. Note: """ lines = [] if (self._modified is False) and (ignoreOriginal is False): return indent, lines if full: changes = self.getDescr().copy() else: changes = self.compareToOrigNodeDescr() if changes.has_key('name'): changes.pop('name') # name is passed to constructor if changes.has_key('position'): changes.pop('position') # position is set in addNode if nodeName is None: nodeName = self.getUniqueNodeName() if changes.has_key('function'): changes.pop('function') # function has to be set separately: code, i = self.getNodeSourceCodeForDoit( networkName=networkName, nodeName=nodeName, indent=indent, ignoreOriginal=ignoreOriginal) if code: # Note: the line to add the code to the node is returned # within 'code' lines.extend(code) if len(changes): txt = "apply(%s.configure, (), %s)\n"%( nodeName, str(changes)) lines.append(indent+txt) return indent, lines def getNodeSourceCodeForDoit(self, networkName, nodeName,indent="", ignoreOriginal=False, full=0): lines = [] ed = self.getEditor() if (self._modified is True) or (ignoreOriginal is True): if nodeName != 'self': lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) lines.append(indent+"code = \"\"\"%s\"\"\"\n"%self.sourceCode) lines.append(indent+"%s.configure(function=code)\n"% nodeName) return lines, indent def getAfterConnectionsSourceCode(self, networkName, indent="", ignoreOriginal=False): """Here, we provide a hook for users to generate source code which might be needed to adress certain events after connections were formed: for example, connections might generate new ports.""" # The MacroOutputNode subclasses this method and returns real data lines = [] return lines def compareToOrigNodeDescr(self): """compare this node to the original node as defined in a given node library, such as StandardNodes. Return a dictionary containing the differences.""" ownDescr = self.getDescr().copy() dummy = self.__class__() # we need to create a base class node # we dont need to add the self generated port # as we only look here for the node modifications for k,v in ownDescr.items(): if k == 'name': if v == dummy.name: ownDescr.pop(k) elif k == 'position': # this is a bit tricky: the dummy node # has not been added to a net yet, thus we assume a new position continue elif k == 'function': #we don't compare the prototype as it is automatically generated #the code itself is what may have be changed if v[v.find(':'):] == dummy.sourceCode[dummy.sourceCode.find(':'):]: ownDescr.pop(k) elif k == 'expanded': if v == dummy.inNodeWidgetsVisibleByDefault: ownDescr.pop(k) elif k == 'specialPortsVisible': if v == dummy.specialPortsVisible: ownDescr.pop(k) elif k == 'paramPanelImmediate': # default value is 0 if v == 0 or v is False: ownDescr.pop(k) elif k == 'frozen': # default is False if v == dummy.frozen: ownDescr.pop(k) return ownDescr def checkIfNodeForSavingIsDefined(self, lines, networkName, indent): """This method fixes a problem with saving macros that come from a node library. If only a widget value has changed, we do not have a handle to the node. Thus, we need to create this additional line to get a handle """ ed = self.getEditor() nodeName = self.getUniqueNodeName() if ed._tmpListOfSavedNodes.has_key(nodeName) is False: # This part is a bit complicated: we need to define the various # macro nodes if we have nested macros and they are not explicitly # created (e.g. a macro from a node library) from macros import MacroNetwork if isinstance(self.network, MacroNetwork): roots = self.network.macroNode.getRootMacro() for macro in roots[1:]: # skip root, because this is always defined!? nn = macro.getUniqueNodeName() # was nn = 'node%d'%macro._id if ed._tmpListOfSavedNodes.has_key(nn) is False: txt = "%s = %s.macroNetwork.nodes[%d]\n"%( nn, macro.network.macroNode.getUniqueNodeName(), macro.network.nodeIdToNumber(macro._id)) lines.append(indent+txt) ed._tmpListOfSavedNodes[nn] = macro # now process the 'regular' nodes #import pdb;pdb.set_trace() txt = "%s = %s.nodes[%d]\n"%(nodeName, networkName, self.network.nodeIdToNumber(self._id)) lines.append(indent+txt) ed._tmpListOfSavedNodes[nodeName] = self return lines ############################################################################# #### The following methods are needed to generate source code (not for saving #### networks) ############################################################################# def saveSource_cb(self, dependencies=False): """ the classname is extracted from the given filename """ lPossibleFileName = "New" + self.name + ".py" lPossibleFileNameSplit = lPossibleFileName.split(' ') initialfile = '' for lSmallString in lPossibleFileNameSplit: initialfile += lSmallString userResourceFolder = self.getEditor().resourceFolderWithVersion if userResourceFolder is None: return userVisionDir = userResourceFolder + os.sep + 'Vision' + os.sep userLibsDir = userVisionDir + 'UserLibs' + os.sep defaultLibDir = userLibsDir + 'MyDefaultLib' file = tkFileDialog.asksaveasfilename( initialdir = defaultLibDir , filetypes=[('python source', '*.py'), ('all', '*')], title='Save source code in a category folder', initialfile=initialfile ) if file: # get rid of the extension and of the path lFileSplit = file.split('/') name = lFileSplit[-1].split('.')[0] self.saveSource(file, name, dependencies) # reload the modified library self.getEditor().loadLibModule(str(lFileSplit[-3])) def saveSource(self, filename, classname, dependencies=False): f = open(filename, "w") map( lambda x, f=f: f.write(x), self.getNodeSourceCode(classname, networkName='self.masterNetwork', dependencies=dependencies) ) f.close() def getNodeSourceCode(self, className, networkName='self.network', indent="", dependencies=False): """This method is called through the 'save source code' mechanism. Generate source code describing a node. This code can be put into a node library. This is not for saving networks. dependencies: True/False False: the node is fully independent from his original node. True : the node is saved as a subclass of the original node, and only modifications from the original are saved. """ lines = [] kw = {} # keywords dict kw['dependencies'] = dependencies indent0 = indent txt, indent = apply(self.getHeaderBlock, (className, indent), kw) lines.extend(txt) # this make sure the port types will be avalaible when saved code will run lTypes = {} lSynonyms = {} lPorts = self.inputPorts + self.outputPorts for p in lPorts: lName = p.datatypeObject.__class__.__name__ if (lTypes.has_key(lName) is False) and \ (p.datatypeObject.__module__ != 'NetworkEditor.datatypes'): lTypes[lName] = p.datatypeObject.__module__ lName = p.datatypeObject['name'] if lSynonyms.has_key(lName) is False: lSplitName = lName.split('(') lBaseName = lSplitName[0] if (len(lSplitName) == 2) and (lSynonyms.has_key(lBaseName) is False): lDict = self.network.getTypeManager().getSynonymDict(lBaseName) if lDict is not None: lSynonyms[lBaseName] = lDict lDict = self.network.getTypeManager().getSynonymDict(lName) if lDict is not None: lSynonyms[lName] = lDict kw['types'] = lTypes kw['synonyms'] = lSynonyms txt, indent = apply(self.getInitBlock, (className, indent), kw) kw.pop('types') kw.pop('synonyms') lines.extend(txt) if dependencies is True: nodeName = 'self' indent, txt = self.getNodeSourceCodeForNode(self.network, indent=indent, full=0, nodeName=nodeName) lines.extend(txt) lines.extend("\n\n" + indent0 + " " + \ "def afterAddingToNetwork(self):\n" + \ indent + "pass\n") constrkw = {} constrkw.update( self.constrkw ) constrkw['name'] = className dummyNode = apply( self.originalClass,(),constrkw) indent, txt = self.getNodeSourceCodeForPorts( self.network, indent=indent, ignoreOriginal=False, full=0, dummyNode=dummyNode, nodeName=nodeName) lines.extend(txt) indent, txt = self.getNodeSourceCodeForWidgets( self.network, indent=indent, ignoreOriginal=False, full=0, dummyNode=dummyNode, nodeName=nodeName) lines.extend(txt) elif dependencies is False: txt = self.getComputeFunctionSourceCode(indent=indent) lines.extend(txt) txt, indent = apply(self.getPortsCreationSourceCode, (self.inputPorts, 'input', indent), kw) lines.extend(txt) txt, indent = apply(self.getPortsCreationSourceCode, (self.outputPorts, 'output', indent), kw) lines.extend(txt) txt, indent = self.getWidgetsCreationSourceCode(indent) lines.extend(txt) else: assert(False) indent1 = indent + ' '*4 lines.extend("\n\n" + indent0 + " " + \ "def beforeAddingToNetwork(self, net):\n") # this make sure the host web service is loaded if self.constrkw.has_key('host'): lines.extend( indent + "try:\n" ) ## get library import cache ## then write libray import code cache = self.network.buildLibraryImportCache( {'files':[]}, self.network, selectedOnly=False) li = self.network.getLibraryImportCode( cache, indent1, editor="self.editor", networkName="net", importOnly=True, loadHost=True) lines.extend(li) lines.extend( indent + "except:\n" + \ indent1 + "print 'Warning! Could not load web services'\n\n") # this make sure the port widgets will be avalaible when saved code will run lines.extend(indent + "try:\n" ) lWidgetsClass = [] for p in self.inputPorts: lClass = p.widget.__class__ lModule = lClass.__module__ if ( lModule != 'NetworkEditor.widgets') \ and (lModule != '__builtin__') \ and (lModule not in lWidgetsClass): lWidgetsClass.append(lClass) lines.append(indent1 + "ed = net.getEditor()\n") for w in lWidgetsClass: lWidgetsClassName = w.__name__ lines.append(indent1 + "from %s import %s\n" % (w.__module__, lWidgetsClassName) ) lines.extend(indent1 + "if %s not in ed.widgetsTable.keys():\n" % lWidgetsClassName ) lines.extend(indent1 + 4*' ' + \ "ed.widgetsTable['%s'] = %s\n" % (lWidgetsClassName, lWidgetsClassName) ) lines.extend(indent + "except:\n" + \ indent1 + "import traceback; traceback.print_exc()\n" + \ indent1 + "print 'Warning! Could not import widgets'\n") lines.extend("\n") return lines #################################################### #### Helper Methods follow to generate save file ### #################################################### def getHeaderBlock(self, className, indent="", **kw): """Generate source code to import a node from a library or file.""" lines = [] dependencies = kw['dependencies'] import datetime lNow = datetime.datetime.now().strftime("%A %d %B %Y %H:%M:%S") lCopyright = \ """######################################################################## # # Vision Node - Python source code - file generated by vision # %s # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Daniel Stoffler, Michel Sanner and TSRI # # revision: Guillaume Vareille # ######################################################################### # # $%s$ # # $%s$ # """%(lNow, "Header:", "Id:") # if directly in the txt, CVS fills these fields lines.append(lCopyright) return lines, indent def getInitBlock(self, className, indent="", **kw): """Generate source code to define the __init__() method of the node, building the correct constrkw dict, etc.""" lines = [] dependencies = kw['dependencies'] lines.append(indent+"# import node's base class node\n") # if dependencies is True: # mod = self.originalClass.__module__ # klass = self.originalClass.__name__ # txt1 = "from %s import %s\n"%(mod,klass) # lines.append(indent+txt1) # txt2 = "class %s(%s):\n"%(className,klass) # lines.append(indent+txt2) # else: # txt1 = "from NetworkEditor.items import NetworkNode\n" # lines.append(indent+txt1) # txt2 = "class %s(NetworkNode):\n"%className # lines.append(indent+txt2) txt1 = "from NetworkEditor.items import NetworkNode\n" lines.append(indent+txt1) mod = self.originalClass.__module__ klass = self.originalClass.__name__ txt1 = "from %s import %s\n"%(mod,klass) lines.append(indent+txt1) txt2 = "class %s(%s):\n"%(className,klass) lines.append(indent+txt2) if self.originalClass.__doc__ is not None: lines.append(indent+' \"\"\"'+self.originalClass.__doc__) lines.append('\"\"\"\n') indent1 = indent + 4*" " indent2 = indent1 + 4*" " if kw.has_key('types'): lines.append(indent1 + "mRequiredTypes = " + kw['types'].__str__() + '\n') if kw.has_key('synonyms'): lines.append(indent1 + "mRequiredSynonyms = [\n") for lkey, lSynonym in kw['synonyms'].items(): lines.extend(indent2 + lSynonym.__str__() + ',\n') lines.append(indent1 + ']\n') # build constructor keyword from original class # constrkw is not used by the original class but only by NetworkNode constrkw = '' for name, value in self.constrkw.items(): constrkw = constrkw + name+'='+str(value)+', ' constrkw = constrkw + "constrkw = " + str(self.constrkw)+', ' indent += 4*" " lines.append(indent+\ "def __init__(self, %s name='%s', **kw):\n" % ( constrkw, className)) indent += 4*" " lines.append(indent+"kw['constrkw'] = constrkw\n") lines.append(indent+"kw['name'] = name\n") if dependencies is True: klass = self.originalClass.__name__ lines.append(indent+"apply(%s.__init__, (self,), kw)\n"%klass) # we just fully blank everything an recreate them # we will need to save only the differences and not everything #lines.append(indent+"self.inputPortsDescr = []\n") #lines.append(indent+"self.outputPortsDescr = []\n") #lines.append(indent+"self.widgetDescr = {}\n") if self._modified is True: lines.append(indent+"self.inNodeWidgetsVisibleByDefault = %s\n"%self.inNodeWidgetsVisibleByDefault) else: lines.append(indent+"apply( NetworkNode.__init__, (self,), kw)\n") if self.inNodeWidgetsVisibleByDefault: lines.append(indent+"self.inNodeWidgetsVisibleByDefault = True\n") return lines, indent def getPortsCreationSourceCode(self, ports, ptype='input', indent="",**kw): """generates code to create ports using the inputportsDescr and outputPortsDescr""" lines = [] dependencies = kw['dependencies'] assert ptype in ['input', 'output'] for p in ports: d = p.getDescr() if d is None: d = {} lines.append(indent+"self.%sPortsDescr.append(\n"%ptype) lines.append(indent+ 4*" " + "%s)\n"%str(d) ) return lines, indent def getWidgetsCreationSourceCode(self, indent="",**kw): """generating code to create widgets using the widgetDescr""" lines = [] for p in self.inputPorts: if p.widget is None: continue d = p.widget.getDescr() # save current widget value d['initialValue'] = p.widget.getDataForSaving() if d is None: d = {} lines.append(indent+"self.widgetDescr['%s'] = {\n"%p.name) lines.append(indent+ 4*" " + "%s\n"%str(d)[1:] ) #ommit first { return lines, indent def getComputeFunctionSourceCode(self, indent="", **kw): lines = [] nodeName = 'self' lines.append(indent+"code = \"\"\"%s\"\"\"\n"%self.sourceCode) lines.append(indent+"%s.configure(function=code)\n"% nodeName) return lines ###################### END of methods generating source code ################ ############################################################################# def outputData(self, **kw): for p in self.outputPorts: if kw.has_key(p.name): data = kw[p.name] kw.pop(p.name) p.outputData(data) else: ed = self.getEditor() if ed.hasGUI: ed.balloons.tagbind( self.network.canvas, p.id, p.balloonBase) if len(kw): for k in kw.keys(): warnings.warn( "WARNING: port %s not found in node %s"%(k, self.name) ) def setFunction(self, source, tagModified=True): """Set the node's compute function. If tagModified is True, we set _modified=True""" self.sourceCode = source self.dynamicComputeFunction = self.evalString(source) if tagModified: self._setModified(True) # update the source code editor if available if self.objEditor is not None: if self.objEditor.funcEditorDialog is not None: self.objEditor.funcEditorDialog.settext(source) def scheduleChildren(self, portList=None): """run the children of this node in the same thread as the parent if portList is None all children are scheduled, else only children of the specified ports are scheduled """ #print "NetworkNodeBase.scheduleChildren" net = self.network # get the list of nodes to run if portList is None: allNodes = net.getAllNodes(self.children) for n in self.children: n.forceExecution = 1 else: children = [] for p in portList: children.extend(map (lambda x: x.node, p.children) ) for n in children: n.forceExecution = 1 # since a node can be a child through multiple ports we have to # make the list of children unique allNodes = net.getAllNodes(uniq(children)) if len(allNodes): #self.forceExecution = 1 #print "SCHEDULE CHILDREN", allNodes net.runNodes(allNodes) def schedule_cb(self, event=None): self.forceExecution = 1 self.schedule() def schedule(self): """start an execution thread for the subtree under that node """ #print "NetworkNodeBase.schedule", self.network.runOnNewData net = self.network ed = net.getEditor() if ed.hasGUI: if hasattr(ed, 'buttonBar'): bl = ed.buttonBar.toolbarButtonDict if bl.has_key('softrun'): bl['softrun'].disable() bl['run'].disable() #bl['runWithoutGui'].disable() #if ed.withThreads is True: bl['pause'].enable() bl['stop'].enable() net.run([self]) def computeFunction(self): # make sure all required input ports present data # make sure data is valid and available # call self.dynamicComputeFunction # Return 'Go' after successful execution or 'Stop' otherwise for p in self.outputPorts: if p.dataView: p.clearDataView() if not self.dynamicComputeFunction: return 'Stop' lArgs = [self,] ed = self.getEditor() # for each input port of this node newData = 0 for port in self.inputPorts: # this make sure the text entries are used even if the user hasn't pressed return if ed.hasGUI and port.widget is not None: w = port.widget.widget if isinstance(w, Tkinter.Entry) \ or isinstance(w, Pmw.ComboBox): before = port.widget.lastUsedValue after = port.widget.widget.get() if before != after: port.widget._newdata = True if port.hasNewData(): newData = 1 data = port.getData() # returns 'Stop' if bad or missing data if type(data) is types.StringType: if data.lower()=='stop': # turn node outline to missing data color if ed.hasGUI and ed.flashNodesWhenRun: c = self.iconMaster c.tk.call((c._w, 'itemconfigure', self.innerBox, '-outline', '#ff6b00', '-width', 4)) return 'Stop' if port.dataView: port.updateDataView() # update Data Browser GUI (only if window is not deiconified) if port.objectBrowser and \ port.objectBrowser.root.state()=='normal': port.objectBrowser.root.after( 100, port.objectBrowser.refresh_cb ) lArgs.append(data) stat = 'Stop' if newData \ or self.forceExecution \ or (self.network and self.network.forceExecution): #print "running %s with:"%self.name, args lCurrentDir = os.getcwd() if self.network: if self.network.filename is not None: lNetworkDir = os.path.dirname(self.network.filename) elif hasattr(self.network, 'macroNode') \ and self.network.macroNode.network.filename is not None: lNetworkDir = os.path.dirname(self.network.macroNode.network.filename) else: import Vision if hasattr(Vision, 'networkDefaultDirectory'): lNetworkDir = Vision.networkDefaultDirectory else: lNetworkDir = '.' # MS WHY do we have to go there ? Oct 2010 # removed it because this prevented networks from .psf file # wfrom working as the tmp dir was deleted #if os.path.exists(lNetworkDir): os.chdir(lNetworkDir) try: stat = apply( self.dynamicComputeFunction, tuple(lArgs) ) finally: os.chdir(lCurrentDir) if stat is None: stat = 'Go' for p in self.outputPorts: # update Data Viewer GUI if p.dataView: p.updateDataView() # update Data Browser GUI (only if window is not deiconified) if p.objectBrowser and p.objectBrowser.root.state() =='normal': p.objectBrowser.root.after( 100, p.objectBrowser.refresh_cb ) for p in self.inputPorts: p.releaseData() return stat def growRight(self, id, dx): """Expand (and shrink) the x-dimension of the node icon to (and from) the right.""" # we get the coords coords = self.iconMaster.coords(id) # compute the middle point using the bounding box of this object bbox = self.iconMaster.bbox(id) xmid = (bbox[0] + bbox[2]) * 0.5 # add dx for every x coord right of the middle point for i in range(0,len(coords),2): if coords[i]>xmid: coords[i]=coords[i]+dx apply( self.iconMaster.coords, (id,)+tuple(coords) ) def growDown(self, id, dy): """Expand (and shrink) the y-dimension of the node icon to (and from) the top.""" # we get the coords coords = self.iconMaster.coords(id) # compute the middle point using the bounding box of this object bbox = self.iconMaster.bbox(id) ymid = (bbox[1] + bbox[3]) * 0.5 # add dy for every y coord below of the middle point for i in range(1,len(coords),2): if coords[i]>ymid: coords[i]=coords[i]+dy apply( self.iconMaster.coords, (id,)+tuple(coords) ) def updateCode(self, port='ip', action=None, tagModified=True, **kw): """update signature of compute function in source code. We re-write the first line with all port names as arguments. **kw are not used but allow to match updateCode signature of output ports """ code = self.sourceCode # handle input port if port == 'ip': if action=='add' or action=='remove' or action=='create': ## This was bas: 13 assumed that was no space between doit( and ## self. If there is a space we lost the f at the end of self #signatureBegin = code.index('def doit(')+13 signatureBegin = code.index('self')+4 signatureEnd = code[signatureBegin:].index('):') signatureEnd = signatureBegin+signatureEnd newCode = code[:signatureBegin] if action=='create': for p in self.inputPortsDescr: newCode += ', ' + p['name'] #if p['required'] is True: # newCode += "='NA' " #else: # newCode += '=None ' else: for p in self.inputPorts: newCode += ', ' + p.name #if p.required is True: # newCode += "='NA' " #else: # newCode += '=None ' newCode = newCode + code[signatureEnd:] elif action=='rename': newname = kw['newname'] oldname = kw['oldname'] newCode = code.replace(oldname, newname) # handle output port elif port == 'op': newname = kw['newname'] if action==None: return if action=='add': # add comment on how to output data olds = "## to ouput data on port %s use\n"%newname olds += "## self.outputData(%s=data)\n"%newname code += olds elif action=='remove': oldname = kw['oldname'] # remove comment on how to output data olds = "## to ouput data on port %s use\n"%oldname olds += "## self.outputData(%s=data)\n"%oldname code = code.replace(olds, '') elif action=='rename': oldname = kw['oldname'] olds = "## to ouput data on port %s use\n"%oldname olds += "## self.outputData(%s=data)\n"%oldname news = "## to ouput data on port %s use\n"%newname news += "## self.outputData(%s=data)\n"%newname code = code.replace(olds, news) else: raise ValueError ( "action should be either 'add', 'remove', 'rename', got ", action) newCode = code else: warnings.warn("Wrong port type specified!", stacklevel=2) return # finally, set the new code self.setFunction(newCode, tagModified=tagModified) def toggleNodeExpand_cb(self, event=None): widgetsInNode = self.getWidgetsForMaster('Node') if len(widgetsInNode)==0: widgetsInParamPanel = self.getWidgetsForMaster('ParamPanel') if len(widgetsInParamPanel): if self.paramPanel.master.winfo_ismapped() == 1: self.paramPanel.hide() self.paramPanelTk.set(0) else: self.paramPanel.show() self.paramPanelTk.set(1) else: if self.isExpanded(): self.expandedIcon = False self.hideInNodeWidgets() else: self.expandedIcon = True self.showInNodeWidgets() self._setModified(True) def getWidthForPorts(self, maxi=None): # compute the width in the icon required for input and output ports # if maxw is not none, the maximum is return if maxi is None: maxi = maxwidth = 0 # find last visible inputport if len(self.inputPorts): for p in self.inputPorts[::-1]: # going backwards if p.visible: break maxwidth = p.relposx+2*p.halfPortWidth if len(self.outputPorts): for p in self.outputPorts[::-1]: # going backwards if p.visible: break if p.relposx+2*p.halfPortWidth > maxwidth: maxwidth = p.relposx+2*p.halfPortWidth return max(maxi, int(round(maxwidth*self.scaleSum))) def getHeightForPorts(self, maxi=None): # compute the height in the icon required for input and output ports # if maxw is not none, the maximum is return maxheight = 0 if maxi is None: maxi = 0 # find last visible inputport if len(self.inputPorts): for p in self.inputPorts[::-1]: # going backwards if p.visible: break maxheight = p.relposy+2*p.halfPortHeight if len(self.outputPorts): for p in self.outputPorts[::-1]: # going backwards if p.visible: break if p.relposy+2*p.halfPortHeight > maxheight: maxheight = p.relposy+2*p.halfPortHeight return max(maxi, int(round(maxheight*self.scaleSum))) def getWidthForLabel(self, maxi=None): # compute the width in the icon required for the label # if maxis is not not, the maximum is return if maxi is None: maxi = 0 bb = self.iconMaster.bbox(self.textId) return max(maxi, 10+(bb[2]-bb[0]) ) # label has 2*5 padding def getWidthForNodeWidgets(self, maxi=None): # compute the width in the icon required for node widgets # if maxis is not not, the maximum is return if maxi is None: maxi = 0 if self.isExpanded(): return max(maxi, self.nodeWidgetMaster.winfo_reqwidth()+10) else: return maxi def autoResizeX(self): # we find how wide the innerBox has to be canvas = self.iconMaster neededWidth = self.getWidthForPorts() neededWidth = self.getWidthForLabel(neededWidth) neededWidth = self.getWidthForNodeWidgets(neededWidth) # get width of current innerbox bb = canvas.bbox(self.innerBox) w = bb[2]-bb[0] self.resizeIcon(dx=neededWidth-w) def autoResizeY(self): canvas = self.iconMaster bb = canvas.bbox(self.textId) labelH = 12+self.progBarH+(bb[3]-bb[1]) # label has 2*5 padding if self.isExpanded(): widgetH = self.nodeWidgetMaster.winfo_reqheight() if len(self.getWidgetsForMaster('Node')): labelH += 6 else: widgetH = 0 bb = canvas.bbox(self.innerBox) curh = bb[3]-bb[1] self.resizeIcon(dy=labelH+widgetH-curh) def autoResize(self): self.autoResizeX() self.autoResizeY() if len(self.getWidgetsForMaster('node')): # resize gets the right size but always grows to the right # by hiding and showing the widgets in node we fix this self.toggleNodeExpand_cb() self.toggleNodeExpand_cb() def getSize(self): """returns size of this node as a tuple of (width, height) in pixels""" bbox = self.iconMaster.bbox(self.outerBox) w = bbox[2] - bbox[0] h = bbox[3] - bbox[1] return (w, h) def hideInNodeWidgets(self, rescale=1): # hide widgets in node by destroying canvas object holding them # the NE widget is not destroyed canvas = self.iconMaster if rescale: self.autoResizeX() h = self.nodeWidgetMaster.winfo_reqheight() self.resizeIcon(dy=-h-6) #bb = canvas.bbox(self.nodeWidgetTkId) #self.resizeIcon(dy=bb[1]-bb[3]) canvas.delete(self.nodeWidgetTkId) def showInNodeWidgets(self, rescale=1): canvas = self.iconMaster widgetFrame = self.nodeWidgetMaster oldbb = canvas.bbox(self.innerBox)# find current bbox #if len(self.nodeWidgetsID): # bb = canvas.bbox(self.nodeWidgetsID[-1]) # find bbox of last widget #else: # bb = canvas.bbox(self.textId) # find bbox of text bb = canvas.bbox(self.textId) # find bbox of text # pack the frame containg the widgets so we can measure it's size widgetFrame.pack() canvas.update_idletasks() h = widgetFrame.winfo_reqheight() # before asking for its size w = widgetFrame.winfo_reqwidth() ## FIXME the frame is created with a given size. Since it is on a canvas ## it does not resize when widgets are added or removed ## newh =0 ## for p in self.inputPorts: ## if p.widget and p.widget.inNode: ## newh += p.widget.widgetFrame.winfo_reqheight() ## h = newh-h tags = (self.iconTag, 'node') # compute center (x,y) of canvas window # add a window below text for widgets self.nodeWidgetTkId = widgetWin = canvas.create_window( bb[0]+(w/2), bb[3]+ self.progBarH +(h/2), tags=tags, window=widgetFrame ) if self.selected: canvas.addtag_withtag('selected', widgetWin) if rescale: self.autoResizeX() self.resizeIcon(dy=h+6) def getWidgetsForMaster(self, masterName): """Return a dict of all widgets bound for a given master in a given node (self). Masters can be 'Node' or 'ParamPanel'. key is an instance of the port, value is an instance of the widget""" widgets = {} for k, v in self.widgetDescr.items(): master = v.get('master', 'ParamPanel') if master.lower()==masterName.lower(): # find corresponding widget to this port for port in self.inputPorts: if port.name == k: widget = port.widget break widgets[port] = widget return widgets def drawMapIcon(self, naviMap): canvas = naviMap.mapCanvas x0, y0 = naviMap.upperLeft scaleFactor = naviMap.scaleFactor c = self.network.canvas.bbox(self.iconTag) cid = canvas.create_rectangle( [x0+c[0]*scaleFactor, y0+c[1]*scaleFactor, x0+c[2]*scaleFactor, y0+c[3]*scaleFactor], fill='grey50', outline='black', tag=('navimap',)) self.naviMapID = cid return cid def buildIcons(self, canvas, posx, posy, small=False): """Build NODE icon with ports etc """ NetworkItems.buildIcons(self, canvas) network = self.network self.paramPanelTk = Tkinter.IntVar() # to toggle Param. Panel self.paramPanelTk.set(0) # build node's Icon ed = self.getEditor() if ed.hasGUI: self.buildNodeIcon(canvas, posx, posy, small=small) # instanciate output ports for p in self.outputPorts: p.buildIcons(resize=False) # instanciate input ports inNode = 0 for p in self.inputPorts: # this loop first so all port have halfPortWidth p.buildIcons( resize=False ) for p in self.inputPorts: p.createWidget() if p.widget is not None: inNode = max(inNode, p.widget.inNode) if ed.hasGUI: self.autoResizeX() # in case we added too many ports # at least one widget is in the node. We show it if visible by default if inNode: if self.inNodeWidgetsVisibleByDefault: self.expandedIcon = True self.showInNodeWidgets( rescale=1 ) # instanciate special input ports for ip in self.specialInputPorts: ip.buildIcons(resize=False) if not self.specialPortsVisible: ip.deleteIcon() # instanciate special output ports for op in self.specialOutputPorts: op.buildIcons(resize=False) if not self.specialPortsVisible: op.deleteIcon() # this is needed because only now do we know the true relposx of ports # if we do not do this here, some port icons might be outside the node # but we do not want to autoResize() because that would rebuild widgets if ed.hasGUI: self.autoResizeX() # add command entries to node's pull down self.menu.add_command(label='Run', command=self.schedule_cb, underline=0) self.menu.add_checkbutton(label="Frozen", variable = self.frozenTk, command=self.toggleFrozen_cb, underline=0) # self.menu.add_command(label='Freeze', command=self.toggleFreeze) self.menu.add_separator() self.menu.add_command(label='Edit', command=self.edit, underline=0) self.menu.add_command(label='Edit compute function', command=self.editComputeFunction_cb, underline=14) self.menu.add_command(label='Introspect', command=self.introspect, underline=0) self.menu.add_checkbutton(label="Parameter Panel", variable = self.paramPanelTk, command=self.toggleParamPanel_cb, underline=0) self.menu.add_separator() self.menu.add_command(label='Copy', command=self.copy_cb, underline=0) self.menu.add_command(label='Cut', command=self.cut_cb, underline=0) self.menu.add_command(label='Delete', command=self.delete_cb, underline=0) self.menu.add_command(label='Reset', command=self.replaceWith, underline=0) if self.__class__ is FunctionNode and hasattr(self.function, 'serviceName'): def buildhostCascadeMenu(): self.menu.cascadeMenu.delete(0, 'end') for lKey in self.library.libraryDescr.keys(): if lKey.startswith('http://') and lKey != suppressMultipleQuotes(self.constrkw['host']): cb = CallBackFunction( self.replaceWithHost, host=lKey) self.menu.cascadeMenu.add_command(label=lKey, command=cb) self.menu.cascadeMenu = Tkinter.Menu(self.menu, tearoff=0, postcommand=buildhostCascadeMenu) self.menu.add_cascade(label='Replace with node from', menu=self.menu.cascadeMenu) if self.specialPortsVisible: self.menu.add_command(label='Hide special ports', command=self.hideSpecialPorts, underline=5) else: self.menu.add_command(label='Show special ports', command=self.showSpecialPorts, underline=5) def delete(self): # remove all connections to this node inConnections = self.getInConnections() self.network.deleteConnections(inConnections, 0, schedule=False) outConnections = self.getOutConnections() self.network.deleteConnections(outConnections, 0) self.beforeRemovingFromNetwork() # delete this node's ports inNode = 0 for p in self.inputPorts[:]: # IMPORTANT NOTE: since we will delete # the port from self.inputPorts while we are looping over this # list, we need to loop over a copy to avoid unpredictable # results! if p.dataView is not None: # kill data viewer window p.dataView.destroy() if p.widget: # close widget editor if p.widget.objEditor: p.widget.objEditor.Cancel_cb() inNode = max(0, p.widget.inNode) # close port editor if p.objEditor: p.objEditor.Cancel() self.deletePort(p, resize=False, updateSignature=False) for p in self.outputPorts[:]: if p.dataView is not None: # kill data viewer window p.dataView.destroy() if p.objEditor: p.objEditor.Cancel() self.deletePort(p, resize=False, updateSignature=False) for p in self.specialInputPorts[:]: self.deletePort(p, resize=False, updateSignature=False) for p in self.specialOutputPorts[:]: self.deletePort(p, resize=False, updateSignature=False) # delete the node's param. panel self.paramPanel.destroy() if self.objEditor: self.objEditor.Dismiss() if self.isExpanded() and inNode: self.hideInNodeWidgets( ) self.deleteIcon() self.afterRemovingFromNetwork() def addSaveNodeMenuEntries(self): """add 'save source code' and 'add to library' entries to node menu'""" if self.readOnly: return try: self.menu.index('Save as customized node') except: self.menu.add_separator() funcDependent = CallBackFunction(self.saveSource_cb, True) funcIndependent = CallBackFunction(self.saveSource_cb, False) if hasattr(self, 'geoms') is False: if issubclass(self.__class__, FunctionNode): pass # still in devellopment: #self.menu.add_command( # label='Save as customized node inheriting', # command=funcDependent) else: self.menu.add_command( label='Save as customized node', command=funcIndependent) ## PLEASE NOTE: This will be enabled in a future release of Vision ## ed = self.network.getEditor() ## if hasattr(ed, 'addNodeToLibrary'): ## fun = CallBackFunction( ed.addNodeToLibrary, self) ## self.menu.add_command(label='add to library', command=fun) def copy_cb(self, event=None): ed = self.network.getEditor() self.network.selectNodes([self]) ed.copyNetwork_cb(event) def cut_cb(self, event=None): ed = self.network.getEditor() self.network.selectNodes([self]) ed.cutNetwork_cb(event) def delete_cb(self, event=None): self.network.selectNodes([self]) nodeList = self.network.selectedNodes[:] self.network.deleteNodes(nodeList) def edit(self, event=None): if self.objEditor: self.objEditor.top.master.lift() return self.objEditor = NodeEditor(self) def editComputeFunction_cb(self, event=None): if not self.objEditor: self.objEditor = NodeEditor(self) self.objEditor.editButton.invoke() def evalString(self, str): if not str: return try: function = eval("%s"%str) except: #try: obj = compile(str, '', 'exec') if self.__module__ == '__main__': d = globals() else: # import the module from which this node comes mn = self.__module__ m = __import__(mn) # get the global dictionary of this module ind = string.find(mn, '.') if ind==-1: # not '.' was found d = eval('m'+'.__dict__') else: d = eval('m'+mn[ind:]+'.__dict__') # use the module's dictionary as global scope exec(obj, d) # when a function has names arguments it seems that the # co_names is (None, 'functionName') if len(obj.co_names)==0: function = None else: function = eval(obj.co_names[-1], d) #except: # raise ValueError return function def move(self, dx, dy, absolute=True, tagModified=True): """if absolute is set to False, the node moves about the increment dx,dy. If absolute is True, the node moves to the position dx,dy Connections are updated automatically.""" if self.editor.hasGUI: self.network.moveSubGraph([self], dx, dy, absolute=absolute, tagModified=tagModified) def getSourceCode(self): # this method is implemented in subclasses # create the source code to rebuild this object # used for saving or copying pass def toggleParamPanel_cb(self, event=None): if self.paramPanel.master.winfo_ismapped() == 0: self.paramPanel.show() self.showParams_cb() else: self.paramPanel.hide() def ensureRootNode(self): # count parent to decide whether or not second node is a root lInConnections = self.getInConnections() if len(lInConnections) == 0: self.isRootNode = 1 if self not in self.network.rootNodes: self.network.rootNodes.append(self) else: for lConn in lInConnections: if lConn.blocking is True: if self in self.network.rootNodes: self.network.rootNodes.remove(self) break; else: # we didn't break # all the connections are not blocking self.isRootNode = 1 if self not in self.network.rootNodes: self.network.rootNodes.append(self) class NetworkNode(NetworkNodeBase): """This class implements a node that is represented using a Polygon """ def __init__(self, name='NoName', sourceCode=None, originalClass=None, constrkw=None, library=None, progbar=0, **kw): apply( NetworkNodeBase.__init__, (self, name, sourceCode, originalClass, constrkw, library, progbar), kw) self.highlightOptions = {'highlightbackground':'red'} self.unhighlightOptions = {'highlightbackground':'gray50'} self.selectOptions = {'fill':'yellow'} self.deselectOptions = {'fill':'gray85'} self.inNodeWidgetsVisibleByDefault = True self.inputPortByName = {} self.outputPortByName = {} def replaceWithHost(self, klass=None, library=None, host='http://krusty.ucsd.edu:8081/opal2'): """a function to replace a node with another node. the connections are recreated. the connected ports must have the same name in the new node and in the original node. """ #print "replaceWithHost", self, host constrkw = copy.deepcopy(self.constrkw) if library is None: library = self.library if library is not None: constrkw['library'] = library if klass is None: klass = self.__class__ constrkw['klass'] = klass if klass is FunctionNode \ and hasattr(self.function, 'serviceName') \ and host is not None: constrkw['host'] = suppressMultipleQuotes(host) serverName = host.split('http://')[-1] serverName = serverName.split('/')[0] serverName = serverName.split(':')[0] serverName = serverName.replace('.','_') # to replace exactly with the same one #constrkw['functionOrString'] = \ # self.function.serviceOriginalName.lower() + '_' + serverName # to pick any better version if self.library.libraryDescr.has_key(host): lversion = 0 # replace with the highest version available on the host #lversion = self.function.version # replace only if version is at least equal to current # to eval we need to bring the main scope into this local scope from mglutil.util.misc import importMainOrIPythonMain lMainDict = importMainOrIPythonMain() for modItemName in set(lMainDict).difference(dir()): locals()[modItemName] = lMainDict[modItemName] del constrkw['functionOrString'] for node in self.library.libraryDescr[host]['nodes']: try: lFunction = eval(node.kw['functionOrString']) if lFunction.serviceName == self.function.serviceName \ and lFunction.version >= lversion: constrkw['functionOrString'] = lFunction.serviceOriginalName + '_' + serverName except: pass if constrkw.has_key('functionOrString'): return apply(self.replaceWith, (), constrkw) else: return False def replaceWith(self, klass=None, **kw): """a function to replace a node with another node. the connections are recreated. the connected ports must have the same name in the new node and in the original node. """ if len(kw) == 0: kw = copy.deepcopy(self.constrkw) if kw.has_key('library') is False: kw['library'] = self.library if klass is None: klass = self.__class__ try: lNewNode = apply(klass,(),kw) lNewNode.inNodeWidgetsVisibleByDefault = self.expandedIcon #by default we want the new node to be in the same state as the curren one self.network.addNode(lNewNode, posx=self.posx, posy=self.posy) if self.specialPortsVisible is True: self.showSpecialPorts() lFailure = False for port in self.inputPorts: if lFailure is False: for connection in port.connections: if lFailure is False: try: self.network.connectNodes( connection.port1.node, lNewNode, connection.port1.name, port.name, blocking=connection.blocking ) except: lFailure = True for port in self.inputPorts: if port.widget is not None: try: lNewNode.inputPortByName[port.name].widget.set(port.widget.get()) except: pass lDownstreamNodeInputPortSingleConnection = {} if lFailure is False: for port in self.outputPorts: if lFailure is False: # input ports downstream have to accept multiple connections otherwise they can't be connected for connection in port.connections: lDownstreamNodeInputPortSingleConnection[connection.port2.node] = \ (connection.port2.name, connection.port2.singleConnection) connection.port2.singleConnection = 'multiple' try: self.network.connectNodes( lNewNode, connection.port2.node, port.name, connection.port2.name, blocking=connection.blocking ) except: lFailure = True # input ports downstream are set back to what they were for lNode, portNameSingleConnection in lDownstreamNodeInputPortSingleConnection.items(): lNode.inputPortByName[portNameSingleConnection[0]].singleConnection = portNameSingleConnection[1] if lFailure is False: self.network.deleteNodes([self]) #print "replaced" return True else: self.network.deleteNodes([lNewNode]) except Exception, e: print e #warnings.warn( str(e) ) return False def createPorts(self): for kw in self.outputPortsDescr: kw['updateSignature'] = False # prevent recreating source code sig. op = self.addOutputPort(**kw) # create all inputPorts from description for kw in self.inputPortsDescr: kw['updateSignature'] = False # prevent recreating source code sig. ip = self.addInputPort(**kw) # create widgets ip.createWidget() # create all specialPorts self.addSpecialPorts() def isExpanded(self): """returns True if widgets inside the node as displayed""" return self.expandedIcon def editorVisible(self): """returns True if the node Editor is visible""" return self.objEditor is not None def getColor(self): if self.iconMaster is None: return return self.iconMaster.itemconfigure(self.innerBox)['fill'][-1] def setColor(self, color): ## FOR unknown reasons c.tk.call((c._w, 'itemcget', self.innerBox, '-fill') can return 'None' sometimes on SGI when using threads c = self.iconMaster if c is None: print 'Canvas is None' return oldCol = c.tk.call((c._w, 'itemcget', self.innerBox, '-fill') ) while oldCol=='None': print "//////////////////////////", oldCol,self.innerBox, c oldCol = c.tk.call((c._w, 'itemcget', self.innerBox, '-fill') ) print "\\\\\\\\\\\\\\\\\\\\\\",oldCol,self.innerBox, c #oldCol = c.itemconfigure(self.innerBox)['fill'][-1] c.tk.call((c._w, 'itemconfigure', self.innerBox, '-fill', color)) #c.itemconfigure(self.innerBox, fill=color) return oldCol ## OBSOLETE was used for nodes that were widgets ## ## def highlight(self, event=None): ## if self.iconMaster is None: return ## apply( self.iconMaster.itemconfigure, (self.innerBox,), ## self.highlightOptions ) ## def unhighlight(self, event=None): ## if self.iconMaster is None: return ## apply( self.iconMaster.itemconfigure, (self.innerBox,), ## self.unhighlightOptions ) def getFont(self): if self.iconMaster is None: return return self.iconMaster.itemconfigure(self.textId)['font'][-1] def setFont(self, font): # has to be a tuple like this: (ensureFontCase('helvetica'),'-12','bold') if self.iconMaster is None: return assert font is not None and len(font) font = tuple(font) self.iconMaster.itemconfig(self.textId, font=font) def select(self): NetworkItems.select(self) if self.iconMaster is None: return apply( self.iconMaster.itemconfigure, (self.innerBox,), self.selectOptions ) def deselect(self): NetworkItems.deselect(self) if self.iconMaster is None: return apply( self.iconMaster.itemconfigure, (self.innerBox,), self.deselectOptions ) def resizeIcon(self, dx=0, dy=0): if dx: self.growRight(self.innerBox, dx) self.growRight(self.outerBox, dx) self.growRight(self.lowerLine, dx) self.growRight(self.upperLine, dx) # move the special outputPort icons if visible if self.specialPortsVisible: for p in self.specialOutputPorts: p.deleteIcon() p.createIcon() if dy: self.growDown(self.innerBox, dy) self.growDown(self.outerBox, dy) self.growDown(self.lowerLine, dy) self.growDown(self.upperLine, dy) for p in self.outputPorts: p.relposy = p.relposy + dy p.deleteIcon() p.createIcon() for c in p.connections: if c.id: c.updatePosition() def addInputPort(self, name=None, updateSignature=True, _previousWidgetDescr=None, **kw): # ): defaults = { 'balloon':None, '_previousWidgetDescr':None, 'required':True, 'datatype':'None', 'width':None, 'height':None, 'singleConnection':True, 'beforeConnect':None, 'afterConnect':None, 'beforeDisconnect':None, 'afterDisconnect':None, 'shape':None, 'color':None, 'cast':True, 'originalDatatype':None, 'defaultValue':None, 'inputPortClass':InputPort, } defaults.update(kw) kw = defaults """Create input port and creates icon NOTE: this method does not update the description""" number = len(self.inputPorts) if name is None: name = 'in'+str(number) # create unique name portNames = [] for p in self.inputPorts: portNames.append(p.name) if name in portNames: i = number while (True): newname = name+str(i) if newname not in portNames: break i = i+1 name = newname # create port inputPortClass = kw.pop('inputPortClass', InputPort) #print 'ADD INPUT PORT', self.name, inputPortClass, kw ip = inputPortClass( name, self, **kw) # name, self, datatype, required, balloon, width, height, # singleConnection, beforeConnect, afterConnect, beforeDisconnect, # afterDisconnect, shape, color, cast=cast, # originalDatatype=originalDatatype, # defaultValue=defaultValue, **kw # ) self.inputPorts.append(ip) if self.iconMaster: ip.buildIcons() if not self.getEditor().hasGUI: ip.createWidget() # create NGWidget # and add descr to node.inputPortsDescr if it does not exist pdescr = self.inputPortsDescr found = False for d in pdescr: if d['name'] == name: found = True break if not found: descr = {'name':name, 'datatype':kw['datatype'], 'required':kw['required'], 'balloon':kw['balloon'], 'singleConnection':kw['singleConnection']} self.inputPortsDescr.append(descr) if _previousWidgetDescr is not None: ip.previousWidgetDescr = _previousWidgetDescr # generate unique number, which is used for saving/restoring ip._id = self._inputPortsID self._inputPortsID += 1 ip._setModified(True) ip._setOriginal(False) # change signature of compute function if updateSignature is True: self.updateCode(port='ip', action='add', tagModified=False) self.inputPortByName[name] = ip return ip def refreshInputPortData(self): d = {} for p in self.inputPorts: d[p.name] = p.getData() return d def addOutputPort(self, name=None, updateSignature=True, **kw): defaults = {'datatype':'None', 'width':None, 'height':None, 'balloon':None, 'beforeConnect':None, 'afterConnect':None, 'beforeDisconnect':None, 'afterDisconnect':None, 'shape':None, 'color':None} defaults.update(kw) kw = defaults """Create output port and creates icon NOTE: this method does not update the description nor the function's signature""" number = len(self.outputPorts) if name is None: name = 'out'+str(number) # create unique name portNames = [] for p in self.outputPorts: portNames.append(p.name) if name in portNames: i = number while (True): newname = name+str(i) if newname not in portNames: break i = i+1 name = newname # create port outputPortClass = kw.pop('outputPortClass', OutputPort) op = outputPortClass(name, self, **kw) #datatype, balloon, width, height, #beforeConnect, afterConnect, beforeDisconnect, #afterDisconnect) self.outputPorts.append(op) if self.iconMaster: op.buildIcons() # and add descr to node.outputPortsDescr if it does not exist pdescr = self.outputPortsDescr found = False for d in pdescr: if d['name'] == name: found = True break if not found: descr = {'name':name, 'datatype':kw['datatype'], 'balloon':kw['balloon']} self.outputPortsDescr.append(descr) # generate unique number, which is used for saving/restoring op._id = self._outputPortsID self._outputPortsID += 1 op._setModified(True) op._setOriginal(False) # add comment to code on how to output data on that port if updateSignature is True: self.updateCode(port='op', action='add', newname=op.name, tagModified=False) self.outputPortByName[name] = op return op def deletePort(self, p, resize=True, updateSignature=True): NetworkItems.deletePort(self, p, resize) # update code first, then delete if updateSignature and isinstance(p, InputPort): self.updateCode(port='ip', action='remove', tagModified=False) self.inputPortByName.pop(p.name) elif updateSignature and isinstance(p, OutputPort): self.updateCode(port='op', action='remove', newname='', oldname=p.name, tagModified=False) self.outputPortByName.pop(p.name) def deletePortByName(self, portName, resize=True, updateSignature=True): """delete a port by specifying a port name (port names are unique within a given node).""" port = self.findPortByName() self.deletePort(port, resize=resize, updateSignature=updateSignature) def showSpecialPorts(self, tagModified=True, event=None): self.specialPortsVisible = True self._setModified(tagModified) for p in self.specialOutputPorts: p.createIcon() for p in self.specialInputPorts: p.createIcon() self.menu.entryconfigure('Show special ports', label='Hide special ports', command=self.hideSpecialPorts) def hideSpecialPorts(self, tagModified=True, event=None): self.specialPortsVisible = False self._setModified(tagModified) for p in self.specialOutputPorts: p.node.network.deleteConnections(p.connections, undo=1) p.deleteIcon() for p in self.specialInputPorts: p.node.network.deleteConnections(p.connections, undo=1) p.deleteIcon() self.menu.entryconfigure('Hide special ports', label='Show special ports', command=self.showSpecialPorts) def addSpecialPorts(self): """add special ports to special ports list. But do not build icons""" # port to receive an impulse that will trigger the execution of the # node ip = RunNodeInputPort(self) ip.network = self.network self.specialInputPorts.append( ip ) # port that always output an impulse upon successful completion # of the node's function op = TriggerOutputPort(self) op.network = self.network self.specialOutputPorts.append( op ) ed = self.getEditor() ip.vEditor = weakref.ref( ed ) op.vEditor = weakref.ref( ed ) def buildSmallIcon(self, canvas, posx, posy, font=None): """build node proxy icon (icons in library categories""" if font is None: font = self.ed.font['LibNodes'] font = tuple(font) self.textId = canvas.create_text( posx, posy, text=self.name, justify=Tkinter.CENTER, anchor='w', tags='node', font=font) self.iconTag = 'node'+str(self.textId) bb = canvas.bbox(self.textId) # adding the self.id as a unique tag for this node canvas.addtag_closest(self.iconTag, posx, posy, start=self.textId) bdx1 = 2 # x padding around label bdy1 = 0 # y padding around label bdx2 = bdx1+3 # label padding + relief width bdy2 = bdy1+3 # label padding + relief width self.innerBox = canvas.create_rectangle( bb[0]-bdx1, bb[1]-bdy1, bb[2]+bdx1, bb[3]+bdy1, tags=(self.iconTag,'node'), fill='gray85') # the innerBox is the canvas item used to designate this node self.id = self.innerBox # add a shadow below if self.library is not None: color1 = self.library.color else: color1 = 'gray95' # upper right triangle self.upperLine = canvas.create_polygon( bb[0]-bdx2, bb[1]-bdy2, bb[0]-bdx1, bb[1]-bdy1, bb[2]+bdx1, bb[3]+bdy1, bb[2]+bdx2, bb[3]+bdy2, bb[2]+bdx2, bb[1]-bdy2, width=4, tags=(self.iconTag,'node'), fill=color1 ) # lower left triangle self.lowerLine = canvas.create_polygon( bb[0]-bdx2, bb[1]-bdy2, bb[0]-bdx1, bb[1]-bdy1, bb[2]+bdx1, bb[3]+bdy1, bb[2]+bdx2, bb[3]+bdy2, bb[0]-bdx2, bb[3]+bdy2, width=4, tags=(self.iconTag,'node'), fill='gray45' ) self.outerBox = canvas.create_rectangle( bb[0]-bdx2, bb[1]-bdy2, bb[2]+bdx2, bb[3]+bdy2, width=1, tags=(self.iconTag,'node')) canvas.tag_raise(self.innerBox, self.outerBox) canvas.tag_raise(self.textId, self.innerBox) return bb def deleteSmallIcon(self, canvas, item): # Experimental! node = item.dummyNode canvas.delete(node.textId) canvas.delete(node.innerBox) canvas.delete(node.outerBox) canvas.delete(node.iconTag) def buildNodeIcon(self, canvas, posx, posy, small=False): # build a frame that will hold all widgets in node if hasattr(self.iconMaster,'tk'): self.nodeWidgetMaster = Tkinter.Frame( self.iconMaster, borderwidth=3, relief='sunken' , bg='#c3d0a6') ed = self.getEditor() if small is True: font = tuple(ed.font['LibNodes']) lInner = 2 lOuter = 4 else: font = tuple(ed.font['Nodes']) lInner = 5 lOuter = 8 self.textId = canvas.create_text( posx, posy, text=self.name, justify=Tkinter.CENTER, anchor='w', tags='node', font=font) canvas.tag_bind(self.textId, "", self.setLabel_cb) self.iconTag = 'node'+str(self.textId) # add self.iconTag tag to self.textId canvas.itemconfig(self.textId, tags=(self.iconTag,'node')) bb = canvas.bbox(self.textId) ## # NOTE: THIS LINE ADDS RANDOMLY WRONG TAGS TO NODES >>>> COPY/PASTE ## # WON'T WORK CORRECTLY!!! WITHOUT THIS LINE, EVERYTHING SEEMS TO ## # WORK FINE. ## #adding the self.id as a unique tag for this node ## canvas.addtag_closest(self.iconTag, posx, posy, start=self.textId) progBarH = self.progBarH # this method is also called by a network refresh, thus we need to # get the description of the node and color it accordingly (frozen # or colored by node library) color = "gray85" # default color is gray if self.editor.colorNodeByLibraryTk.get() == 1: if self.library is not None: color = self.library.color # color by node library color # if node is frozen, this overwrites everything if self.frozen: color = '#b6d3f6' # color light blue self.innerBox = canvas.create_rectangle( bb[0]-lInner, bb[1]-lInner, bb[2]+lInner, bb[3]+lInner+progBarH, tags=(self.iconTag,'node'), fill=color)#, width=2 ) # the innerBox is the canvas item used to designate this node self.id = self.innerBox # add a shadow below (color by Library) if self.library is not None: color1 = self.library.color else: color1 = 'gray95' self.outerBox = canvas.create_rectangle( bb[0]-lOuter, bb[1]-lOuter, bb[2]+lOuter, bb[3]+lOuter+progBarH, width=1, tags=(self.iconTag,'node')) # get a shortcut to the bounding boxes used later on ibb = canvas.bbox(self.innerBox) obb = canvas.bbox(self.outerBox) # upper right polygon (this is used to color the node icons upper # and right side with the corresponding node library color) self.upperLine = canvas.create_polygon( # note: we have to compensate +1 and -1 because of '1'-based # coord system obb[0]+1, obb[1]+1, ibb[0]+1, ibb[1]+1, ibb[2]-1, ibb[3]-1, obb[2]-1, obb[3]-1, obb[2]-1, obb[1]+1, width=4, tags=(self.iconTag,'node'), fill=color1 ) # lower left polygon (this is used to 'shade' the node icons lower # and right side with a dark grey color to give it a 3-D impression self.lowerLine = canvas.create_polygon( # note: we have to compensate +1 and -1 because of '1'-based # coord system obb[0]+1, obb[1]+1, ibb[0]+1, ibb[1]+1, ibb[2]-1, ibb[3]-1, obb[2]-1, obb[3]-1, obb[0]+1, obb[3]-1, width=4, tags=(self.iconTag,'node'), fill='gray45' ) canvas.tag_raise(self.outerBox) canvas.tag_raise(self.innerBox, self.outerBox) canvas.tag_raise(self.textId, self.innerBox) # add the progress bar if self.hasProgBar: pbid1 = canvas.create_rectangle( bb[0]-3, bb[3]-2, bb[2]+3, bb[3]+1+progBarH, tags=(self.iconTag,'node'), fill='green') self.pbid1 = pbid1 pbid2 = canvas.create_rectangle( bb[2]+3, bb[3]-2, bb[2]+3, bb[3]+1+progBarH, {'tags':(self.iconTag,'node'), 'fill':'red'} ) self.pbid2 = pbid2 # and set posx, posy self.updatePosXPosY(posx, posy) if self.network is not None: self.move(posx, posy) self.hasMoved = False # reset this attribute because the current # position is now the original position def setLabel_cb(self, event): self._tmproot = root = Tkinter.Toplevel() root.transient() root.geometry("+%d+%d"%root.winfo_pointerxy()) root.overrideredirect(True) self._tmpEntry = Tkinter.Entry(root) self._tmpEntry.pack() self._tmpEntry.bind("", self.setNewLabel_cb) def setNewLabel_cb(self, event): name = self._tmpEntry.get() self.rename(name) self._tmpEntry.destroy() self._tmproot.destroy() def setProgressBar(self, percent): """update node's progress bar. percent should be between 0.0 and 1.0""" if not self.hasProgBar: return canvas = self.iconMaster c = canvas.coords(self.pbid1) c[0] = c[0] + (c[2]-c[0])*percent canvas.coords(self.pbid2, c[0], c[1], c[2], c[3]) def updatePosXPosY(self, dx=None, dy=None): """set node.posx and node.posy after node has been moved""" bbox = self.iconMaster.bbox(self.outerBox) self.posx = bbox[0] self.posy = bbox[1] def setModifiedTag(self): """THIS METHOD REMAINS FOR BACKWARDS COMPATIBILITY WITH OLD NETWORKS! Sets self._modified=True""" self._setModified(True) class NetworkConnection(NetworkItems): """This class implements a connection between nodes, drawing lines between the centers of 2 ports. The mode can be set to 'straight' or 'angles' to have a straight line or lines using only right angles. smooth=1 option can be used for splines joinstyle = 'bevel', 'miter' and 'round' """ arcNum = 0 def __init__(self, port1, port2, mode='straight', name=None, blocking=None, smooth=False, splitratio=None, hidden=False, **kw): if name is None: name = port1.node.name+'('+port1.name+')'+'_'+port2.node.name+'('+port2.name+')' if splitratio is None: splitratio=[random.uniform(.2,.75), random.uniform(.2,.75)] # was [.5, .5] NetworkItems.__init__(self, name) self.hidden = hidden self.id2 = None self.iconTag2 = None self.port1 = port1 self.port2 = port2 port1.children.append(port2) #assert self not in port1.connections port1.connections.append(self) port1.node.children.append(port2.node) port2.parents.append(port1) #assert self not in port2.connections port2.connections.append(self) port2.node.parents.append(port1.node) leditor = self.port1.editor if blocking is None: blocking = leditor.createBlockingConnections self.blocking = blocking # when true a child node can not run before # the parent node has run self.mode = mode if leditor is not None and hasattr(leditor, 'splineConnections'): self.smooth = leditor.splineConnections else: self.smooth = smooth self.splitratio = copy.deepcopy(splitratio) w = self.connectionWidth = 3 if port1.node.getEditor().hasGUI: col = port1.datatypeObject['color'] if not kw.has_key('arrow'): kw['arrow']='last' if not kw.has_key('fill'): kw['fill']=col if not kw.has_key('width'): kw['width']=w if not kw.has_key('width'): kw['width']=w if not kw.has_key('activefill'): kw['activefill']='pink' kw['smooth'] = self.smooth self.lineOptions = kw self.highlightOptions = {'fill':'red', 'width':w, 'arrow':'last'} self.unhighlightOptions = {'width':w, 'arrow':'last'} self.unhighlightOptions['fill'] = col self.selectOptions = { 'connection0': {'fill':'blue', 'width':w, 'arrow':'last'}, 'connection1': {'fill':'pink', 'width':w, 'arrow':'last'}, 'connection2': {'fill':'purple', 'width':w, 'arrow':'last'}, } self.deselectOptions = {'width':w, 'arrow':'last'} self.deselectOptions['fill'] = col self.mouseAction[''] = self.reshapeConnection self.parentMenu = None self.isBlockingTk = Tkinter.IntVar() self.isBlockingTk.set(self.blocking) if isinstance(port1.node, NetworkNode) and isinstance(port2.node, NetworkNode): self._mode = 1 else: self._mode = 2 def reshapeConnection(self, event): # get a handle to the network of this node c = self.iconMaster # register an additional function to reshape connection num = event.num # FIXME looks like I am binding this many times ! c.bind(""%num, self.moveReshape,'+') c.bind(""%num, self.moveEndReshape, '+') ## def moveReshape(self, event): ## c = self.iconMaster ## y = c.canvasy(event.y) ## dy = y - self.network.lasty ## self.network.lasty = y ## coords = c.coords(self.iconTag) ## coords[3] = coords[3]+dy ## coords[5] = coords[5]+dy ## apply( c.coords, (self.iconTag,)+tuple(coords) ) ## def moveEndReshape(self, event): ## c = self.iconMaster ## num = event.num ## c.unbind(""%num) ## c.bind(""%num, self.moveEndReshape, '+') # patch from Karl Gutwin 2003-03-27 16:05 def moveReshape(self, event): #print "moveReshape" c = self.iconMaster y = c.canvasy(event.y) x = c.canvasx(event.x) dy = y - self.network.lasty dx = x - self.network.lastx self.network.lasty = y self.network.lastx = x coords = c.coords(self.iconTag) if len(coords)==12: coords[4] = coords[4]+dx coords[6] = coords[6]+dx if y > ((coords[5]+coords[7])/2): coords[3] = coords[3]+dy coords[5] = coords[5]+dy else: coords[7] = coords[7]+dy coords[9] = coords[9]+dy else: coords[3] = coords[3]+dy coords[5] = coords[5]+dy self.calculateNewSplitratio(coords) apply( c.coords, (self.iconTag,)+tuple(coords) ) def calculateNewSplitratio(self, coords): self.splitratio[0] = coords[0]-coords[4] lDistance = coords[0]-coords[-2] if lDistance != 0: self.splitratio[0] /= float(lDistance) if self.splitratio[0] > 2: self.splitratio[0] = 2 elif self.splitratio[0] < -2: self.splitratio[0] = -2 self.splitratio[1] = coords[1]-coords[5] lDistance = coords[1]-coords[-1] if lDistance != 0: self.splitratio[1] /= float(lDistance) if self.splitratio[1] > 2: self.splitratio[1] = 2 elif self.splitratio[1] < -2: self.splitratio[1] = -2 def moveEndReshape(self, event): c = self.iconMaster num = event.num c.unbind(""%num) c.unbind(""%num) def highlight(self, event=None): if self.iconMaster is None: return c = self.iconMaster apply( c.itemconfigure, (self.iconTag,), self.highlightOptions ) def unhighlight(self, event=None): if self.iconMaster is None: return c = self.iconMaster apply( c.itemconfigure, (self.iconTag,), self.unhighlightOptions) def setColor(self, color): if self.iconMaster is None: return c = self.iconMaster apply( c.itemconfigure, (self.iconTag,), {'fill':color} ) self.unhighlightOptions['fill'] = color self.deselectOptions['fill'] = color def getColor(self): return self.deselectOptions['fill'] def select(self): self.selected = 1 if self.iconMaster is None: return sum = self.port1.node.selected + self.port2.node.selected if sum==2: self.iconMaster.addtag('selected', 'withtag', self.iconTag) apply( self.iconMaster.itemconfigure, (self.iconTag,), self.selectOptions['connection%d'%sum] ) def deselect(self): NetworkItems.deselect(self) if self.iconMaster is None: return sum = self.port1.node.selected + self.port2.node.selected if sum<2: self.iconMaster.dtag(self.iconTag, 'selected') if sum==0: opt = self.deselectOptions else: opt = self.selectOptions['connection%d'%sum] apply( self.iconMaster.itemconfigure, (self.iconTag,), opt ) def shadowColors(self, colorTk): # for a given Tkcolor return a dark tone 40% and light tone 80% c = self.iconMaster maxi = float(c.winfo_rgb('white')[0]) rgb = c.winfo_rgb(colorTk) base = ( rgb[0]/maxi*255, rgb[1]/maxi*255, rgb[2]/maxi*255 ) dark = "#%02x%02x%02x"%(base[0]*0.6,base[1]*0.6,base[2]*0.6) light = "#%02x%02x%02x"%(base[0]*0.8,base[1]*0.8,base[2]*0.8) return dark, light def toggleBlocking_cb(self, event=None): self.blocking = not self.blocking self.isBlockingTk.set(self.blocking) if not self.blocking: self.port2.node.ensureRootNode() def toggleVisibility_cb(self, event=None): self.setVisibility(not self.hidden) def setVisibility(self, hidden): self.hidden = hidden del self.network.connById[self.id] if self.id2 is not None: del self.network.connById[self.id2] self.deleteIcon() self.buildIcons(self.network.canvas) self.network.connById[self.id] = self if self.id2 is not None: self.network.connById[self.id2] = self def reparent_cb(self, type): node = self.port2.node self.network.deleteConnections([self]) node.reparentGeomType(type, reparentCurrent=False) def drawMapIcon(self, naviMap): if self.id is None: # geom nodes with aprentNode2 seen to create return # conenctions with no id mapCanvas = naviMap.mapCanvas x0, y0 = naviMap.upperLeft scaleFactor = naviMap.scaleFactor canvas = self.iconMaster cc = self.network.canvas.coords(self.id) nc = [] for i in range(0, len(cc), 2): nc.append( x0+cc[i]*scaleFactor ) nc.append( y0+cc[i+1]*scaleFactor ) if self.naviMapID is None: cid = mapCanvas.create_line( *nc, tag=('navimap',)) self.naviMapID = cid return cid else: mapCanvas.coords(self.naviMapID, *nc) def updatePosition(self): if self.iconMaster is None: return # spoted by guillaume, I am not sure what it means if self.port1 is None or self.port2 is None: import traceback traceback.print_stack() print c, id(id) print 'IT HAPPENED AGAIN: a conection is missing ports' return if self.port1.id is None or self.port2.id is None: return # one the ports is not visible c = self.iconMaster coords = self.getLineCoords() if self.hidden is False: apply( c.coords, (self.id,)+tuple(coords) ) else: if isinstance(self.port1, SpecialOutputPort): lcoords1 = (coords[0],coords[1],coords[0]+20,coords[1]) lcoords2 = (coords[-2]-16,coords[-1],coords[-2],coords[-1]) else: lcoords1 = (coords[0],coords[1],coords[0],coords[1]+20) lcoords2 = (coords[-2],coords[-1]-16,coords[-2],coords[-1]) apply( c.coords, (self.id,)+tuple(lcoords1) ) apply( c.coords, (self.id2,)+tuple(lcoords2) ) naviMap = self.port1.node.network.naviMap self.drawMapIcon(naviMap) def getLineCoords(self): canvas = self.iconMaster c1 = self.port1.getCenterCoords() c2 = self.port2.getCenterCoords() n1 = self.port1.node n2 = self.port2.node if self._mode==1: if self.mode == 'straight': outOffy = c1[1]+15 inOffy = c2[1]-15 return [ c1[0], c1[1], c1[0], outOffy, c2[0], inOffy, c2[0], c2[1] ] else: # if self.mode == 'angles': dy = c2[1]-c1[1] if dy > 30: # draw just 1 segment down, 1 horizontal and 1 down again dy2 = dy * self.splitratio[1] outOffy = c1[1]+dy2 return [ c1[0], c1[1], c1[0], outOffy, c2[0], outOffy, c2[0], c2[1] ] else: outOffy = c1[1]+15 # go down 15 pixels from output inOffy = c2[1]-15 # go up 15 pixel from input dx = c2[0]-c1[0] dx2 = dx * self.splitratio[0] mid = [ c1[0]+dx2, outOffy, c2[0]-(dx-dx2), inOffy ] return [ c1[0], c1[1], c1[0], outOffy ] + mid + \ [ c2[0], inOffy, c2[0], c2[1] ] else: vx1, vy1 = self.port1.vectorRotated vy1 = -vy1 vx2, vy2 = self.port2.vectorRotated vy2 = -vy2 if self.mode == 'straight': outOffy = c1[0]+15*vx1 inOffy = c2[0]-15*vy1 return [ c1[0], c1[1], c1[0], outOffy, c2[0], inOffy, c2[0], c2[1] ] else: # if self.mode == 'angles': dx = c2[0]-c1[0] # check if port vectors are opposite dot = vx1*vx2 + vy1*vy2 if dot < 0.0: # 3 segments, 2 projecting out of node and 1 joining them # draw 1 segment along p1.vector # 1 segment along -p2.vector and a segement joing them proj = 20 p1x = c1[0]+proj*vx1 p1y = c1[1]+proj*vy1 p2x = c2[0]+proj*vx2 p2y = c2[1]+proj*vy2 #print 'getLineCoords A',c1[0], c1[1], p1x, p1y, p2x, p2y, c2[0], c2[1] #print 'A', c1, c2, vx1, vy1, vx2, vy2 return [ c1[0], c1[1], p1x, p1y, p2x, p2y, c2[0], c2[1] ] else: proj = 20 p1x = c1[0]+proj*vx1 # move up p1y = c1[1]+proj*vy1 perp = -vy1, vx1 # check that perpendicular vector point from on port to the other ppvx = c2[0]-c1[0] # vector form port1 to port2 ppvy = c2[1]-c1[1] dot = ppvx*perp[0] + ppvy*perp[1] if dot>0.0: sign = 1.0 else: sign = -1.0 p2x = p1x+proj*sign*perp[0] # move side ways a bit p2y = p1y+proj*sign*perp[1] p3x = c2[0]+proj*vx2 p3y = c2[1]+proj*vy2 #print 'B', c1, c2, vx1, vy1, vx2, vy2, perp #print 'getLineCoords B', c1[0], c1[1], p1x, p1y, p2x, p2y, p3x, p3y,c2[0], c2[1] return [ c1[0], c1[1], p1x, p1y, p2x, p2y, p3x, p3y, c2[0], c2[1] ] ## def getLineCoords(self): ## if isinstance(self.port1, SpecialOutputPort): ## return self.getLineCoordsLeftRightPorts() ## elif isinstance(self.port1, ImageOutputPort): ## return self.getLineCoordsLeftRightPorts() ## else: ## return self.getLineCoordsTopBottomPorts() ## def getLineCoordsLeftRightPorts(self): ## canvas = self.iconMaster ## c1 = self.port1.getCenterCoords() ## c2 = self.port2.getCenterCoords() ## if self.mode == 'straight': ## outOffy = c1[0]+15 ## inOffy = c2[0]-15 ## return [ c1[0], c1[1], c1[0], outOffy, ## c2[0], inOffy, c2[0], c2[1] ] ## else: # if self.mode == 'angles': ## dx = c2[0]-c1[0] ## if dx > 30: # draw just 1 segment down, 1 horizontal and 1 down again ## dx2 = dx * self.splitratio[0] ## outOffx = c1[0]+dx2 ## inOffx = c2[0]-(dx-dx2) ## return [ c1[0], c1[1], outOffx, c1[1], inOffx, c2[1], ## c2[0], c2[1] ] ## else: ## outOffx = c1[0]+15 # go right 15 pixels from output ## inOffx = c2[0]-15 # go left 15 pixel from input ## dy = c2[1]-c1[1] ## dy2 = dy * self.splitratio[1] ## mid = [ outOffx, c1[1]+dy2, inOffx, c2[1]-(dy-dy2) ] ## return [ c1[0], c1[1], outOffx, c1[1] ] + mid + \ ## [ inOffx, c2[1], c2[0], c2[1] ] ## def getLineCoordsTopBottomPorts(self): ## # implements straight and angle connections between nodes ## canvas = self.iconMaster ## c1 = self.port1.getCenterCoords() ## c2 = self.port2.getCenterCoords() ## if self.mode == 'straight': ## outOffy = c1[1]+15 ## inOffy = c2[1]-15 ## return [ c1[0], c1[1], c1[0], outOffy, ## c2[0], inOffy, c2[0], c2[1] ] ## else: # if self.mode == 'angles': ## dy = c2[1]-c1[1] ## if dy > 30: # draw just 1 segment down, 1 horizontal and 1 down again ## dy2 = dy * self.splitratio[1] ## outOffy = c1[1]+dy2 ## return [ c1[0], c1[1], c1[0], outOffy, c2[0], ## outOffy, c2[0], c2[1] ] ## else: ## outOffy = c1[1]+15 # go down 15 pixels from output ## inOffy = c2[1]-15 # go up 15 pixel from input ## dx = c2[0]-c1[0] ## dx2 = dx * self.splitratio[0] ## mid = [ c1[0]+dx2, outOffy, c2[0]-(dx-dx2), inOffy ] ## return [ c1[0], c1[1], c1[0], outOffy ] + mid + \ ## [ c2[0], inOffy, c2[0], c2[1] ] def buildIcons(self, canvas): """Build CONNECTION icon """ NetworkItems.buildIcons(self, canvas) kw = self.lineOptions arcTag = '__arc'+str(self.arcNum) self.arcNum = self.arcNum + 1 kw['tags'] = ('connection', arcTag) coords = self.getLineCoords() if self.hidden is False: g = apply( canvas.create_line, tuple(coords), kw ) else: #print "coords", coords if isinstance(self.port1, SpecialOutputPort): lcoords1 = (coords[0],coords[1],coords[0]+20,coords[1]) lcoords2 = (coords[-2]-16,coords[-1],coords[-2],coords[-1]) else: lcoords1 = (coords[0],coords[1],coords[0],coords[1]+20) lcoords2 = (coords[-2],coords[-1]-16,coords[-2],coords[-1]) g = apply( canvas.create_line, tuple(lcoords1), kw ) g2 = apply( canvas.create_line, tuple(lcoords2), kw ) self.iconTag2 = 'conn'+str(g2) self.id2 = g2 self.iconTag = 'conn'+str(g) self.id = g cb = CallBackFunction(self.network.deleteConnections, ([self])) if self.port2.name == 'parent' and hasattr(self.port2.node,'selectedGeomIndex'): # i.e. it's a geometry node cbSiblings = CallBackFunction(self.reparent_cb, ('siblings')) cbAll = CallBackFunction(self.reparent_cb, ('all')) self.menu.add_command(label='delete / reparent to root', command=cb) self.menu.add_command(label='reparent pointed siblings to root', command=cbSiblings) self.menu.add_command(label='reparent all pointed geoms to root', command=cbAll) else: self.menu.add_command(label='delete', command=cb) if self.hidden is False: self.menu.add_command(label='hide', command=self.toggleVisibility_cb) else: self.menu.add_command(label='show', command=self.toggleVisibility_cb) self.menu.add_checkbutton(label='blocking', variable = self.isBlockingTk, command=self.toggleBlocking_cb) # adding the self.id as a unique tag for this node canvas.addtag_withtag(self.iconTag, arcTag ) if self.hidden is True: canvas.addtag_withtag(self.iconTag2, arcTag ) canvas.dtag( arcTag ) canvas.lower(g, 'node') def getSourceCode(self, networkName, selectedOnly=0, indent="", ignoreOriginal=False, connName='conn'): # build and return connection creation source code from NetworkEditor.ports import TriggerOutputPort lines = [] conn = self if conn._original is True and ignoreOriginal is False: return lines if selectedOnly and \ conn.port1.node.selected+conn.port2.node.selected < 2: return lines node1 = conn.port1.node node2 = conn.port2.node n1Name = node1.getUniqueNodeName() n2Name = node2.getUniqueNodeName() lines = node1.checkIfNodeForSavingIsDefined(lines, networkName, indent) lines = node2.checkIfNodeForSavingIsDefined(lines, networkName, indent) lines.append(indent+'if %s is not None and %s is not None:\n'%( n1Name, n2Name)) if isinstance(conn.port1, TriggerOutputPort): line1 = networkName+".specialConnectNodes(\n" else: line1 = '%s = '%connName +networkName+".connectNodes(\n" ## line = line + "%s, %s, %d, %d)\n"%(n1Name, n2Name, ## conn.port1.number, conn.port2.number) port1Name = conn.port1.name port2Name = conn.port2.name from macros import MacroInputNode, MacroOutputNode # treat connections to MacroInputNode separately if isinstance(conn.port1.node, MacroInputNode): if len(conn.port1.connections) > 1: i = 0 for c in conn.port1.connections: if c == conn: break else: i = i + 1 if i == 0: port1Name = 'new' else: port1Name = 'new' # treat connections to MacroOutpuNode separately if isinstance(conn.port2.node, MacroOutputNode): if len(conn.port2.connections) > 1: i = 0 for c in conn.port2.connections: if c == conn: break else: i = i + 1 if i == 0: port2Name = 'new' else: port2Name = 'new' line2 = '%s, %s, "%s", "%s", blocking=%s\n'%( n1Name, n2Name, port1Name, port2Name, conn.blocking) line3 = '' if conn.splitratio != [.5,.5]: line3 = ', splitratio=%s'%(conn.splitratio) if self.hidden is True: line3 += ', hidden=True' line3 += ')\n' lines.append(indent + ' '*4 + 'try:\n') lines.append(indent + ' '*8 + line1) lines.append(indent + ' '*12 + line2) lines.append(indent + ' '*12 + line3) lines.append(indent + ' '*4 + 'except:\n') lines.append(indent + ' '*8 + \ 'print "WARNING: failed to restore connection between %s and %s in network %s"\n'%(n1Name,n2Name,networkName)) lines.extend(node1.customizeConnectionCode(self, connName, indent + ' '*4)) lines.extend(node2.customizeConnectionCode(self, connName, indent + ' '*4)) return lines def destroyIcon(self): self.deleteIcon() self.id = None self.iconMaster = None self.id2 = None self.network.canvas.delete(self.iconTag2) self.iconTag2 = None self.naviMapID = None class FunctionNode(NetworkNode): """ Base node for Vsiion nodes exposing a function or callable object The RunFunction node is an example of subclassing this node. Opal web services nodes are instance of this node exposing the opal web service python wrapper callable object This object support creating input ports for all parameters to the function. Positional (i.e. without default value) arguments always generate an input port visible on the node. For named arguments arguments a widget is created base on the type of the default value (e.g. entry for string, dial for int and float etc.) If the function or callable object has a .params attribute this attribute is expected to be a dictionary where the key is the name of the argument and the\ value is dictionary providing additional info about this parameter. the folloing keys are recognized in this dictionary: {'default': 'False', # default value (not used as it is taken from the function signature) 'type': 'boolean', 'description': 'string' #use to create tooltip 'ioType': 'INPUT', # can be INPUT, INOUT, } if type is FILE a a file browser will be generated if type is selection a values keywords should be present and provide a list of possible values that will be made available in a combobox widget """ codeBeforeDisconnect = """def beforeDisconnect(self, c): # upon disconnecting we want to set the attribute function to None c.port2.node.function = None # remove all ports beyond the 'function' and 'importString' input ports for p in c.port2.node.inputPorts[2:]: c.port2.node.deletePort(p) """ def passFunction(): pass passFunc = passFunction def __init__(self, functionOrString=None, importString=None, posArgsNames=[], namedArgs={}, **kw): if functionOrString is not None or kw.has_key('functionOrString') is False: kw['functionOrString'] = functionOrString elif kw.has_key('functionOrString') is True: functionOrString = kw['functionOrString'] if importString is not None or kw.has_key('importString') is False: kw['importString'] = importString elif kw.has_key('importString') is True: importString = kw['importString'] if len(posArgsNames)>0 or kw.has_key('posArgsNames') is False: kw['posArgsNames'] = posArgsNames elif kw.has_key('posArgsNames') is True: posArgsNames = kw['posArgsNames'] if len(namedArgs)>0 or kw.has_key('namedArgs') is False: kw['namedArgs'] = namedArgs elif kw.has_key('namedArgs') is True: namedArgs = kw['namedArgs'] if type(functionOrString) == types.StringType: # we add __main__ to the scope of the local function # the folowing code is similar to: "from __main__ import *" # but it doesn't raise any warning, and its probably more local # and self and in1 are still known in the scope of the eval function from mglutil.util.misc import importMainOrIPythonMain lMainDict = importMainOrIPythonMain() for modItemName in set(lMainDict).difference(dir()): locals()[modItemName] = lMainDict[modItemName] if importString is not None: try: lImport = eval(importString) if lImport == types.StringType: importString = lImport except: pass exec(importString) if kw.has_key('masternet') is True: masterNet = kw['masternet'] lfunctionOrString = functionOrString while type(lfunctionOrString) == types.StringType: try: function = eval(lfunctionOrString) except NameError: function = None lfunctionOrString = function else: function = functionOrString if function is not None and kw.has_key('library'): # so we know where to find the current editor function._vpe = kw['library'].ed function._node = self # so we can find the vision node if hasattr(function, 'params') and type(function.params) == types.DictType: argsDescription = function.params else: argsDescription = {} if inspect.isclass(function) is True: try: function = function() except: function = None if function is None: #def testFunction(a, b=1): # print 'testFunction', a, b # return a, b function = self.passFunc if hasattr(function, 'name'): name = function.name elif hasattr(function, '__name__'): name = function.__name__ else: name = function.__class__.__name__ kw['name'] = name apply( NetworkNode.__init__, (self,), kw ) self.function = function # function or command to be called self.posArgsNames = posArgsNames self.namedArgs = namedArgs # dict: key: arg name, value: arg default self.outputPortsDescr.append(datatype='None', name='result') #for key, value in outputDescr: # self.outputPortsDescr.append(datatype=value, name=key) # get arguments description from inspect import getargspec if hasattr(function, '__call__') and hasattr(function.__call__, 'im_func'): args = getargspec(function.__call__.im_func) else: args = getargspec(function) if len(args[0])>0 and args[0][0] == 'self': args[0].pop(0) # get rid of self allNames = args[0] defaultValues = args[3] if defaultValues is None: defaultValues = [] nbNamesArgs = len(defaultValues) if nbNamesArgs > 0: self.posArgsNames = args[0][:-nbNamesArgs] else: self.posArgsNames = args[0] d = {} for name, val in zip(args[0][-nbNamesArgs:], defaultValues): d[name] = val self.namedArgs = d # create widgets and ports for named arguments self.buildPortsForPositionalAndNamedArgs(self.posArgsNames, self.namedArgs, argsDescription=argsDescription) # create the constructor arguments such that when the node is restored # from file it will have all the info it needs if functionOrString is not None \ and type(functionOrString) == types.StringType: self.constrkw['functionOrString'] = "\'"+suppressMultipleQuotes(functionOrString)+"\'" if importString is not None: self.constrkw['importString'] = "\'"+suppressMultipleQuotes(importString)+"\'" elif hasattr(function, 'name'): # case of a Pmv command self.constrkw['command'] = 'masterNet.editor.vf.%s'%function.name elif hasattr(function, '__name__'): # a function is not savable, so we are trying to save something self.constrkw['functionOrString'] = "\'"+function.__name__+"\'" else: # a function is not savable, so we are trying to save something self.constrkw['functionOrString'] = "\'"+function.__class__.__name__+"\'" if (importString is None or importString == '') \ and self.constrkw.has_key('importString') is True: del self.constrkw['importString'] if len(self.posArgsNames) > 0: self.constrkw['posArgsNames'] = self.posArgsNames elif self.constrkw.has_key('posArgsNames') is True: del self.constrkw['posArgsNames'] if len(self.namedArgs) > 0: self.constrkw['namedArgs'] = self.namedArgs elif self.constrkw.has_key('namedArgs') is True: del self.constrkw['namedArgs'] if kw.has_key('host') is True: self.constrkw['host'] = '\"'+suppressMultipleQuotes(kw['host'])+'\"' elif self.constrkw.has_key('host') is True: del self.constrkw['host'] code = """def doit(self, *args): # get all positional arguments posargs = [] for pn in self.posArgsNames: posargs.append(locals()[pn]) # build named arguments kw = {} for arg in self.namedArgs.keys(): kw[arg] = locals()[arg] # call function try: if hasattr(self.function,'__call__') and hasattr(self.function.__call__, 'im_func'): result = apply( self.function.__call__, posargs, kw ) else: result = apply( self.function, posargs, kw ) except Exception, e: from warnings import warn warn(str(e)) result = None self.outputData(result=result) """ if code: self.setFunction(code) # change signature of compute function self.updateCode(port='ip', action='create', tagModified=False) def buildPortsForPositionalAndNamedArgs(self, args, namedArgs, argsDescription={}, createPortNow=False): lAllPortNames = args + namedArgs.keys() for name in lAllPortNames: if name in args: ipdescr = {'name':name, 'required':True} if argsDescription.get(name): lHasDefaultValue = True val = argsDescription[name]['default'] else: lHasDefaultValue = False else: ipdescr = {'name':name, 'required':False} lHasDefaultValue = True val = namedArgs[name] dtype = 'None' if lHasDefaultValue is True: if argsDescription.get(name) and argsDescription[name]['type']=='selection': dtype = 'string' self.widgetDescr[name] = { 'class': 'NEComboBox', 'initialValue':val, 'choices':argsDescription[name]['values'], 'labelGridCfg':{'sticky':'w'}, 'labelCfg':{'text':name}, } elif argsDescription.get(name) \ and argsDescription[name]['type']=='FILE' \ and ( argsDescription[name]['ioType']=='INPUT' \ or argsDescription[name]['ioType']=='INOUT'): dtype = 'string' self.widgetDescr[name] = { 'class': 'NEEntryWithFileBrowser', 'initialValue':val, 'labelGridCfg':{'sticky':'w'}, 'labelCfg':{'text':name}, } elif type(val) is types.BooleanType: dtype = 'boolean' self.widgetDescr[name] = { 'class': 'NECheckButton', 'initialValue':val==True, 'labelGridCfg':{'sticky':'w'}, 'labelCfg':{'text':name}, } elif type(val) in [ types.IntType, types.LongType]: dtype = 'int' self.widgetDescr[name] = { 'class': 'NEDial', 'size':50, 'showLabel':1, 'oneTurn':1, 'type':'int', 'initialValue':val, 'labelGridCfg':{'sticky':'w'}, 'labelCfg':{'text':name}, } elif type(val) in [types.FloatType, types.FloatType]: dtype = 'float' self.widgetDescr[name] = { 'class': 'NEDial', 'size':50, 'showLabel':1, 'oneTurn':1, 'type':'float', 'initialValue':val, 'labelGridCfg':{'sticky':'w'}, 'labelCfg':{'text':name}, } elif type(val) is types.StringType: dtype = 'string' self.widgetDescr[name] = { 'class': 'NEEntry', 'width':10, 'initialValue':val, 'labelGridCfg':{'sticky':'w'}, 'labelCfg':{'text':name}, } if argsDescription.get(name): self.widgetDescr[name]['labelBalloon'] = argsDescription[name]['description'] ipdescr.update({'datatype':dtype, 'balloon':'Defaults to '+str(val), 'singleConnection':True}) self.inputPortsDescr.append( ipdescr ) if createPortNow is True: # create port ip = apply( self.addInputPort, (), ipdescr ) # create widget if necessary if dtype != 'None': ip.createWidget(descr=self.widgetDescr[name]) def inSegment( p, s1, s2): ## inSegment(): determine if a point is inside a segment ## Input: a point p, and a collinear segment [s1,s2] ## Return: 1 = P is inside S ## 0 = P is not inside S if s1[0] != s2[0]: # [s1,s2] is not vertical if s1[0]<=p[0] and p[0]<=s2[0]: return True if s1[0]>=p[0] and p[0]>=s2[0]: return True else: # S is vertical, so test y coordinate if s1[1]<=p[1] and p[1]<=s2[1]: return True if s1[1]>=p[1] and p[1]>=s2[1]: return True return False def perp( a ): # return 2D vector orthogonal to a b = [0,0] b[0] = -a[1] b[1] = a[0] return b def seg_intersect(a1, a2, b1, b2) : # line segment a given by endpoints a1, a2 # line segment b given by endpoints b1, b2 # return x,y of intersection of 2 segments or None, None da = (a2[0]-a1[0], a2[1]-a1[1]) db = (b2[0]-b1[0], b2[1]-b1[1]) dp = (a1[0]-b1[0], a1[1]-b1[1]) dap = perp(da) denom = numpy.dot( dap, db) if denom==0.0: return None, None num = numpy.dot( dap, dp ) l = (num / denom) return l*db[0]+b1[0], l*db[1]+b1[1] ## ## Image nodes are node for which the node is represented by an image rendered ## using pycairo and added to the canvas rather than using Tkinter.Canvas ## primitives ## def rotateCoords(center, coords, angle): """ Rotate a list of 2D coords around the center by an angle given in degrees """ cangle = cmath.exp(angle*1j*math.pi/180) offset = complex(center[0], center[1]) rotatedxy = [] for i in range(0, len(coords), 2): v = cangle * (complex(coords[i], coords[i+1]) - offset) + offset rotatedxy.append(v.real) rotatedxy.append(v.imag) return rotatedxy class NodeStyle: """ Class to define the rendering style of a node. Every node has a NodeStyle instance that is used to render the node's image. The VPE has a NodeStylesManager that stores alternate styles for each node """ def __init__(self, **kw): #flowDirection='leftRight' self.rotAngle = 0.0 self.width = None self.height = None self.fillColor = [0.82, 0.88, 0.95, 0.5] self.outlineColor = [0.28, 0.45, 0.6, 1.] self.inputPorts = {} self.iportNumToName = [] # list of names of input ports self.outputPorts = {} self.oportNumToName = [] # list of names of output ports self.configure(**kw) #if flowDirection=='leftRight': # sideIn = 'left' # sideOut = 'right' #elif flowDirection=='topBottom': # sideIn = 'top' # sideOut = 'bottom' #else: # raise RuntimeError, "bad flowDirection" #self.flowDirection = flowDirection def getPortXY(self, descr, node): ulx, uly = node.UL width = node.activeWidth height = node.activeHeight dx, dy = descr.get('ulrpos', (None, None)) if dx is not None: if abs(dx) < 1.0: dx *= width if abs(dy) < 1.0: dy *= height return ulx+dx, uly+dy dx, dy = descr.get('urrpos', (None, None)) if dx is not None: if abs(dx) < 1.0: dx *= width if abs(dy) < 1.0: dy *= height return ulx+width+dx, uly+dy dx, dy = descr.get('llrpos', (None, None)) if dx is not None: if abs(dx) < 1.0: dx *= width if abs(dy) < 1.0: dy *= height return ulx+dx, uly+height+dy dx, dy = descr.get('lrrpos',(None, None)) if dx is not None: if abs(dx) < 1.0: dx *= width if abs(dy) < 1.0: dy *= height return ulx+width+dx, uly+height+dy # fixme .. find a good location for this port return ulx+10, uly def getEdge(self, styleDict): for k,v in styleDict.items(): if k[-4:]=='rpos': found = True break if not found: print 'PORT EDGE NOT FOUND %rpos key missing using "top"', portNum, descr return 'top' if k=='ulrpos': if v[0]==0: return 'left' else: return 'top' elif k=='urrpos': if v[0]==0: return 'right' else: return 'top' elif k=='llrpos': if v[0]==0: return 'left' else: return 'bottom' elif k=='lrrpos': if v[0]==0: return 'right' else: return 'bottom' def setInputPortStyles(self, ipStyles): for name, styleDict in ipStyles: self.iportNumToName.append(name) styleDict['edge'] = self.getEdge(styleDict) self.inputPorts[name] = styleDict def setOutputPortStyles(self, opStyles): for name, styleDict in opStyles: self.oportNumToName.append(name) styleDict['edge'] = self.getEdge(styleDict) self.outputPorts[name] = styleDict def configure(self, **kw): width = kw.get('width', None) if width: if width > 0 and isinstance(width, (int, float)): self.width = width else: print 'WARNING bad width', width, type(width) height = kw.get('height', None) if height: if height > 0 and isinstance(height, (int, float)): self.height = height else: print 'WARNING bad height', height, type(height) rotAngle = kw.get('rotAngle', None) if rotAngle is not None: if isinstance(rotAngle, (int, float)): self.rotAngle = rotAngle else: print 'WARNING bad rotAngle', rotAngle, type(rotAngle) fillColor = kw.get('fillColor', None) if fillColor: if len(fillColor)==3 and isinstance(fillColor[0], float): self.fillColor[:3] = fillColor elif len(fillColor)==4 and isinstance(fillColor[0], float): self.fillColor = fillColor[:] else: print 'WARNING bad fillColor', fillColor, type(fillColor) outlineColor = kw.get('outlineColor', None) if outlineColor: if len(outlineColor)==3 and isinstance(outlineColor[0], float): self.outlineColor[:3] = outlineColor elif len(outlineColor)==4 and isinstance(outlineColor[0], float): self.outlineColor = outlineColor[:] else: print 'WARNING bad outlineColor', outlineColor, type(outlineColor) inputPorts = kw.get('inputPorts', None) if inputPorts: assert isinstance(inputPorts, dict) self.inputPorts = inputPorts.copy() outputPorts = kw.get('outputPorts', None) if outputPorts: assert isinstance(outputPorts, dict) self.outputPorts = outputPorts.copy() iportNumToName = kw.get('iportNumToName', None) if iportNumToName: assert isinstance(iportNumToName, list) self.iportNumToName = iportNumToName[:] oportNumToName = kw.get('oportNumToName', None) if oportNumToName: assert isinstance(oportNumToName, list) self.oportNumToName = oportNumToName[:] def getStyle(self): style = {'width':self.width, 'height':self.height, 'fillColor': self.fillColor, 'outlineColor': self.outlineColor, 'rotAngle': self.rotAngle, 'inputPorts': self.inputPorts, 'iportNumToName' : self.iportNumToName, 'outputPorts': self.outputPorts, 'oportNumToName' : self.oportNumToName, } return style def copy(self): return self.__class__( width = self.width, height = self.height, rotAngle = self.rotAngle, fillColor = self.fillColor[:], outlineColor = self.outlineColor[:], inputPorts = self.inputPorts.copy(), iportNumToName = self.iportNumToName[:], outputPorts = self.outputPorts.copy(), oportNumToName = self.oportNumToName[:] ) def getSize(self): return self.width, self.height def getFillColor(self): return self.fillColor def getOutlineColor(self): return self.outlineColor def getAngle(self): return self.rotAngle class ImageNode(NetworkNode): #def edit(self, event=None): #if self.objEditor: # self.objEditor.top.master.lift() # return #from ImageNodeEditor import ImageNodeEditor #self.objEditor = ImageNodeEditor(self) def saveStylesDefinition(self): # now save style in styles folder from mglutil.util.packageFilePath import getResourceFolderWithVersion sm = self.editor.nodeStylesManager styles = sm.getStylesForNode(self) visionrcDir = getResourceFolderWithVersion() folder = os.path.join(visionrcDir, 'Vision', 'nodeStyles') filename = os.path.join(folder, "%s__%s.py"%( self.library.name.replace('.', '___'), self.__class__.__name__)) f = open(filename, 'w') f.write("styles = {\n") default = styles.get('default', styles.keys()[0]) f.write(" 'default' : '%s',\n"%default) for name, style in styles.items(): if name=='default': continue f.write(" '%s' : %s,\n"%(name, str(style.getStyle()))) f.write(" }\n") f.close() ## def getStylesDefinitionSourceCode(self, indent): ## #'EwSignals_v0.1|EwAmpDelaySignal' : { ## # 'default' : 'square80' ## # 'square80' : {'width':80, 'height':80}, ## # 'small rectangle' : {'width':200, 'height':120}, ## # 'large rectangle' : {'width':300, 'height':240}, ## # }, ## lines = [] ## lines.append(indent+"'%s|%s' : {\n"%(self.library.name, self.__class__.__name__)) ## indent1 = indent + ' ' ## for name, style in self.styles.items(): ## lines.append(indent1+"'%s' : %s,\n"%(name, str(style))) ## lines.append(indent+"},\n") ## return lines def __init__(self, name='NoName', library=None, iconFileName=None, iconPath='', **kw): """ This class implements a NetworkNode that is rendered using a single image generated using pycairo """ constrkw = kw.pop('constrkw', None) NetworkNode.__init__(*(self, name, None, constrkw, None, library, 0), **kw) # posx and posy are upper left corner of BoundingBox(self.innerBox) self.center= [0,0] # coords of node's center in canvas self.rotAngle = 0.0 # keep track of rotation angle self.selectOptions = {} self.deselectOptions = {} self.iconPath = iconPath self.iconFileName = iconFileName # create the node renderer from NetworkEditor.drawNode import CairoNodeRenderer self.renderer = CairoNodeRenderer() self.posx = None # x coord of upper left corner of the node's image self.posy = None # y coord of upper left corner of the node's image self.activeWidth = None # length of not node's box self.activeheight = None # height of not node's box self.UL = (None, None) # offset of upper left corner of box in image # node styles self.nodeStyle = None self.currentNodeStyle = None # None means no predefined style is applied # else it is the name of the style def resize(self, event): self.startDrawingResizeBox(event) def startDrawingResizeBox(self, event): num = event.num self.mouseButtonFlag = self.mouseButtonFlag & ~num canvas = self.network.canvas canvas.configure(cursor="bottom_right_corner") x1, y1, x2, y2 = self.getBoxCorners() self.origx = x1 self.origy = y1 x = canvas.canvasx(event.x) y = canvas.canvasy(event.y) self.hasMoved = 0 canvas.bind(""%num, self.endResizeBox) canvas.bind(""%num, self.resizeBox) self.resizeBoxID = canvas.create_rectangle(x1, y1, x2, y2, outline='green') # function to draw the box def selectionBoxMotion(self, event): self.ResizeBox(event) # call back for motion events def resizeBox(self, event): canvas = self.network.canvas #if self.resizeBoIDx: canvas.delete(self.resizeBoxID) x = canvas.canvasx(event.x) y = canvas.canvasy(event.y) # check if the mouse moved only a few pixels. If we are below a # threshold we assume we did not move. This is usefull for deselecting # nodes for people who don't have a steady hand (who move the mouse # when releasing the mouse button, or when the mouse pad is very soft # and the mouse moves because it is pressed in the pad...) if abs(self.origx-x) < 10 or abs(self.origy-y) < 10: self.hasMoved = 0 else: self.hasMoved = 1 canvas.coords(self.resizeBoxID, self.origx, self.origy, x, y) x1, y1, x2, y2 = canvas.bbox(self.resizeBoxID) self.nodeStyle.configure(width=x2-x1, height=y2-y1) self.boxCorners = self.getBoxCorners() self.redrawNode() #print 'ORIG2', self.origx, self.origy, x, y, canvas.bbox(self.resizeBoxID) # callback for ending command def endResizeBox(self, event): canvas = self.network.canvas canvas.configure(cursor="") x1, y1, x2, y2 = canvas.bbox(self.resizeBoxID) width = self.activeWidth = x2-x1 height = self.activeHeight = y2-y1 self.nodeStyle.configure(width=width, height=y2-y1) self.redrawNode() canvas.delete(self.resizeBoxID) num = event.num self.mouseButtonFlag = self.mouseButtonFlag & ~num canvas.unbind(""%num) canvas.unbind(""%num) del self.origx del self.origy del self.resizeBoxID self.currentNodeStyle = None # set the a style name that is a key in # ed.nodeStylesManager.styles OR set to None when the style is modified # but not saved as a style def getNodeDefinitionSourceCode(self, networkName, indent="", ignoreOriginal=False): self.nameInSavedFile = self.getUniqueNodeName() # specialize this method to save the the system configuration lines = NetworkNode.getNodeDefinitionSourceCode( self, networkName, indent=indent, ignoreOriginal=ignoreOriginal) # save node rotation if self.rotAngle !=0.0: lines.append( indent + '%s.rotate(%f)\n'%( self.nameInSavedFile, self.rotAngle)) from NetworkEditor.macros import MacroNetwork if isinstance(self.network, MacroNetwork): if hasattr(self.network.macroNode, 'nameInSavedFile') and \ self.network.macroNode.nameInSavedFile: # we are saving the macro name = "%s.macroNetwork.nodes[%d]"%( self.network.macroNode.nameInSavedFile, # <- this break copy of network in macro self.network.nodeIdToNumber(self._id)) else: # we are copying the network in a macro name = self.nameInSavedFile else: name = self.nameInSavedFile if self.currentNodeStyle: # we have a predefined style lines.append( indent + '%s.setStyle("%s")\n'%( name, self.currentNodeStyle)) else: # the style is modified but not saved as a template lines.append( indent + 'nodeStyle = %s\n'%self.nodeStyle.getStyle()) lines.append( indent + '%s.nodeStyle.configure(**nodeStyle)\n'%( name)) return lines def autoResizeX(self): return def drawMapIcon(self, naviMap): canvas = naviMap.mapCanvas x0, y0 = naviMap.upperLeft scaleFactor = naviMap.scaleFactor c = self.getBoxCorners() cid = canvas.create_rectangle( [x0+c[0]*scaleFactor, y0+c[1]*scaleFactor, x0+c[2]*scaleFactor, y0+c[3]*scaleFactor], fill='grey50', outline='black', tag=('navimap',)) ## import Image ## im = self.imShadow1 ## self.scaledImage = im.resize((int(im.size[0]*scaleFactor), ## int(im.size[1]*scaleFactor)), Image.ANTIALIAS) ## self.mapImagetk = ImageTk.PhotoImage(image=self.scaledImage) ## cid = canvas.create_image( x0+self.posx*scaleFactor, y0+self.posy*scaleFactor, ## image=self.mapImagetk) self.naviMapID = cid return cid def deleteIcon(self): NetworkNode.deleteIcon(self) if hasattr(self, 'imagetk'): del self.imagetk # else the selected rendered node remains if hasattr(self, 'imagetkRot'): del self.imagetkRot # else the selected rendered node remains if hasattr(self, 'imagetkSel'): del self.imagetkSel # else the selected rendered node remains if hasattr(self, 'imagetkSelRot'): del self.imagetkSelRot # else the selected rendered node remains def select(self): canvas = self.iconMaster tags = canvas.itemcget(self.id, 'tags').split() NetworkNode.select(self) canvas.itemconfigure(self.innerBox, tags=tags+['selected'], image = self.imagetkSelRot) def deselect(self): canvas = self.iconMaster tags = canvas.itemcget(self.id, 'tags').split() NetworkNode.deselect(self) canvas.itemconfigure(self.innerBox, tags=tags, image = self.imagetkRot) canvas.dtag(self.innerBox,'selected') def getColor(self): return (0.82, 0.88, 0.95, 0.5) def setColor(self, color): return def pickedComponent(self, x, y): xr = x - self.posx-self.shadowOffset[0] # x relative to image upper left corner yr = y - self.posy-self.shadowOffset[1] # y relative to image upper left corner # check if the (x,y) is the position of an input port for p in self.inputPorts: px, py = p.posRotated dx = abs(xr-px) dy = abs(yr-py) if dx<10 and dy<10: return p, 'input' # check if the (x,y) is the position of an output port for p in self.outputPorts: px, py = p.posRotated dx = abs(xr-px) dy = abs(yr-py) if dx<10 and dy<10: return p, 'output' # check if we clicked inside the node p1x, p1y, p2x, p2y = self.boxCorners #self.network.canvas.create_rectangle(p1x, p1y, p2x, p2y, outline='blue') angle = self.rotAngle if angle !=0.0: # unrotate the (x,y) point cx, cy = self.nodeCenter[0]+self.posx, self.nodeCenter[1]+self.posy x, y = rotateCoords((cx,cy), [x, y], angle) #self.network.canvas.create_rectangle(x-2, y-2, x+2, y+2, outline='red') if (x>=p1x and x<=p2x) and (y>=p1y and y<=p2y): # check if we picked resize handle #if (x-p1x<10 and y-p1y<10): # print 'UL resize' # return self, 'resize UL' if (p2x-x<10 and p2y-y<10): return self, 'resize' return self, 'node' return None, None def buildIcons(self, canvas, posx, posy, small=False): """Build NODE icon with ports etc""" NetworkNode.buildIcons(self, canvas, posx, posy, small) # add node editing menu entries self.menu.add_separator() self.menu.add_command(label='Rotate', command=self.rotate_cb) #self.menu.add_command(label='Resize', command=self.resize) self.stylesMenuTK = Tkinter.StringVar() self.stylesMenu = Tkinter.Menu(self.menu, tearoff=0, postcommand=self.fillStyleMenu) self.menu.add_cascade(label="styles", menu=self.stylesMenu) def fillStyleMenu(self, event=None): self.stylesMenu.delete(0, 'end') self.stylesMenu.add_command(label="Save As ...", command=self.saveStyle) self.stylesMenu.add_command(label="Set as Default", command=self.setDefaultStyle, state='disabled') self.stylesMenu.add_separator() self.stylesMenu.add_radiobutton( label='auto', variable=self.stylesMenuTK, command=self.setStyle_cb, value='auto') sm = self.editor.nodeStylesManager styles = sm.getStylesForNode(self) if styles: for name, style in styles.items(): if name=='default': continue self.stylesMenu.add_radiobutton( label=name, variable=self.stylesMenuTK, command=self.setStyle_cb, value=name) if self.currentNodeStyle: self.stylesMenuTK.set(self.currentNodeStyle) if self.currentNodeStyle: # enable Set as Default menu entry self.stylesMenu.entryconfigure(1, state='normal') else: self.stylesMenuTK.set('') def setDefaultStyle(self): name = self.stylesMenuTK.get() self.editor.nodeStylesManager.setDefault(self, name) self.saveStylesDefinition() def setStyle_cb(self): name = self.stylesMenuTK.get() self.setStyle(name) def setStyle(self, name): if name == 'auto': self.currentNodeStyle = 'auto' else: sm = self.editor.nodeStylesManager styles = sm.getStylesForNode(self) self.nodeStyle.configure( **styles[name].getStyle() ) self.currentNodeStyle = name self.redrawNode() def _saveStyle(self, result): #self.askNameWidget.withdraw() self.askNameWidget.deactivate() if result == 'OK':# or hasattr(result, "widget"): name = self.askNameWidget.get() style = self.nodeStyle.getStyle() sm = self.editor.nodeStylesManager sm.addStyle(self, name, NodeStyle(**style)) self.currentNodeStyle = name self.saveStylesDefinition() def saveStyle(self): master = self.editor.root w = Pmw.PromptDialog( master, title = 'style name', label_text = "Enter the name for this style", entryfield_labelpos = 'n', buttons = ('OK', 'Cancel'), command=self._saveStyle) sm = self.editor.nodeStylesManager styles = sm.getStylesForNode(self) if styles: nb = len(styles)+1 else: nb = 1 w.insertentry(0, "custom%d"%nb) w.component('entry').selection_range(0, Tkinter.END) w.component('entry').focus_set() w.component('entry').bind('', self._saveStyle) w.geometry( '+%d+%d' % (master.winfo_x()+200, master.winfo_y()+200)) self.askNameWidget = w w.activate() def drawNode(self, sx, sy, line, fill, macro, padding): renderer = self.renderer # render the node shape renderer.makeNodeImage(sx, sy, line, fill, macro) self.UL = list(renderer.ul) # add ports #ip = self.inputPortsDescr #for pn in range(len(ip)): # # find the position in image # x, y , size, vector, line, fill, outline, label = ip.getDrawingParams( # pn, self) #print 'ZZZZZZZZZZ', self.nodeStyle.iportNumToName ## def drawInputPort(port, portStyle): ## port.vector = portStyle['vector'] ## port.vectorRotated = portStyle['vector'] ## x, y = self.nodeStyle.getPortXY(portStyle, self) ## renderer.drawPort('in', x, y, portStyle) ## port.posRotated = [x,y] ## port.pos = (x,y) ## if macro: ## pn = 0 ## for op in self.macroNetwork.ipNode.outputPorts[1:]: ## ip = op.connections[0].port2 ## node = ip.node ## portStyle = node.nodeStyle.inputPorts[ip.name] ## print 'Drawing macro input', ip.name, portStyle ## drawInputPort(ip, portStyle) ## else: ## for pn, portName in enumerate(self.nodeStyle.iportNumToName): ## if not self.widgetDescr.has_key(portName): ## portStyle = self.nodeStyle.inputPorts[portName] ## port = self.inputPorts[pn] ## drawInputPort(port, portStyle) for pn, portName in enumerate(self.nodeStyle.iportNumToName): portStyle = self.nodeStyle.inputPorts[portName] port = self.inputPorts[pn] port.vector = portStyle['vector'] port.vectorRotated = portStyle['vector'] x, y = self.nodeStyle.getPortXY(portStyle, self) #edge = op[pn]['edge'] #renderer.drawPort('out', x, y, size, vector, line, fill, outline, label, edge) renderer.drawPort('in', x, y, portStyle) port.posRotated = [x,y] port.pos = (x,y) #op = self.outputPortsDescr #for pn in range(len(op)): # # find the position # x, y, size, vector, line, fill, outline, label = op.getDrawingParams( # pn, self) # #print 'DRAWNODE1', self.name, pn, vector for pn, portName in enumerate(self.nodeStyle.oportNumToName): portStyle = self.nodeStyle.outputPorts[portName] port = self.outputPorts[pn] port.vector = portStyle['vector'] port.vectorRotated = portStyle['vector'] x, y = self.nodeStyle.getPortXY(portStyle, self) #edge = op[pn]['edge'] #renderer.drawPort('out', x, y, size, vector, line, fill, outline, label, edge) renderer.drawPort('out', x, y, portStyle) port.posRotated = [x,y] port.pos = (x,y) renderer.drawLabel(self.name, padding) if self.iconFileName: filename = os.path.join(self.iconPath, self.iconFileName) renderer.drawIcon(filename) def getDefaultPortsStyleDict(self): ipStyles = [] # count visible ports ct = 0 for p in self.inputPortsDescr: if not self.widgetDescr.has_key(p['name']): ct += 1 incr = 1.0/(ct+1) for n, pd in enumerate(self.inputPortsDescr): ipStyles.append( (pd['name'], { 'ulrpos':((n+1)*incr,0), 'vector':(0,1), 'size':15, 'fill':(1,1,1,1), 'line':(0.28, 0.45, 0.6, 1.), 'edge':'top', 'outline':(0.28, 0.45, 0.6, 1.), 'label':pd['name'], })) opStyles = [] incr = 1.0/(len(self.outputPortsDescr)+1) for n, pd in enumerate(self.outputPortsDescr): opStyles.append( (pd['name'], { 'llrpos':((n+1)*incr,0), 'vector':(0,-1), 'size':15, 'fill': (1,1,1,1), 'line':(0.28, 0.45, 0.6, 1.), 'edge':'bottom', 'outline':(0.28, 0.45, 0.6, 1.), 'label':pd['name'], })) return ipStyles, opStyles def computeDefaultNodeSize(self): renderer = self.renderer ## compute the size needed for the node # get size of label x_bearing, y_bearing, width, height = renderer.getLabelSize(self.name) iconwidth = iconheight = 0 if self.iconFileName: iconwidth = 20 iconheight = 20 - 10 # -10 is minus the port label height width += iconwidth height += iconheight # here we compute how much space we need around the label for port icons and labels # port label are written using "Sans', size 10 which is 8 pixels heigh # for now we add 10 above the label and 10 below for the label # then we add maxPortSize / 2 above and below for the ports glyph # on the sides we will all the max of the port label length + 4 + max port glyph / 2 maxPortSize = 0 if self.iconFileName: maxPortLabLen = {'left':10, 'right':0, 'top':0, 'bottom':0} maxPortLabHeight = {'left':0, 'right':0, 'top':10, 'bottom':0} else: maxPortLabLen = {'left':0, 'right':0, 'top':0, 'bottom':0} maxPortLabHeight = {'left':0, 'right':0, 'top':0, 'bottom':0} sumPortLabLenTop = 0.0 sumPortLabLenBottom = 0.0 for pn, port in enumerate(self.inputPorts): if self.widgetDescr.has_key(port.name): continue pd = self.nodeStyle.inputPorts[port.name] edge = pd['edge'] size = pd['size'] label = pd['label'] if size > maxPortSize: maxPortSize=size if label: x_b, y_b, w, h = renderer.getLabelSize(label, 'Sans', size=10) if edge=='top': sumPortLabLenTop += w+10 elif edge=='bottom': sumPortLabLenBottom += w+10 if w > maxPortLabLen[edge]: maxPortLabLen[edge] = w if h > maxPortLabHeight[edge]: maxPortLabHeight[edge] = h for pn, port in enumerate(self.outputPorts): pd = self.nodeStyle.outputPorts[port.name] edge = pd['edge'] size = pd['size'] label = pd['label'] if size > maxPortSize: maxPortSize=size if label: x_b, y_b, w, h = renderer.getLabelSize(label, 'Sans', size=10) if edge=='top': sumPortLabLenTop += w+10 elif edge=='bottom': sumPortLabLenBottom += w+10 if w > maxPortLabLen[edge]: maxPortLabLen[edge] = w if h > maxPortLabHeight[edge]: maxPortLabHeight[edge] = h pady = maxPortLabHeight['top'] + maxPortLabHeight['bottom'] + maxPortSize + 2*8 padx = maxPortLabLen['left'] + maxPortLabLen['right'] + maxPortSize + 2*8 padding = { 'left': max(5, maxPortLabLen['left']), 'right': max(5, maxPortLabLen['right']), 'top': max(5, maxPortLabHeight['top']), 'bottom': max(5, maxPortLabHeight['bottom']) } sx = max( max(width, sumPortLabLenTop, sumPortLabLenBottom) + iconwidth + 2*8, max(maxPortLabLen['bottom'], maxPortLabLen['top'], )) sy = height+pady #print 'GGGGG', maxPortSize, maxPortLabLen, maxPortLabHeight, width, height, padx, pady, sx, sy return sx, sy, padding def makeNodeImage(self, canvas, posx, posy): renderer = self.renderer if self.nodeStyle is None: # no styles defined. Happens when we first # click on a node in the tree #print 'NO NodeStyle', self.library if self.library is not None: color1 = self.library.fillColor color2 = self.library.outlineColor else: color1 = '#FFFFFF' color2 = '#AAAAAA' fillColor = [float(x)/256**2 for x in self.iconMaster.winfo_rgb(color1)] outlineColor = [float(x)/256**2 for x in self.iconMaster.winfo_rgb(color2)] sm = self.editor.nodeStylesManager stylesDict = sm.getStylesForNode(self) if stylesDict is None or stylesDict['default']=='auto': # no style available for this class #print ' no style dict' # create node Style because it will be used in computeDefaultNodeSize self.nodeStyle = style = NodeStyle( width=200, height=200, fillColor=fillColor, outlineColor=outlineColor) ipStyles, opStyles = self.getDefaultPortsStyleDict() style.setInputPortStyles(ipStyles) style.setOutputPortStyles(opStyles) sx, sy, padding = self.computeDefaultNodeSize() style.configure(width=sx, height=sy) #self.currentNodeStyle = None #'auto' self.currentNodeStyle = 'auto' else: #print ' with style dict' default = stylesDict['default'] style = stylesDict[default].copy() self.currentNodeStyle = default self.nodeStyle = style sx, sy, padding = self.computeDefaultNodeSize() elif self.currentNodeStyle == 'auto': #print 'Auto NodeStyle' # call to compute padding sx, sy, padding = self.computeDefaultNodeSize() style = self.nodeStyle = NodeStyle(width=sx, height=sy) ipStyles, opStyles = self.getDefaultPortsStyleDict() style.setInputPortStyles(ipStyles) style.setOutputPortStyles(opStyles) else: #print 'WITH NodeStyle' # call to compute padding sx, sy, padding = self.computeDefaultNodeSize() sx, sy = self.nodeStyle.getSize() self.activeWidth = sx self.activeHeight = sy # render the node shape fill = self.nodeStyle.getFillColor() line = self.nodeStyle.getOutlineColor() rotAngle = self.nodeStyle.getAngle() #print 'ANGLE', self.nodeStyle.getAngle() from NetworkEditor.macros import MacroImageNode macro = isinstance(self, MacroImageNode) # draw the node self.drawNode(sx, sy, line, fill, macro, padding) image = renderer.getPilImage() self.imShadow1, offset = renderer.addDropShadow() self.shadowOffset = offset #self.imShadow1 = image self.imagetk = ImageTk.PhotoImage(image=self.imShadow1) self.imagetkRot = self.imagetk self.imwidth = self.imagetk.width() self.imheight = self.imagetk.height() self.boxCorners = self.getBoxCorners() # assign ports posx and posy for port in self.inputPorts+self.outputPorts: x,y = port.pos port.posx = posx + self.shadowOffset[0] + x port.posy = posy + self.shadowOffset[0] + y canvas.itemconfigure(self.innerBox, image=self.imagetk) # draw image outline #bb = canvas.bbox(self.innerBox) #canvas.create_rectangle(*bb, outline='black') ## this could be used to render selected nodes # draw rectangle around node image bbox ## x1, y1, x2, y2 = renderer.bbox ## self.nodeOutline = canvas.create_rectangle( ## x1+posx, y1+posy, x2+posx, y2+posy, fill='yellow', ## outline='yellow', tags=(self.iconTag,'node')) ## build an image with yellow background for selected state fill = (1., 1, 0., 0.5) line = (0.28, 0.45, 0.6, 1.) self.drawNode(sx, sy, line, fill, macro, padding) self.imShadow2, offset = renderer.addDropShadow() #self.imShadow2 = image self.imagetkSel = ImageTk.PhotoImage(image=self.imShadow2) self.imagetkSelRot = self.imagetkSel bb = canvas.bbox(self.innerBox) self.nodeCenter = (bb[2]-bb[0])*.5, (bb[3]-bb[1])*.5 #self.nodeCenter = (sx*.5, sy*.5) #print 'ROTATE', rotAngle, self.rotAngle self.rotate(rotAngle) def buildNodeIcon(self, canvas, posx, posy, small=False): renderer = self.renderer self.iconMaster = canvas # set posx, posy self.posx = posx self.posy = posy self.innerBox = canvas.create_image( posx, posy, anchor=Tkinter.NW, # image=self.imagetk, tags=(self.iconTag,'node')) self.makeNodeImage(canvas, posx, posy) self.outerBox = self.innerBox bb = canvas.bbox(self.innerBox) #print 'NODE IMAGE BBOX', bb, posx, posy, self.UL, self.activeWidth, self.activeHeight, self.shadowOffset # draw node's image bounding box #canvas.create_rectangle( # *bb, fill='', outline='yellow', tags=(self.iconTag,'node')) # draw node's box bounding box #canvas.create_rectangle( # bb[0]+self.UL[0]+self.shadowOffset[0], # bb[1]+self.UL[1]+self.shadowOffset[1], # bb[0]+self.UL[0]+self.shadowOffset[0]+self.activeWidth, # bb[1]+self.UL[1]+self.shadowOffset[1]+self.activeHeight, # fill='', outline='green', tags=(self.iconTag,'node')) #self.nodeCenter = (bb[2]-bb[0])*.5, (bb[3]-bb[1])*.5 self.textId = self.innerBox self.id = self.innerBox # used to build network.nodesById used for picking self.iconTag = 'node'+str(self.textId) # used in node.select #if self.network is not None: # self.move(posx, posy) self.hasMoved = False # reset this attribute because the current # position is now the original position ## ## functions used to rotate Nodes ## def rotateRelative(self, angle): canvas = self.iconMaster self.rotAngle = (self.rotAngle + angle)%360 # center of unrotated image cx, cy = self.nodeCenter # rotate node images imShadowRotated = self.imShadow1.rotate( self.rotAngle, Image.BICUBIC, expand=1) self.imagetkRot = ImageTk.PhotoImage(image=imShadowRotated) imShadowRotated = self.imShadow2.rotate( self.rotAngle, Image.BICUBIC, expand=1) self.imagetkSelRot = ImageTk.PhotoImage(image=imShadowRotated) canvas.itemconfigure(self.innerBox, image=self.imagetkRot) bb = canvas.bbox(self.innerBox) #bb = self.boxCorners rcx, rcy = (bb[2]-bb[0])*.5, (bb[3]-bb[1])*.5 canvas.coords(self.innerBox, self.posx+cx-rcx, self.posy+cy-rcy) # rotate input ports # node center in canvas coordinate system cx = self.posx + self.nodeCenter[0] cy = self.posy + self.nodeCenter[1] # node uper left corner in canvas coordinate system offx = self.posx+self.shadowOffset[0] offy = self.posy+self.shadowOffset[1] for p in self.inputPorts: hpx = p.pos[0]+offx hpy = p.pos[1]+offy hpxr, hpyr = rotateCoords( (cx, cy), (hpx, hpy), -self.rotAngle) p.posRotated = (hpxr-offx, hpyr-offy) # display hotspots #canvas.create_rectangle(hpxr-5, hpyr-5, hpxr+5, hpyr+5) a,b,c,d = rotateCoords( (cx, cy), (0, 0, p.vector[0], p.vector[1]), self.rotAngle) p.vectorRotated = [c-a, d-b] # rotate output ports for p in self.outputPorts: hpx = p.pos[0]+offx hpy = p.pos[1]+offy hpxr, hpyr = rotateCoords( (cx, cy), (hpx, hpy), -self.rotAngle) p.posRotated = (hpxr-offx, hpyr-offy) # draw rectangel on hotspot for debuging #canvas.create_rectangle(hpxr-5, hpyr-5, hpxr+5, hpyr+5) a,b,c,d = rotateCoords( (cx, cy), (0, 0, p.vector[0], p.vector[1]), self.rotAngle) p.vectorRotated = [c-a, d-b] #print p.vector, a,b,c,d, p.vectorRotated def rotate(self, absoluteAngle): angle = absoluteAngle-self.rotAngle self.rotateRelative(angle) def rotate_cb(self): canvas = self.iconMaster bb = canvas.bbox(self.innerBox) # posx and pos y are upper left corner # self.nodeCenter is center of node relative to posx, posy cx = self.posx+self.nodeCenter[0] cy = self.posy+self.nodeCenter[1] rad = 0.5*max(bb[2]-bb[0], bb[1]-bb[0])+10 #draw a circle with an arrow to indicate node rotation mode arcid = canvas.create_oval( cx-rad, cy-rad, cx+rad, cy+rad, outline='green', width=2.0, tags=('arc',)) self._arcid = arcid canvas.tag_bind(arcid, "", self.startRotate_cb) def startRotate_cb(self, event=None): canvas = self.iconMaster self._x0 = canvas.canvasx(event.x) - self.posx - self.nodeCenter[0] self._y0 = canvas.canvasy(event.y) - self.posy - self.nodeCenter[1] #self._deltaAngle = 0 canvas.itemconfigure(self._arcid, outline='red') canvas.tag_bind(self._arcid, "", self.moveToRotate_cb) canvas.tag_bind(self._arcid, "", self.endRotate_cb) #self._lineid = canvas.create_line( # self.posx + self.nodeCenter[0], self.posy + self.nodeCenter[1], # canvas.canvasx(event.x), canvas.canvasy(event.y), # fill='green', width=2.0, tags=('arc',)) self._textid = canvas.create_text( canvas.canvasx(event.x)+10, canvas.canvasy(event.y)-10, text='%d'%self.rotAngle, fill='black', tags=('arc',)) def fullAngle(self, x0, y0, x1, y1): n0 = math.sqrt(x0*x0 + y0*y0) n1 = math.sqrt(x1*x1 + y1*y1) x0 = x0/n0 y0 = y0/n0 x1 = x1/n1 y1 = y1/n1 tetha = math.acos( (x0*x1 + y0*y1)) if x0*y1-y0*x1 > 0: return math.degrees(tetha) else: return math.degrees(2*math.pi-tetha) def moveToRotate_cb(self, event=None): canvas = self.iconMaster x1 = canvas.canvasx(event.x) - self.posx - self.nodeCenter[0] y1 = canvas.canvasy(event.y) - self.posy - self.nodeCenter[1] #canvas.coords( # self._lineid, self.posx + self.nodeCenter[0], # self.posy + self.nodeCenter[1], # canvas.canvasx(event.x), canvas.canvasx(event.y)) angle = self.fullAngle(self._x0, -self._y0, x1, -y1) self.rotate(15*int(angle/15)) canvas.itemconfigure(self._textid, text='%d'%self.rotAngle) canvas.coords(self._textid, canvas.canvasx(event.x)+10, canvas.canvasx(event.y)-10) for p in self.inputPorts: for c in p.connections: c.updatePosition() for p in self.outputPorts: for c in p.connections: c.updatePosition() def endRotate_cb(self, event=None): canvas = self.iconMaster self.iconMaster.delete('arc') self.nodeStyle.configure(rotAngle=self.rotAngle) self.currentNodeStyle = None # set the a style name that is a key in del self._arcid del self._x0 del self._y0 def updatePosXPosY(self, dx, dy): """set node.posx and node.posy after node has been moved""" self.posx += dx self.posy += dy self.boxCorners = self.getBoxCorners() def getBoxCorners(self): dx, dy = self.UL sx, sy = self.shadowOffset p1x, p1y = self.posx + dx + sx, self.posy + dy + sy, p2x = self.posx + dx + sx + self.activeWidth p2y = self.posy + dy + sy + self.activeHeight return p1x, p1y, p2x, p2y ## ## functions used to move ports ## def segmentIntersection(self, p0, p1): canvas = self.iconMaster # center of unrotated image cx, cy = self.nodeCenter[0]+self.posx, self.nodeCenter[1]+self.posy p1x, p1y, p2x,p2y = self.boxCorners coords = (p1x, p1y, p2x, p1y, p2x, p2y, p1x, p2y, p1x, p1y) # rotate coords of the box box coords = rotateCoords((cx,cy), coords, -self.rotAngle) l = len(coords) for i in range(0, l, 2): p2 = (coords[i], coords[i+1]) p3 = (coords[(i+2)%l], coords[(i+3)%l]) xi, yi = seg_intersect(p0, p1, p2, p3) inSegment( (xi, yi), p2, p3) if inSegment( (xi, yi), p0, p1) and inSegment( (xi, yi), p2, p3): #print 'intersection with edge', i, p0, p1, p2, p3, xi, yi return xi,yi return None, None def movePortTo(self, port, x, y): # x.y are absolute coordinates on the canvas and are expected to be # on the node's box outline # # x and y are potentially on the rotated shape of the box # we undo the rotation to set the port description, re-create the image # and rotate the node #print 'MOVE PORT TO', x, y angle = self.rotAngle if angle !=0.0: # unrotate the (x,y) point cx, cy = self.nodeCenter[0]+self.posx, self.nodeCenter[1]+self.posy x, y = rotateCoords((cx,cy), [x,y], self.rotAngle) # find the port's style description from ports import ImageInputPort, ImageOutputPort if isinstance(port, ImageInputPort): name = self.nodeStyle.iportNumToName[port.number] pd = self.nodeStyle.inputPorts[name] else: name = self.nodeStyle.oportNumToName[port.number] pd = self.nodeStyle.outputPorts[name] #print 'BEFORE', pd # now remove the **rpos entry for k,v in pd.items(): if k[-4:]=='rpos': print k, pd[k] del pd[k] width = float(self.activeWidth) height = float(self.activeHeight) p1x, p1y, p2x, p2y = self.getBoxCorners() #print p1x, p1y, p2x, p2y if abs(x-p1x)<2: # left edge pd['ulrpos'] = (0, (y-p1y)/height) pd['vector'] = (-1, 0) #print 'ulrpos1', (x-p1x, y-p1y) elif abs(x-p2x)<2: # right edge pd['lrrpos'] = (0, (y-p2y)/height) pd['vector'] = (1, 0) #print 'lrrpos2', (x-p2x, y-p2y) elif abs(y-p1y)<2: # top edge pd['ulrpos'] = ((x-p1x)/width, 0) pd['vector'] = (0, 1) #print 'ulrpos1', (x-p1x, y-p1y), vector elif abs(y-p2y)<2: # bottom edge pd['lrrpos'] = ((x-p2x)/width, 0) pd['vector'] = (0, -1) #print 'lrrpos2', (x-p2x, y-p2y) else: print "ERROR: edge not found", x, y, p1x, p1y, p2x, p2y pd['edge'] = self.nodeStyle.getEdge(pd) #print 'AFTER', id(pd), pd self.rotate(angle) self.redrawNode() port._hasMoved = True def rename(self, name, tagModified=True): """Rename a node. remember the name has changed, resize the node if necessary""" if name == self.name or name is None or len(name)==0: return # if name contains ' " remove them name = name.replace("'", "") name = name.replace('"', "") self.name=name self.redrawNode() if tagModified is True: self._setModified(True) def redrawNode(self): self.makeNodeImage(self.iconMaster, self.posx, self.posy) # update all connections for port in self.inputPorts+self.outputPorts: for c in port.connections: if c.id: c.updatePosition() def movePortToRelativeLocation(self, port, corner, dx, dy): # move a port to a position relative to one of its corners # corner can be ul, ll, ur, or lr string # dx, dy are relative displacements from the corner point p1x, p1y, p2x,p2y = self.boxCorners if corner=='ul': cornerx, cornery = p1x, p1y elif corner=='ll': cornerx, cornery = p1x, p2y elif corner=='ur': cornerx, cornery = p2x, p1y elif corner=='lr': cornerx, cornery = p2x, p2y self.movePortTo(port, cornerx+dx, cornery+dy) def movePortToRelativePos(self, port, corner, edge, percentx, percenty): # move a port to a position specified by a corner and a percent of edge length angle = self.rotAngle if angle !=0.0: # unrotate the (x,y) point cx, cy = self.nodeCenter[0]+self.posx, self.nodeCenter[1]+self.posy x, y = rotateCoords((cx,cy), [x,y], self.rotAngle) # find the port's style description from ports import ImageInputPort, ImageOutputPort if isinstance(port, ImageInputPort): name = self.nodeStyle.iportNumToName[port.number] pd = self.nodeStyle.inputPorts[name] else: name = self.nodeStyle.oportNumToName[port.number] pd = self.nodeStyle.outputPorts[name] ## # find the port's description ## found = False ## for p, pd in zip(self.inputPorts, self.inputPortsDescr): ## if p==port: ## found = True ## break ## if not found: ## for p, pd in zip(self.outputPorts, self.outputPortsDescr): ## if p==port: ## found = True ## break # now remove the **rpos entry for k,v in pd.items(): if k[-4:]=='rpos': del pd[k] p1x, p1y, p2x,p2y = self.boxCorners width = p2x-p1x height = p2y-p1y if corner=='ul': pd['ulrpos'] = (percentx, percenty) elif corner=='ll': pd['llrpos'] = (percentx, percenty) elif corner=='ur': pd['urrpos'] = (percentx, percenty) elif corner=='lr': pd['lrrpos'] = (percentx, percenty) if edge=='left': pd['vector'] = (-1, 0) elif edge=='right': pd['vector'] = (1, 0) elif edge=='top': pd['vector'] = (0, 1) elif edge=='bottom': pd['vector'] = (0, -1) self.makeNodeImage(self.iconMaster, self.posx, self.posy) self.rotate(angle) # update all connections for p in self.inputPorts+self.outputPorts: for c in p.connections: if c.id: c.updatePosition() port._hasMoved = True #n = WarpIV.ed.currentNetwork.nodes[0] class ImageDataNode(ImageNode): """ Subclass ImageNode to render a node as a circle with an input at the top and an output at the bottom (use for WarpIV data """ def drawMapIcon(self, naviMap): canvas = naviMap.mapCanvas x0, y0 = naviMap.upperLeft scaleFactor = naviMap.scaleFactor c = self.getBoxCorners() cid = canvas.create_rectangle( [x0+c[0]*scaleFactor, y0+c[1]*scaleFactor, x0+c[2]*scaleFactor, y0+c[3]*scaleFactor], fill='grey50', outline='black', tag=('navimap',)) ## import Image ## im = self.imShadow1 ## self.scaledImage = im.resize((int(im.size[0]*scaleFactor), ## int(im.size[1]*scaleFactor)), Image.ANTIALIAS) ## self.mapImagetk = ImageTk.PhotoImage(image=self.scaledImage) ## cid = canvas.create_image( x0+self.posx*scaleFactor, y0+self.posy*scaleFactor, ## image=self.mapImagetk) self.naviMapID = cid return cid def getDefaultPortsStyleDict(self): ipStyles = [] ipStyles.append( ('name', { 'ulrpos':(0.1,0), 'vector':(0,1), 'size':15, 'fill':(1,1,1,1), 'line':(0.28, 0.45, 0.6, 1.), 'edge':'top', 'outline':(0.28, 0.45, 0.6, 1.), 'label':'name', })) ipStyles.append( ('value', { 'ulrpos':(0.5,0), 'vector':(0,1), 'size':15, 'fill':(1,1,1,1), 'line':(0.28, 0.45, 0.6, 1.), 'edge':'top', 'outline':(0.28, 0.45, 0.6, 1.), 'label':'value', })) opStyles = [] opStyles.append( ('output1', { 'llrpos':(0.5,0), 'vector':(0,-1), 'size':15, 'fill': (1,1,1,1), 'line':(0.28, 0.45, 0.6, 1.), 'edge':'bottom', 'outline':(0.28, 0.45, 0.6, 1.), 'label':'output1', })) return ipStyles, opStyles def drawNode(self, sx, sy, line, fill, macro, padding): renderer = self.renderer self.activeWidth = sx self.activeHeight = sy border = renderer.border renderer.makeCircleNodeImage(sx, sy, line, fill, macro) self.UL = list(renderer.ul) port = self.inputPorts[1] # only draw second port for value portStyle = self.nodeStyle.inputPorts[port.name] port.vector = portStyle['vector'] port.vectorRotated = portStyle['vector'] x, y = self.nodeStyle.getPortXY(portStyle, self) #print 'ZZZZZZZ', x, y, border+sx*.5 #x, y = border+sx*.5, border+portStyle.get('size', 10)*.5 portStyle['label'] = None renderer.drawPort('in', x, y, portStyle) port.posRotated = [x,y] port.pos = (x,y) port = self.outputPorts[0] portStyle = self.nodeStyle.outputPorts[self.outputPorts[0].name] port.vector = portStyle['vector'] port.vectorRotated = portStyle['vector'] x, y = self.nodeStyle.getPortXY(portStyle, self) #x, y = border+sx*.5, border+sy-portStyle.get('size', 10)*.5 portStyle['label'] = None renderer.drawPort('out', x, y, portStyle) port.posRotated = [x,y] port.pos = (x,y) padding = {'left':5, 'top':0, 'right':5, 'bottom':5} renderer.drawLabel(self.name, padding) NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/macros.py0000644000175000017500000012313312224657620023145 0ustar moellermoeller## Automatically adapted for numpy.oldnumeric Jul 23, 2007 by ######################################################################### # # Date: Nov. 2001 Authors: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner, Daniel Stoffler and TSRI # # revision: Guillaume Vareille # ######################################################################### # # $Header: /opt/cvs/python/packages/share1.5/NetworkEditor/macros.py,v 1.117 2013/10/08 01:27:44 sanner Exp $ # # $Id: macros.py,v 1.117 2013/10/08 01:27:44 sanner Exp $ # import re, string import Tkinter import weakref import types import datetime from NetworkEditor.net import Network, ImageNode from NetworkEditor.items import NetworkNode, NetworkItems from NetworkEditor.ports import InputPort, OutputPort ################################################################ # to avoid multiple list enclosure, input port macro node # must always be singleConnection. # to have multiple connections, # the solution is to duplicate the input port in the macronode # (there is no such need for the output port macronode) ################################################################ class MacroNetwork(Network): """class to hold all the information about a bunch of nodes and connections """ def __init__(self, macroNode, name='Noname'): Network.__init__(self, name) self.macroNode = macroNode self._originalNodes = [] # list of nodes that belong to a node # library macro. The list is populated # upon adding the macro to a network # and is used for saving macro networks self._originalConnections = [] # list of connections that belong to a # node library macro. def setExec(self,status): """Loop recursively over nodes in nested macros and set MacroNetwork's execStatus to 'stop' """ self.execStatus = status for n in self.nodes: if isinstance(n, MacroNode): n.macroNetwork.setExec(status) def stop(self): """set execStatus to 'stop'. The execution will stop after completion of the current node. For a MacroNode, we need to find the master network and invokde its stop method """ master = self.macroNode.network while isinstance(master, MacroNetwork): master = self.macroNode.network master.stop() class MacroBase: def getPortName(self, port): return "%s_%d_%s_%d"%(self.name.replace(' ', '_'), id(self), port.name, port._id) def deletePort(self, p, resize=True, updateSignature=False): # call base class method # NOTE: we do this, because NetworkNode also subclasses this method # and does some more stuff (like updating the node source code) NetworkItems.deletePort(self, p, resize) def updateCode(self, port='ip', action=None, tagModified=True, **kw): """we do not want to change the compute functions of the macro nodes upon adding/deleting ports""" pass class MacroNode(MacroBase, NetworkNode): def __init__(self, name='NoName', sourceCode=None, originalClass=None, constrkw={}, library=None, progbar=0, macroNetworkClass=MacroNetwork, **kw): apply( NetworkNode.__init__, (self, name, sourceCode, originalClass, constrkw, library, progbar), kw) self.expanded = 0 self.isSchedulingNode = True # the outputPort node will schedule all # children of the macro node self.hasRun = 0 # set to 1 when the node is executed. This is used to # force the execution of root nodes in macro the first # time the macro network is executed self.mouseAction[''] = self.expand self.macroNetwork = macroNetworkClass(self, self.name) # add special nodes for input and output self.macroNetwork.ipNode = MacroInputNode(self, 'input Ports') self.macroNetwork.opNode = MacroOutputNode(self, 'output Ports') code = """def doit(self, *args): # propagate forceExecution status if self.network.forceExecution: self.macroNetwork.forceExecution = self.network.forceExecution # copy args to output ports of the macroIpNode macroIp = self.macroNetwork.ipNode kw = {} i = 0 newData = 0 for p, data in map(None, self.inputPorts, args): if self.macroNetwork.forceExecution or self.inputPorts[i].hasNewValidData(): macroIp.newData = 1 kw[macroIp.outputPorts[p.number+1].name] = data i = i + 1 if not macroIp.newData and not self.macroNetwork.forceExecution: return # output the data on the IPnode output ports apply( macroIp.outputData, (), kw ) # run root nodes inside macroNetwork (if needed) # at the first execution of a macro node all root nodes have to run if not self.hasRun or self.macroNetwork.forceExecution: # list of roots with InputPortNode as last one # because InputPortNode schedules its children roots = self.macroNetwork.rootNodes[:] self.hasRun = 1 else: roots = [] for n in self.macroNetwork.rootNodes: if self.macroNetwork.forceExecution: n.forceExecution = 1 roots.append(n) elif n.newData: roots.append(n) if len(roots): #print 'MacroNode doit', roots, self.hasRun, self.macroNetwork.forceExecution subRunNodes = self.macroNetwork.getSubRunNodes(roots = roots) # output the data on the IPnode output ports apply( macroIp.outputData, (), kw ) self.macroNetwork.runNodes(subRunNodes) """ self.setFunction(code) def isMacro(self): """Returns False if this node is not a MacroNode, returns True if MacroNode""" return True def schedule_cb(self, event=None): #print "MacroNode.schedule_cb" self.macroNetwork.forceExecution = 1 NetworkNode.schedule_cb(self) def beforeRemovingFromNetwork(self): # if a macro node is deleted, we also have to delete its macro network ed = self.getEditor() ed.deleteNetwork(self.macroNetwork) def beforeAddingToNetwork(self, network): # here we do have a valid network self.macroNetwork.vEditor = weakref.ref(network.getEditor()) self.macroNetwork.runOnNewData = network.runOnNewData def afterAddingToNetwork(self): ed = self.getEditor() ed.addNetwork(self.macroNetwork) self.macroNetwork.addNode(self.macroNetwork.ipNode, 200, 20) self.macroNetwork.addNode(self.macroNetwork.opNode, 200, 280) if ed.hasGUI: self.macroNetwork.buildIcons() # currently, we are now inside the macro network but the macro node # was not flagged "expanded=1" yet, so to get back in sync, we do: self.expand() def afterBuildingMacroNetwork(self): # is called after the macro next work was created pass def buildIcons(self, canvas, posx, posy): NetworkNode.buildIcons(self, canvas, posx, posy) myfont = list(self.getFont()) if not 'bold' in myfont: myfont.append('bold') self.setFont(tuple(myfont)) self.autoResizeX() try: self.menu.index('expand') except: self.menu.add_separator() self.menu.add_command(label='expand', command=self.expand) self.menu.add_command(label='shrink', command=self.shrink) self.addSaveNodeMenuEntries() def expand(self, event=None): ed = self.getEditor() if not self.expanded: ed.menuButtons['Networks'].menu.entryconfig( "Close...", state=Tkinter.DISABLED) ed.networkArea.showpage(self.macroNetwork.name) self.expanded = 1 else: ed.networkArea.selectpage(self.macroNetwork.name) def shrink(self, event=None): ed = self.getEditor() if not self.expanded: return ed.menuButtons['Networks'].menu.entryconfig( "Close...", state=Tkinter.NORMAL) self.expanded = 0 ed.networkArea.hidepage(self.macroNetwork.name) # make my own network the current one # Note: because we can have nested macros, we have to find the one # macro node that is currently expanded net = self.network while isinstance(net, MacroNetwork): if net.macroNode.expanded: ed.setNetwork(net) return else: net = net.macroNode.network # else: we have to set the network to what we got here, or we loose # the focus ed.setNetwork(net) def getRootMacro(self): """This method is used to find the root macro node for nested macros""" result = [self] parent = self while isinstance(parent.network, MacroNetwork): parent = parent.network.macroNode result.append(parent) return result[::-1] def getNodeDefinitionSourceCode(self, networkName, indent="", ignoreOriginal=False): """This method builds the text-string to describe a macro node in a saved file.""" lines = [] nodeName = self.nameInSavedFile = self.getUniqueNodeName() ############################################################### # add lines to import node from macros.py, add macro to network ############################################################### txt = NetworkNode.getNodeDefinitionSourceCode( self, networkName, indent) lines.extend(txt) ## if self.library: ## txt = NetworkNode.getNodeDefinitionSourceCode( ## self, networkName, indent) ## lines.extend(txt) ## else: ## if not self._original: ## txt1 = 'from NetworkEditor.macros import MacroNode\n' ## txt2 = "%s = MacroNode(name='%s')\n"%(nodeName, self.name) ## txt3 = "%s.addNode(%s, %d, %d)\n"%( ## networkName, nodeName, self.posx, self.posy) ## lines.append(indent+txt1) ## lines.append(indent+txt2) ## lines.append(indent+txt3) ############################################################### # add lines to add all macro nodes first, recursively ############################################################### # We have to add all macro nodes first, and then start in the leaf # macros, add the nodes there, and work our way back up # (because of connections) for node in self.macroNetwork.nodes: if isinstance(node, MacroNode): txt1 = node.getNodeDefinitionSourceCode( nodeName+".macroNetwork", indent) lines.extend(txt1) ############################################################### # check if an original node was deleted ############################################################### deletedNodes = 0 # count how many nodes have already been deleted # NOTE: because we add a line for each node we want to delete, # we have to decrement the orignumber by how many nodes we already # deleted for orignode, orignumber in self.macroNetwork._originalNodes: if orignode not in self.macroNetwork.nodes: # add handle to macro node lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) # add line to delete node txt = "%s.deleteNodes([%s])\n"%( nodeName+".macroNetwork", nodeName+".macroNetwork.nodes["+str(orignumber-deletedNodes)+"]") deletedNodes += 1 lines.append(indent+txt) ############################################################### # check if an original connection was deleted ############################################################### for origconn, p1, n1, p2, n2 in self.macroNetwork._originalConnections: if origconn not in self.macroNetwork.connections: # only generate code if the nodes still exist (if not this # means, the node has been deleted which will delete the # connections so we do not have to add code, # and also if ports are not None. If ports were None means # that the user deleted the port which we catch below and # this also deletes the connection so we need not add code here invalid = False # this indicates a connection to a deleted # node or port # port1 or port2 deleted? if type(p1) == types.NoneType or type(p2) == types.NoneType: invalid = True # node1 deleted? if n1 not in self.macroNetwork.nodes: invalid = True # node2 deleted? if n2 not in self.macroNetwork.nodes: invalid = True # only if both ports still exist do the following if not invalid: lines = self.checkIfNodeForSavingIsDefined( lines, networkName, indent) node1 = nodeName+".macroNetwork.nodes[%d]"%( self.macroNetwork.nodeIdToNumber(n1._id),) node2 = nodeName+".macroNetwork.nodes[%d]"%( self.macroNetwork.nodeIdToNumber(n2._id),) txt = "%s.deleteConnection(%s, '%s', %s, '%s')\n"%( nodeName+".macroNetwork", node1, p1.name, node2, p2.name) lines.extend(indent+txt) ############################################################### # add lines to add/modify nodes in a macro network ############################################################### for node in self.macroNetwork.nodes: if not isinstance(node, MacroNode): txt2 = node.getNodeDefinitionSourceCode( nodeName+".macroNetwork", indent) lines.extend(txt2) ############################################################### # add lines to create connections in macro networks ############################################################### macroNetworkName = "%s.macroNetwork"%nodeName if len(self.macroNetwork.connections): lines.append( '\n'+indent+"## saving connections for network "+\ "%s ##\n"%self.name) lines.append(indent+'%s.freeze()\n'%macroNetworkName) for conn in self.macroNetwork.connections: lines.extend(conn.getSourceCode( macroNetworkName, False, indent)) lines.append(indent+'%s.unfreeze()\n'%macroNetworkName) ############################################################### # add lines to configure dynamically created MacroOutputPorts # Note: right now we catch the port name ############################################################### txt = self.macroNetwork.ipNode.getDynamicPortsModificationSourceCode( macroNetworkName, indent, ignoreOriginal) lines.extend(txt) ############################################################### # add lines to configure dynamically created MacroOutputPorts # Note: right now we catch the name and "singleConnection" # which might be modified by the user ############################################################### txt = self.macroNetwork.opNode.getDynamicPortsModificationSourceCode( macroNetworkName, indent, ignoreOriginal) lines.extend(txt) ############################################################### # Also, catch singleConnection events on the MacroNode input ports # if they were changed compared to the node that is connected to # the MacroInput node. We can do this only after we have formed # the connections inside the macro network ############################################################### # find node connected txt = [] for ip in self.inputPorts: # add line to allow the renaming of the macronode's input ports txt.append(indent+\ "%s.inputPorts[%d].configure(name='%s')\n"%( nodeName, ip.number, ip.name) ) txt.append(indent+\ "%s.inputPorts[%d].configure(datatype='%s')\n"%( nodeName, ip.number, ip.datatypeObject['name']) ) txt.append(indent+"## configure MacroNode input ports\n") lines.extend(txt) # add line to allow the renaming of the macronode's output ports txt = [] for op in self.outputPorts: txt.append(indent+\ "%s.outputPorts[%d].configure(name='%s')\n"%( nodeName, op.number, op.name) ) txt.append(indent+\ "%s.outputPorts[%d].configure(datatype='%s')\n"%( nodeName, op.number, op.datatypeObject['name']) ) txt.append(indent+"## configure MacroNode output ports\n") lines.extend(txt) ############################################################### # Shrink the macro node ############################################################### lines.append(indent+nodeName+".shrink()\n") ############################################################### # configure macro node: Freeze, etc ############################################################### ind, txt = self.getNodeSourceCodeForNode(networkName, indent, ignoreOriginal) lines.extend(txt) return lines def getNodeSourceCodeForPorts(self, networkName, indent="", full=0): """We need to override the baseclass method since we do not want to save port modifications to a macro node""" lines = [] return indent, lines def getAfterConnectionsSourceCode(self, networkName, indent="", ignoreOriginal=False): """This method allows users to add source code that needs to be generated after nodes were connected, which might trigger the creation of new ports.""" lines = [] for node in self.macroNetwork.nodes: lines.extend(node.getAfterConnectionsSourceCode( networkName, indent, ignoreOriginal) ) return lines def resetTags(self): """This method subclasses NetworkNode.resetTags(). Used to reset the attributes _modified and _original in node, ports, widgets, conn""" NetworkNode.resetTags(self) for node in self.macroNetwork.nodes: node.resetTags() self.macroNetwork.ipNode._setOriginal(False) self.macroNetwork.opNode._setOriginal(False) def buildOriginalList(self): """Summary: used to catch delete node and delete connection events in the macro network. Puts nodes and connections into the macronetwork._originalNodes and macronetwork._originalConnections lists. This is used to find out if a node or connection in a macro network of a macro node that came from a node library was deleted.""" macNet = self.macroNetwork macNet._originalNodes = [] macNet._originalConnections = [] # store instance of node and current number in net.nodes for node in macNet.nodes: macNet._originalNodes.append( ( node, self.macroNetwork.nodeIdToNumber(node._id) ) ) # store instance of original connection, port1, node1, port2, node2 for conn in macNet.connections: macNet._originalConnections.append( ( conn, conn.port1, conn.port1.node, conn.port2, conn.port2.node) ) def getNodeSourceCode(self, className, networkName='self.masterNetwork', indent="", dependencies=True): """This method is called through the 'save source code' mechanism. The idea here is to generate source code for a macro network that can be put into a node library. This is not for saving networks dependencies: True/False False: the saved macro node is fully independent from it's original macro (if any). True : if relevant, the macro node is saved as a subclass of an original macro, only modifications from the original are saved (not implemented yet) in both case, saved macros depend as well of other embeded macros and of nodes from libraries. """ lines = [] self.nameInSavedFile = self.getUniqueNodeName() ## get header descr headerBlock = self.getHeaderBlock(className, indent) lines.extend(headerBlock) ## get __init__ descr, and proper indent initBlock = self.getInitBlock(className, indent) lines.extend(initBlock) ## get library import cache ## then write libray import code cache = {'files':[]} cache = self.network.buildLibraryImportCache( cache, self.macroNetwork, selectedOnly=False) ## get beforeAddingToNetwork descr beforeBlock = self.getBeforeBlock(cache, indent) lines.extend(beforeBlock) ## get afterAddingToNetwork descr afterBlock = self.getAfterBlock(cache, networkName, indent) lines.extend(afterBlock) return lines #################################################### #### Helper Methods follow to generate save file ### #################################################### def getHeaderBlock(self, className, indent=""): lines = [] lNow = datetime.datetime.now().strftime("%A %d %B %Y %H:%M:%S") lCopyright = """######################################################################## # # Vision Macro - Python source code - file generated by vision # %s # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Daniel Stoffler, Michel Sanner and TSRI # # revision: Guillaume Vareille # ######################################################################### # # $%s$ # # $%s$ # """%(lNow, "Header:", "Id:") # if directly in the txt, CVS fills these fields lines.append(lCopyright) #lines.append(indent+"from NetworkEditor.macros import MacroNode\n") #lines.append(indent+"class "+className+"(MacroNode):\n") mod = self.originalClass.__module__ klass = self.originalClass.__name__ if klass != 'c': lines.append(indent+"from NetworkEditor.macros import MacroNode\n") if className == klass: txt2 = "class %s(MacroNode):\n"%(className) lines.append(indent+txt2) else: txt1 = "from %s import %s\n"%(mod,klass) lines.append(indent+txt1) txt2 = "class %s(%s):\n"%(className,klass) lines.append(indent+txt2) if self.originalClass.__doc__ is not None: lines.append(indent+' \"\"\"'+self.originalClass.__doc__) lines.append('\"\"\"\n') return lines def getInitBlock(self, className, indent=""): nodeName = self.name klass = self.originalClass.__name__ indent = indent + 4*" " # move forward 4 lines = [] lines.append("\n") lines.append(indent+"def __init__(self, constrkw={}, name='"+\ nodeName+"', **kw):\n") indent = indent + 4*" " # move forward 4 lines.append(indent+"kw['name'] = name\n") ## if self.library: ## lines.append(indent+"apply(%s.__init__, (self,), kw)\n"%klass) ## else: lines.append(indent+"apply( MacroNode.__init__, (self,), kw)\n") # add text for widgetDescr dict for p in self.inputPorts: w = p.widget if w is None: continue v = w.getConstructorOptions() v['class'] = w.__class__.__name__ lines.append(indent+"""self.widgetDescr['%s'] = %s\n"""%(p.name, str(v)) ) return lines def getBeforeBlock(self, cache, indent=""): lines = [] klass = self.originalClass.__name__ indent = indent + 4*" " # move forward 4 lines.append("\n") lines.append(indent+"def beforeAddingToNetwork(self, net):\n") indent = indent + 4*" " # move forward 4 ## if self.library: ## lines.append(indent+"%s.beforeAddingToNetwork(self, net)\n"%klass) ## else: lines.append(indent+"MacroNode.beforeAddingToNetwork(self, net)\n") li = self.macroNetwork.getLibraryImportCode( cache, indent, editor="net.editor", networkName='net', #importOnly=True, loadHost=True) importOnly=False, loadHost=True) # MS May 2012 if importOnly=True the macro fails to add the libraries # of nodes in the macronetwork which creates problems when the network # is saved again because node.library has no modName and varName set lines.extend(li) return lines def getAfterBlock(self, cache, networkName, indent="", ignoreOriginal=False): # used to save macro node # add text for the 'def afterAddingToNetwork' method aftBlock = [] aftBlock.append('\n') indent = indent + 4*" " # move forward 4 aftBlock.append(indent+"def afterAddingToNetwork(self):\n") indent = indent + 4 * " " # move forward 4 aftBlock.append(indent+"masterNet = self.macroNetwork\n") aftBlock.append(indent+"masterNet.editor._loadingNetwork = True\n") ## call base class afterAddingToNetwork ## if self.library: ## klass = self.originalClass.__name__ ## mod = self.originalClass.__module__ ## aftBlock.append(indent+"from %s import %s\n"%(mod,klass) ) ## aftBlock.append(indent+"%s.afterAddingToNetwork(self)\n"%klass) ## else: aftBlock.append( indent+"from NetworkEditor.macros import MacroNode\n") aftBlock.append(indent+"MacroNode.afterAddingToNetwork(self)\n") ## loading libraries (we need these to set node libraries) li = self.macroNetwork.getLibraryImportCode( cache, indent, editor="self.editor",networkName='net', importOnly=True) aftBlock.extend(li) #lib = self.library #self.library = None ## add nodes to macro network, connect nodeName = self.getUniqueNodeName() aftBlock.append(indent+"## building macro network ##\n") aftBlock.append(indent+"%s = self\n"%nodeName) ed = self.getEditor() data = self.macroNetwork.getNetworkCreationSourceCode( "self.macroNetwork", False, indent, ignoreOriginal=True, importOnly=True) aftBlock.extend(data) # add code for MacroInpuPorts port modifications (such as # name txt = self.macroNetwork.ipNode.getDynamicPortsModificationSourceCode( "self.macroNetwork", indent, ignoreOriginal=True) aftBlock.extend(txt) # add code for MacroOutpuPorts port modifications (such as # singleConnection txt = self.macroNetwork.opNode.getDynamicPortsModificationSourceCode( "self.macroNetwork", indent, ignoreOriginal=True) aftBlock.extend(txt) ############################################################### # to allow the macro in the lib to have the correct port names ############################################################### txt = [] txt.append(indent+"## configure MacroNode input ports\n") for ip in self.inputPorts: # add line to allow the renaming of the macronode's input ports txt.append(indent+\ "%s.inputPorts[%d].configure(name='%s')\n"%( nodeName, ip.number, ip.name) ) txt.append(indent+\ "%s.inputPorts[%d].configure(datatype='%s')\n"%( nodeName, ip.number, ip.datatypeObject['name']) ) txt.append(indent+"## configure MacroNode output ports\n") for op in self.outputPorts: txt.append(indent+\ "%s.outputPorts[%d].configure(name='%s')\n"%( nodeName, op.number, op.name) ) txt.append(indent+\ "%s.outputPorts[%d].configure(datatype='%s')\n"%( nodeName, op.number, op.datatypeObject['name']) ) aftBlock.extend(txt) aftBlock.append(indent+"masterNet.editor._loadingNetwork = False\n") #needed for "save source code" aftBlock.append("\n") aftBlock.append(indent+"%s.shrink()\n"%nodeName) aftBlock.append("\n") # add line to reset all tags aftBlock.append(indent+"## reset modifications ##\n") aftBlock.append(indent+"%s.resetTags()\n"%nodeName) aftBlock.append(indent+"%s.buildOriginalList()\n"%nodeName) aftBlock.append(indent+"%s.afterBuildingMacroNetwork()\n"%nodeName) #self.library = lib return aftBlock class MacroImageNode(MacroNode, ImageNode): def __init__(self, name='NoName', library=None, iconFileName=None, iconPath='', macroNetworkClass=MacroNetwork, **kw): constrkw = kw.pop('constrkw', None) MacroNode.__init__(self, name, None, None, constrkw, library, None, macroNetworkClass, **kw) self.center= [0,0] # coords of node's center in canvas self.rotAngle = 0.0 # keep track of rotation angle self.selectOptions = {} self.deselectOptions = {} self.iconPath = iconPath self.iconFileName = iconFileName # create the node renderer from NetworkEditor.drawNode import CairoNodeRenderer self.renderer = CairoNodeRenderer() def buildIcons(self, canvas, posx, posy): ImageNode.buildIcons(self, canvas, posx, posy) try: self.menu.index('expand') except: self.menu.add_separator() self.menu.add_command(label='expand', command=self.expand) self.menu.add_command(label='shrink', command=self.shrink) self.addSaveNodeMenuEntries() class MacroInputNode(MacroBase, NetworkNode): def __init__(self, macroNode=None, name='NoName', sourceCode=None, originalClass=None, constrkw={}, library=None, progbar=0, **kw): self.macroNode = macroNode # node representing the network apply( NetworkNode.__init__, (self, name, None, None), kw) self.isSchedulingNode = True # this node's function will schedule all # children of ports with new data self.outputPortsDescr.append({'name':'new', 'balloon':'Add new input port'}) self.readOnly = 1 # this node should never be edited #FIXME we should remove edit entry from node's menu # same for MacroInputNode code = """def doit(self, *args): # run all children of macroIpNode of ports that have new data # we add scheduled nodes to a dict so that a given node is not triggered # twice or more portsWithNewData = [] for ip, op in map(None, self.macroNode.inputPorts, self.outputPorts[1:]): if self.network.forceExecution or ip.hasNewValidData(): portsWithNewData.append(op) #print 'IN MacroInputNode, scheduling:', portsWithNewData self.scheduleChildren(portsWithNewData)\n""" self.setFunction(code) if self.macroNode is not None: self.mouseAction[''] = self.macroNode.shrink def buildIcons(self, canvas, posx, posy): NetworkNode.buildIcons(self, canvas, posx, posy) myfont = list(self.getFont()) if not 'bold' in myfont: myfont.append('bold') self.setFont(tuple(myfont)) self.autoResizeX() try: self.menu.index('shrink') except: self.menu.add_command(label='shrink',command=self.macroNode.shrink) def getNodeDefinitionSourceCode(self, networkName, indent="", ignoreOriginal=False): lines = [] if not self._modified and ignoreOriginal is False: return lines #lines = self.checkIfNodeForSavingIsDefined(lines, networkName, indent) nodeName = self.getUniqueNodeName() # always add a handle to the InputPort node txt = "%s = %s.ipNode\n"%(nodeName, networkName) lines.append(indent+txt) dummy, txt1 = self.getNodeSourceCodeForNode(networkName, indent, ignoreOriginal) lines.extend(txt1) # since position is usually handled by addNode, we have to add # a line here to set the correct position if self.posx != 200 and self.posy != 20: txt2 = "%s.move(%d, %d)\n"%(nodeName, self.posx, self.posy) lines.append(indent+txt2) return lines def getDynamicPortsModificationSourceCode(self, networkName, indent="", ignoreOriginal=False): """This method returns source code to configure newly generated output ports. Currently, we only catch the renaming of the ports. """ lines = [] if self._original is True and ignoreOriginal is False: return lines nodeName = self.getUniqueNodeName() # catch port modification "singleConnection": txt = [] # add line to allow the renaming of the macronode's output ports for op in self.outputPorts[1:]: txt = self.checkIfNodeForSavingIsDefined( txt, networkName, indent) txt.append(indent+\ "%s.outputPorts[%d].configure(name='%s')\n"%( nodeName, op.number, op.name) ) if len(txt): lines.append("\n") lines.append(indent+"## modifying MacroInputNode dynamic ports\n") lines.extend(txt) return lines def checkIfNodeForSavingIsDefined(self, lines, networkName, indent): """This method fixes a problem with saving macros that come from a node library. If only a widget value has changed, we do not have a handle to the node. Thus, we need to create this additional line to get a handle""" ed = self.getEditor() nodeName = self.getUniqueNodeName() if ed._tmpListOfSavedNodes.has_key(nodeName) is False: txt = "%s = %s.ipNode\n"%(nodeName, networkName) lines.append(indent+txt) ed._tmpListOfSavedNodes[nodeName] = self return lines class MacroOutputNode(MacroBase, NetworkNode): def __init__(self, macroNode=None, name='NoName', sourceCode=None, originalClass=None, constrkw={}, library=None, progbar=0, **kw): self.macroNode = macroNode # node representing the network apply( NetworkNode.__init__, (self, name, None, None), kw) self.inputPortsDescr.append({'name':'new', 'required':False, 'balloon':'Add new output port'}) self.readOnly = 1 # this node should never be edited #FIXME we should remove edit entry from node's menu # same for MacroInputNode code = """def doit(self, *args): # output data on macrOpNode forceExecution = self.network.forceExecution for op, ip in map(None, self.macroNode.outputPorts, self.inputPorts[1:]): # valid, new or not we put whatever data we have on the output ports of the macroNode if len(ip.connections)==1: op.outputData(ip.getData()) else: op.outputData(self.flatten(ip.getData())) # reset network force execution flag after the macro network ran self.network.forceExecution = 0 # force the parent network to execute. This is needed for instance when # there are 2 levels of macros with an iterate at the lowest level self.macroNode.network.forceExecution = forceExecution # if we are in the MacroNetwork: schedule the children of the MacroNode # to run ed = self.getEditor() if 1: #self.network==ed.currentNetwork or forceExecution: portsWithNewData = [] for op, ip in map(None, self.macroNode.outputPorts, self.inputPorts[1:]): if ip.hasNewValidData(): portsWithNewData.append(op) #print 'ABC', forceExecution, portsWithNewData #print 'MacroOutputNode scheduling:', portsWithNewData self.macroNode.scheduleChildren(portsWithNewData)\n""" self.setFunction(code) if self.macroNode is not None: self.mouseAction[''] = self.macroNode.shrink def flatten(self, data): """When input port has multiple parents we want to concatenate the data provided by all parents as if it came from a single parent since the output port of the macro will provide all this data on a single port. """ if isinstance(data, list): flatdata = [] for d in data: if isinstance(d, list): flatdata.extend(d) else: return data return flatdata else: return data def buildIcons(self, canvas, posx, posy): NetworkNode.buildIcons(self, canvas, posx, posy) myfont = list(self.getFont()) if not 'bold' in myfont: myfont.append('bold') self.setFont(tuple(myfont)) self.autoResizeX() try: self.menu.index('shrink') except: self.menu.add_command(label='shrink',command=self.macroNode.shrink) def getNodeDefinitionSourceCode(self, networkName, indent="", ignoreOriginal=False): lines = [] if not self._modified and ignoreOriginal is False: return lines nodeName = self.getUniqueNodeName() #lines = self.checkIfNodeForSavingIsDefined(lines, networkName, indent) # always define a handle to OutputPorts Node txt = "%s = %s.opNode\n"%(nodeName, networkName) lines.append(indent+txt) # save node modifications dummy, txt1 = self.getNodeSourceCodeForNode(networkName, indent, ignoreOriginal) lines.extend(txt1) # since position is usually handled by addNode, we have to add # a line here to set the correct position if self.posx != 200 and self.posy != 280: txt2 = "%s.move(%d, %d)\n"%(nodeName, self.posx, self.posy) lines.append(indent+txt2) return lines def getDynamicPortsModificationSourceCode(self, networkName, indent="", ignoreOriginal=False): """This method returns source code to configure newly generated input ports. Currently, we only catch the event 'singleConnection'.""" lines = [] if self._original is True and ignoreOriginal is False: return lines nodeName = self.getUniqueNodeName() # catch port modification "singleConnection": txt = [] for i in range(1, len(self.inputPorts)): p = self.inputPorts[i] txt = self.checkIfNodeForSavingIsDefined( txt, networkName, indent) status = p.singleConnection if p.singleConnection == 'auto': status = "'auto'" else: status = p.singleConnection txt.append(indent+\ "%s.inputPorts[%d].configure(singleConnection=%s)\n"%(nodeName,i,status)) # add line to allow the renaming of the macronode's output ports for ip in self.inputPorts[1:]: txt.append(indent+\ "%s.inputPorts[%d].configure(name='%s')\n"%( nodeName, ip.number, ip.name) ) if len(txt): lines.append("\n") lines.append(indent+"## modifying MacroOutputNode dynamic ports\n") lines.extend(txt) return lines def checkIfNodeForSavingIsDefined(self, lines, networkName, indent): """This method fixes a problem with saving macros that come from a node library. If only a widget value has changed, we do not have a handle to the node. Thus, we need to create this additional line to get a handle""" ed = self.getEditor() nodeName = self.getUniqueNodeName() if ed._tmpListOfSavedNodes.has_key(nodeName) is False: txt = "%s = %s.opNode\n"%(nodeName, networkName) lines.append(indent+txt) ed._tmpListOfSavedNodes[nodeName] = self return lines NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/naviMap.py0000644000175000017500000001546712055234722023262 0ustar moellermoeller######################################################################### # # Date: Nov. 2012 Author: Michel Sanner # # sanner@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner and TSRI # ######################################################################### from NetworkEditor.net import AddNodeEvent, ConnectNodes, \ DeleteNodesEvent, DeleteConnectionsEvent from mglutil.util.callback import CallbackFunction import weakref class NavigationMap: """ draw a small representation of the total canvas area with a rectangle showing the outline of the visible portion of the map """ def __init__(self, network, scrolledCanvas, mapCanvas, scaleFactor=0.05): self.network = weakref.ref(network) editor = network.editor self.editor = weakref.ref(editor) self.mapCanvas = mapCanvas # where we draw the map self.sc_canvas = scrolledCanvas # where the network self.scaleFactor = scaleFactor canvas = self.netCanvas = scrolledCanvas.component('canvas') # find total size of the canvas bbox = map(int, canvas.configure('scrollregion')[-1]) # compute map width and height self.width = (bbox[2]-bbox[0])*scaleFactor self.height = (bbox[3]-bbox[1])*scaleFactor self.upperLeft = (0,0) # override callback for scroll bar to moce map to keep # it in the upperLeft corner of the Network cb = CallbackFunction(self.moveMapx, 'y') scrolledCanvas.component('vertscrollbar').configure(command=cb) cb = CallbackFunction(self.moveMapx, 'x') scrolledCanvas.component('horizscrollbar').configure(command=cb) # register interest in event that add items to the minimap editor.registerListener(AddNodeEvent, self.handleAddNode) editor.registerListener(ConnectNodes, self.handleConnectNodes) editor.registerListener(DeleteNodesEvent, self.handleDeleteNodes) editor.registerListener(DeleteConnectionsEvent, self.handleDeleteConnections) # create navi map outline self.outlineCID = canvas.create_rectangle( 0, 0, self.width, self.height, outline='black', tags=('navimap',)) # draw visible window outline self.visibleWinOutlineCID = canvas.create_rectangle( 0, 0, 10, 10, outline='blue', width=2, tags=('navimap','visibleWin',)) mapCanvas.tag_bind(self.visibleWinOutlineCID, "", self.moveVisibleWin_cb) canvas.tag_bind(self.visibleWinOutlineCID, "", self.moveCanvas) canvas.tag_bind(self.visibleWinOutlineCID, "", self.moveCanvasEnd) def __del__(self): self.unregisterCallBacks() def unregisterCallBacks(self): editor = self.editor() editor.unregisterListener(AddNodeEvent, self.handleAddNode) editor.unregisterListener(ConnectNodes, self.handleConnectNodes) editor.unregisterListener(DeleteNodesEvent, self.handleDeleteNodes) editor.unregisterListener(DeleteConnectionsEvent, self.handleDeleteConnections) def moveVisibleWin_cb(self, event): self.lastx = event.x self.lasty = event.y canvas = self.mapCanvas canvas.configure(cursor='hand2') canvas.itemconfigure(self.visibleWinOutlineCID, outline='green') def moveCanvas(self, event=None): ed = self.editor() dx = (event.x - self.lastx)/self.scaleFactor dy = (event.y - self.lasty)/self.scaleFactor canvas = self.netCanvas xo = max(0, canvas.canvasx(0)+dx) yo = max(0, canvas.canvasy(0)+dy) canvas.xview_moveto(xo/float(ed.totalWidth)) canvas.yview_moveto(yo/float(ed.totalHeight)) self.lastx = event.x self.lasty = event.y self.moveMapx(None, None, None) canvas.update_idletasks() def moveCanvasEnd(self, event=None): num = event.num canvas = self.mapCanvas canvas.configure(cursor='') def placeVisibleWin(self): if self.network().showNaviMap: netCanvas = self.netCanvas mapCanvas = self.mapCanvas width = netCanvas.winfo_width() height = netCanvas.winfo_height() if width==1 or height==1: netCanvas.after(100, self.placeVisibleWin) x0 = netCanvas.canvasx(0) y0 = netCanvas.canvasy(0) sc = self.scaleFactor mapCanvas.coords(self.visibleWinOutlineCID, x0+x0*sc, y0+y0*sc, x0+(x0+width)*sc, y0+(y0+height)*sc) mapCanvas.itemconfigure(self.visibleWinOutlineCID, outline='blue') def moveMapx(self, direction, how, *args): # call default callback to scroll canvas canvas = self.netCanvas if direction=='y': canvas.yview(how, *args) elif direction=='x': canvas.xview(how, *args) # canvas coords of upper left corner of visible part x = canvas.canvasx(0) y = canvas.canvasy(0) x0, y0 = self.upperLeft canvas.move('navimap', x-x0, y-y0) self.upperLeft = (x, y) self.placeVisibleWin() ## ## do not use self.mapCanvas but instead use object.network.naviMap as ## ed.refreshNet_cb will recreate a new NaviMap which will be diffrent ## from the one register in the call back ## def handleAddNode(self, event): n = event.node naviMap = n.network.naviMap if n.network == naviMap.network(): n.drawMapIcon(naviMap) def handleConnectNodes(self, event): c = event.connection naviMap = c.network.naviMap if c.network == naviMap.network(): c.drawMapIcon(c.network.naviMap) def handleDeleteNodes(self, event): for n in event.nodes: naviMap = n.network.naviMap if n.network != naviMap.network(): continue # this node is not on this map mapCanvas = naviMap.mapCanvas mapCanvas.delete(n.naviMapID) for p in n.inputPorts: for c in p.connections: mapCanvas.delete(c.naviMapID) for p in n.outputPorts: for c in p.connections: mapCanvas.delete(c.naviMapID) def handleDeleteConnections(self, event): for c in event.connection: naviMap = c.network.naviMap if c.network == naviMap.network(): c.network.naviMap.mapCanvas.delete(c.naviMapID) def printEvent(self, event): print event def clearMap(self): pass NetworkEditor-1.5.7~rc1+cvs.20140424/NetworkEditor/net.py0000644000175000017500000041065712315360544022456 0ustar moellermoeller######################################################################### # # Date: Nov. 2001 Author: Michel Sanner, Daniel Stoffler # # sanner@scripps.edu # stoffler@scripps.edu # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Michel Sanner, Daniel Stoffler and TSRI # # revision: Guillaume Vareille # ######################################################################### import sys import os import types, math, string, threading, weakref import Tkinter, Pmw import traceback, time from time import time, sleep import re from tkSimpleDialog import askstring from mglutil.gui import widgetsOnBackWindowsCanGrabFocus from mglutil.util.callback import CallbackManager, CallBackFunction from mglutil.gui.Misc.Tk.KeybdModMonitor import KeyboardModifierMonitor from NetworkEditor.itemBase import NetworkItemsBase from NetworkEditor.items import NetworkNode, NetworkNodeBase, NetworkConnection, ImageNode from NetworkEditor.ports import InputPort, OutputPort, \ RunChildrenInputPort, RunNodeInputPort from NetworkEditor.flow import ExecutionThread, AfterExecution from NetworkEditor.itemBase import UserPanel from Tkinter import * from Glyph import Glyph from mglutil.events import Event class AddNodeEvent(Event): def __init__(self, network, node, x, y): """ """ self.timestamp = time() self.network = network self.node = node self.position = (x,y) class ConnectNodes(Event): def __init__(self, network, connection): """ """ self.timestamp = time() self.network = network self.connection = connection class DeleteNodesEvent(Event): def __init__(self, network, nodes): """ """ self.timestamp = time() self.network = network self.nodes = nodes class DeleteConnectionsEvent(Event): def __init__(self, network, connections): """ """ self.timestamp = time() self.network = network self.connection = connections class reference_value: def __init__(self, value): self.value = value import socket from select import select class Communicator: """This class provides support for socket-based communication with a network that is running without a GUI """ def __init__(self, network, host='', portBase=50001, listen=5): self.network = network self.host = host self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) i = 0 while 1: try: s.bind((self.host, portBase+i)) break except Exception,e: print e i += 1 if i==10: raise RuntimeError, "Communicator unable to bind" #if e.args[0]==98: # Address already in use #elif e.args[0]==22: # socket already bound s.listen(listen) s.setblocking(0) self.port = portBase+i self.connId = s.fileno() self.clientConnections = [] self.clientInIds = [] def clientConnect(self): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) i = 0 from time import sleep while 1: err = s.connect_ex((self.host, self.port)) if err == 111: sleep(0.2) i +=1 if i == 10: print "Communicator: failed to connect" return None continue break return s def handleCommunications(self): #print "handleCommunications" net = self.network ifd, ofd, efd = select([self.socket.fileno()], [], [], 0) if self.connId in ifd: conn, addr = self.socket.accept() self.clientConnections.append( (conn, addr) ) self.clientInIds.append( conn.fileno() ) print 'Connected by', addr if len(self.clientInIds)==0: return ifd, ofd, efd = select(self.clientInIds, [], [], 0) for conn in self.clientConnections: if conn[0].fileno() in ifd: input = conn[0].recv(4096) #os.read(_pid, 16384) if len(input)==0: # client diconnected self.clientInIds.remove(conn[0].fileno()) self.clientConnections.remove(conn) else: procid = os.getpid() print "process %d input"%procid, repr(input) #mod = __import__('__main__') #mod.__dict__['clientSocket'] = conn[0] #exec(input, mod.__dict__) from mglutil.util.misc import importMainOrIPythonMain lMainDict = importMainOrIPythonMain() lMainDict['clientSocket'] = conn[0] exec(input, lMainDict) class Network(KeyboardModifierMonitor, NetworkItemsBase): """class to hold all the information about a bunch of nodes and connections """ _nodesID = 0 # used to assign a node a unique number # accross all the networks. This is incremented # in the addNodes() method def connectToProcess(self, host, port): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) i = 0 from time import sleep while 1: err = s.connect_ex((host, port)) if err == 111: sleep(0.2) i +=1 if i == 10: print "Communicator: failed to connect" return None continue break return s def generateScript(self): """Create a Python script that corresponds to this network """ count = 0 src = "" ndict = {} # dump all doit methods for node in self.nodes: ndict[node] = [] funcsrc = node.sourceCode.replace('def doit(', node.name+str(count)+'doit(') src += fncscr+'\n' def __init__(self, name='Noname', origin='user'): KeyboardModifierMonitor.__init__(self) NetworkItemsBase.__init__(self, name) self.origin = origin # HACK MS, replace by event self.afterRunCb = None self.canvasFrame=None self.filename = None # file from which the network was loaded if # it is loaded from a file (includes path) self.origName = name # used to delete the Pmw.NoteBook page # correctly after network was renamed self.selectedNodes = [] self.selectedConnections = [] self.firstItemId = None self.canvas = None self.debug = False self.defaultConnectionMode = 'angles' self.defaultConnectionOptions = {} self.needsResetNodeCache = 1 self.hyperScaleRad = 200 # radius is pixel in which nodes scale to 1.0 self.scalingHyper = 0 self.selectionBox = None # canvas object used to draw selectionbox self.glyphSelect = None self.gselectionboxes = [] self.sbrev =0 self.circle = None self.rectangle = None self.currentcircle = None self.currentrectangle = None self.glyphselectionBox = None self.eventHandler = {} # stores callback managers for network events self.createCallbackManager('preSelection') doc = """Gets called every time the selection changes. No arguments.""" self.createCallbackManager('onSelectionChange',doc) self.createCallbackManager('loadLibrary') doc = """Gets called every time we save a network, to add source code at the very beginning of a saved network""" self.createCallbackManager('saveNetworkBegin', doc) doc = """Gets called every time we save a network, to add source code at the very end of a saved network""" self.createCallbackManager('saveNetworkEnd', doc) self.nodes = [] # list of nodes in network self.nodesById = {} # dictionary or nodes # (key: unique tag of the NetworkItem, # value: NetworkItem instance) self.rootNodes = [] # list of nodes with no parents, These are # used to run the network when a # node is added it appended tothat list # when a connection is created it is removed # each node has an isRootNode flag # these are used for turning picking event into netwokr objects self.inPortsId = {} # dictionary or ports # (key: unique id of the port, # value: (node, portnumber)) self.outPortsId = {} # dictionary or ports # (key: unique id of the port, # value: (node, portnumber)) self.connections = [] self.connById = {} # dictionary of connections between nodes # (key: unique tag of the NetworkItem, # value: NetworkItem instance) self.forceExecution = 0 # set to one when a network is forced to # execute. This will force the execution of # all root nodes in subnetworks as well as # pretent all data flwoing into subnetworks is # new self.mouseActionEvents = [ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '' ] # table of functions to call for specific mouse events on canvas self.mouseButtonFlag = 0 self.mouseAction = {} for event in self.mouseActionEvents: self.mouseAction[event] = None self.mouseAction[''] = self.startDrawingSelectionBox self.mouseAction[''] = self.moveCanvasOrSelectedNodes #self.moveSelectedNodesStart self.mouseAction[''] = self.postCanvasMenu self.mouseAction[''] = self.moveCanvasStart self.mouseAction[''] = self.scaleNetworkStart # global lock for running a node in this network # used to make sure only 1 node runs at a time self.RunNodeLock = threading.RLock() # global lock for running nodes in this network # used to avoid multiple runs to get started simultaneously self.RunLock = threading.RLock() self.RunLockCond = threading.Condition(self.RunLock) # lock for modifying execution abortion variable self.execStatusLock = threading.RLock() self.execStatusCond = threading.Condition(self.execStatusLock) self.execStatus = 'waiting' # waiting: ready to run new set of nodes # pending: set by network.run metho # when a new thread is created # running: nodes are currently running # stop: execution will stop ASAP # pause: execution will pause ASAP # frozen: frozen self.runAgain = False # set to False in runNodes, set to True # if runNodes is called during a run self.runOnNewData = reference_value(True) # set to false to avoid execution of the # node and its children node # upon connection or new data self.lastExecutedNodes = [] # this list is filled by runNodes() each # time this method runs a 3-tuple # (node, starttime, endtime) is added for # each node BEFORE it executes # lock used by iterate node self.iterateLock = threading.RLock() self.iterateCond = threading.Condition(self.iterateLock) self.postedMenu = None self.lastPosY = 10 # used in self.addNodes to auto place nodes self.userPanels = {} # dictionary of panel created by the user # to place widgets for an application # the key is the panel name # the value is the Tkinter Frame self.nodeToRunList = [] def onStoppingExecution(self): self.canvas.update_idletasks() for node in self.nodes: node.onStoppingExecution() def getTypeManager(self): ed = self.getEditor() if ed is not None: return ed.typeManager else: return self.typeManager def getNodeByName(self, name): """nodeList <- getNodeByName(self, name) Return all the nodes who's name match the name regular expression """ nodes = [] import re reg = re.compile(name) nodes = filter( lambda x,reg=reg: reg.match(x.name), self.nodes ) return nodes def nodeIdToNumber(self, id): """return the current index of this node in network.nodes if the node's _id is given.""" for i in range(len(self.nodes)): if self.nodes[i]._id == id: break return i def getNodesByIds(self, ids): """returns a list of nodes based on a list of ids""" found = [] for node in self.nodes: if node._id in ids: found.append(node) return found def freeze(self): self.runOnNewData.value = False def unfreeze(self): self.runOnNewData.value = True def createCallbackManager(self, event, doc=None): assert not self.eventHandler.has_key(event) self.eventHandler[event] = CallbackManager() self.eventHandler[event].doc = doc def addCallbackManager(self, event, func): assert self.eventHandler.has_key(event) assert callable(func) self.eventHandler[event].AddCallback(func) def describeCallbackManager(self, event): assert self.eventHandler.has_key(event) return self.eventHandler[event].doc def postCanvasMenu(self, event): ed = self.getEditor() if self.postedMenu: self.postedMenu.unpost() self.postedMenu = None elif ed.menuButtons['Networks'].menu: ed.menuButtons['Networks'].menu.post(event.x_root, event.y_root) self.postedMenu = ed.menuButtons['Networks'].menu def delete(self, saveValues=0): # this method destroys the network # this method is called from editor.deleteNetwork(), use that method # to delete a network, which is doing more stuff that is necessary! lRunOnNewDataValue = self.runOnNewData.value self.runOnNewData.value = False nodes = self.nodes[:] for n in nodes: self.deleteNodes([n]) n.deleteIcon() for c in self.connections: c.deleteIcon() if self.canvas: self.destroyIcons(saveValues=saveValues) for p in self.userPanels.values(): p.master.destroy() self.runOnNewData.value = lRunOnNewDataValue def destroyIcons(self, saveValues=0): # destroy the icons of node, ports, etc, but not the objects itself if not self.canvas: return for n in self.nodes: if n.objEditor: n.objEditor.Dismiss() if n.isExpanded(): n.toggleNodeExpand_cb() for p in n.inputPorts: # get rid of all widgets, but save parameters. These will be # used in node.createWidgets to reconstruct widgets if saveValues and p.widget: wcfg = p.widget.getDescr() wcfg['initialValue'] = p.widget.get() p.node.widgetDescr[p.name] = wcfg p.widget = None p.destroyIcon() for p in n.outputPorts: p.destroyIcon() for c in p.connections: c.destroyIcon() for p in n.specialInputPorts: for c in p.connections: c.destroyIcon() p.destroyIcon() for p in n.specialOutputPorts: for c in p.connections: c.destroyIcon() p.destroyIcon() n.menu.destroy() n.menu = None n.nodeWidgetsID = [] # ids of widgets in node n.iconTag = None n.iconMaster = None n.id = None # setting id to None will rebuild this icon # in self.buildIcons self.destroyCanvas() def destroyCanvas(self): # delete the Pmw.Notebook page and everything in it ed = self.getEditor() ed.networkArea.delete(self.origName) self.canvas.destroy() self.canvasFrame=None self.canvas = None self.firstItemId = None self.nodesById = {} self.connById = {} self.inPortsId = {} self.outPortsId = {} self.naviMap.unregisterCallBacks() def buildIcons(self): ed = self.getEditor() if self.canvas is None: self.createCanvas() lCanvasWasMissing = True else: lCanvasWasMissing = False for n in self.nodes: # Nodes belonging to a MacroNetwork are instanciated here if n.id is None: n.buildIcons(self.canvas, n.posx, n.posy) self.nodesById[n.id] = n # FIXME this is a hack ... when macro network is re-displayed # the widget appear bu the node is not scaled # unless inNode widget is visible by default hide it #widgetsInNode = n.getWidgetsForMaster('Node') #for w in widgetsInNode.values(): # if not w.visibleInNodeByDefault: # n.hideInNodeWidgets( w, rescale=0 ) for c in self.connections: if not c.id: c.buildIcons(self.canvas) self.connById[c.id] = c if c.id2 is not None: self.connById[c.id2] = c if lCanvasWasMissing is True: #FIXME this line was added because else nodes with widgets # in macros are 'flat' whenthe macro is expanded ed.setNetwork(self) self.bindCallbacks() def rename(self, name): ed = self.getEditor() if ed is not None: if self.name in ed.networks.keys(): del ed.networks[self.name] ed.networks[name] = self if self.canvas: ed.renameNetworkTab(self, name) self.name = name self._setModified(True) def enter(self, event=None): if widgetsOnBackWindowsCanGrabFocus is False: lActiveWindow = self.canvas.focus_get() if lActiveWindow is not None \ and ( lActiveWindow.winfo_toplevel() != self.canvas.winfo_toplevel() ): return self.canvas.focus_set() def configure(self, event=None): self.visibleWidth = event.width self.visibleHeight = event.height def bindCallbacks(self): # bind mouse button callbacks # can only be done after the network has be added to an editor # and the draw canas has been created ed = self.getEditor() if self.canvas is None: print 'too early to bind' return self.canvas.bind("", self.mouse1Down) self.canvas.bind("", self.mouse1Down) self.canvas.bind("", self.mouse1Down) self.canvas.bind("", self.mouse2Down) self.canvas.bind("", self.mouse2Down) self.canvas.bind("", self.mouse2Down) self.canvas.bind("", self.mouse1Up) self.canvas.bind("", self.mouse1Up) self.canvas.bind("", self.mouse1Up) # set the focus so that we get keyboard events, and add callbacks self.master = self.canvas.master # hack ot get this to work self.canvas.bind('', self.modifierDown) self.canvas.bind("", self.modifierUp) # bind accelerator keys such as Ctrl-a for selectAll, etc self.canvas.bind('', ed.selectAll_cb) self.canvas.bind('', ed.copyNetwork_cb) #self.canvas.bind('', ed.toggleFreezeNetwork_cb) self.canvas.bind('', ed.toggleRunOnNewData_cb) self.canvas.bind('', ed.createMacro_cb) self.canvas.bind('', ed.newNet_cb) self.canvas.bind('', ed.loadNetwork_cb) self.canvas.bind('', ed.interactiveExit_cb) self.canvas.bind('', ed.print_cb) self.canvas.bind('', ed.softrunCurrentNet_cb) self.canvas.bind('', ed.runCurrentNet_cb) self.canvas.bind('', ed.saveNetwork_cb) self.canvas.bind('', ed.pasteNetwork_cb) self.canvas.bind('', ed.closeNetwork_cb) self.canvas.bind('', ed.cutNetwork_cb) self.canvas.bind('', ed.undo) self.canvas.bind('', self.toggleShowMap) self.showNaviMap = True # bind arrow keys to move nodes with arrows func = CallBackFunction(self.arrowKeys_cb, 0, -1) self.canvas.bind('', func) func = CallBackFunction(self.arrowKeys_cb, 0, 1) self.canvas.bind('', func) func = CallBackFunction(self.arrowKeys_cb, -1, 0) self.canvas.bind('', func) func = CallBackFunction(self.arrowKeys_cb, 1, 0) self.canvas.bind('', func) # same, with SHIFT: 10x bigger move func = CallBackFunction(self.arrowKeys_cb, 0, -10) self.canvas.bind('', func) func = CallBackFunction(self.arrowKeys_cb, 0, 10) self.canvas.bind('', func) func = CallBackFunction(self.arrowKeys_cb, -10, 0) self.canvas.bind('', func) func = CallBackFunction(self.arrowKeys_cb, 10, 0) self.canvas.bind('', func) def toggleShowMap(self, event=None): if self.showNaviMap: # hide the map self.showNaviMap = False self.naviMap.mapCanvas.move('navimap', -10000, -10000) else: # show the map self.showNaviMap = True self.naviMap.mapCanvas.move('navimap', 10000, 10000) def createCanvas(self): """create the Canvas and Title widgets""" ed = self.getEditor() master = self.canvasFrame = ed.networkArea.add(self.name) self.origName = self.name self.scrollregion=[0 , 0, ed.totalWidth, ed.totalHeight] self.scrolledCanvas = Pmw.ScrolledCanvas( master, borderframe=1, #labelpos='n', label_text='main', usehullsize=0, hull_width=ed.visibleWidth, hull_height=ed.visibleHeight, vscrollmode='static', hscrollmode='static') self.canvas = self.scrolledCanvas.component('canvas') self.canvas.configure(background='grey75', width=ed.visibleWidth, height=ed.visibleHeight, scrollregion=tuple(self.scrollregion) ) self.firstItemId = self.canvas.create_line(0,0,0,0) from NetworkEditor.naviMap import NavigationMap self.naviMap = NavigationMap(self, self.scrolledCanvas, self.canvas, 0.05) ## import NetworkEditor, os ## file = os.path.join(NetworkEditor.__path__[0], "back1.gif") ## self.bg = Tkinter.PhotoImage(file=file) ## self.bgId = self.canvas.create_image(0, 0, anchor=Tkinter.NW, ## image=self.bg) # bind standard callbacks self.canvas.bind('', self.configure) self.canvas.bind("", ed.undo) self.canvas.bind("", ed.undo) # pack 'em up self.scrolledCanvas.pack(side=Tkinter.LEFT, expand=1,fill=Tkinter.BOTH) self.canvas.bind("", self.enter) # # MISC # def setSplineConnections(self, yesno): if yesno is True: self.defaultConnectionOptions['smooth'] = 1 for c in self.connections: apply( c.iconMaster.itemconfigure, (c.iconTag,),{'smooth':1}) else: self.defaultConnectionOptions['smooth'] = 0 for c in self.connections: apply( c.iconMaster.itemconfigure, (c.iconTag,),{'smooth':0}) # # save/restore source code generation # def saveToFile(self, filename, copyright=True): lines = [] #if len(self.currentNetwork.userPanels) > 0: #lines += ['#!%s\n'%sys.executable] #lines += ['#!/bin/ksh '+self.resourceFolder+'/pythonsh\n'] lines += ['#!/bin/ksh ~/.mgltools/pythonsh\n'] lines += self.getNetworkCreationSourceCode(copyright=copyright) f = open(filename, 'w') map( f.write, lines ) f.close() def getNetworkCreationSourceCode(self, networkName='masterNet', selectedOnly=0, indent="", withRun=True, ignoreOriginal=False, copyright=False, importOnly=False): """returns code to re-create a network containing nodes and connections selectedOnly: True/False. If set to true, we handle selected nodes only indent: a string with whitespaces for code indentation ignoreOriginal: True/False. Default:False. If set to True, we ignore the _original attribute of nodes (for example, nodes in a macro network that came from a node library where nodes are marked original copyright: if True copyright and network execution code is generated. This is not needed when we copy/paster for instance. )""" ed = self.getEditor() ed._tmpListOfSavedNodes = {} # clear this list # if selectedOnly is TRUE the sub-network of selected nodes and # connections between these nodes are saved lines = [] if copyright is True: import datetime lNow = datetime.datetime.now().strftime("%A %d %B %Y %H:%M:%S") lCopyright = \ """######################################################################## # # Vision Network - Python source code - file generated by vision # %s # # The Scripps Research Institute (TSRI) # Molecular Graphics Lab # La Jolla, CA 92037, USA # # Copyright: Daniel Stoffler, Michel Sanner and TSRI # # revision: Guillaume Vareille # ######################################################################### # # $%s$ # # $%s$ # """%(lNow, "Header:", "Id:") lines.append(lCopyright) lines.append(indent + """ if __name__=='__main__': from sys import argv if '--help' in argv or '-h' in argv or '-w' in argv: # run without Vision withoutVision = True from Vision.VPE import NoGuiExec ed = NoGuiExec() from NetworkEditor.net import Network import os masterNet = Network("process-"+str(os.getpid())) ed.addNetwork(masterNet) else: # run as a stand alone application while vision is hidden withoutVision = False from Vision import launchVisionToRunNetworkAsApplication, mainLoopVisionToRunNetworkAsApplication if '-noSplash' in argv: splash = False else: splash = True masterNet = launchVisionToRunNetworkAsApplication(splash=splash) import os masterNet.filename = os.path.abspath(__file__) """ ) lines.append(indent+'from traceback import print_exc\n') # lines.append(indent+"#### Network: "+self.name+" ####\n") # lines.append(indent+"#### File written by Vision ####\n\n") ## save code to create user panels for name, value in self.userPanels.items(): #print "name, value", name, value, dir(value.frame) lines.append(indent+ "masterNet.createUserPanel('%s' ,width=%d, height=%d)\n" %(name, value.frame.winfo_width(), value.frame.winfo_height() ) ) ## add stuff that users might want to add at very beginning of network callbacks = self.eventHandler['saveNetworkBegin'] txt = callbacks.CallCallbacks(indent=indent) for t in txt: lines.extend(t) ## get library import cache (called recursively) ## then write libray import code cache = {'files':[]} cache = self.buildLibraryImportCache(cache, self, selectedOnly) li = self.getLibraryImportCode(cache, indent, editor="%s.getEditor()"%networkName, importOnly=importOnly, loadHost=True) lines.extend(li) ## add node creation code li = self.getNodesCreationSourceCode(networkName, selectedOnly, indent, ignoreOriginal) lines.extend(li) ## add code to run individualy each node before the connections lines.append(indent+'#%s.run()\n'%networkName) ## add code to freeze network execution to avoid executing upon connection lines.append(indent+'%s.freeze()\n'%networkName) ## add connection creation code if len(self.connections): lines.append( '\n'+indent+"## saving connections for network "+\ "%s ##\n"%self.name) for i,conn in enumerate(self.connections): lines.extend(conn.getSourceCode( networkName, selectedOnly, indent, ignoreOriginal, connName='conn%d'%i)) ## add code to set correctly runOnNewData lines.append(indent+'%s.runOnNewData.value = %s\n'%(networkName,self.runOnNewData.value)) ## allow to add code after connections were formed (connections ## might generate new ports, for example -> see MacroOutputNode). for node in self.nodes: if selectedOnly: if not node.selected: continue lines.extend(node.getAfterConnectionsSourceCode( networkName, indent, ignoreOriginal) ) ## add stuff that users might want to add at very end of network callbacks = self.eventHandler['saveNetworkEnd'] txt = callbacks.CallCallbacks(indent=indent) for t in txt: lines.extend(t) for lNodeName, lNode in ed._tmpListOfSavedNodes.items(): #print "_tmpListOfSavedNodes", lNodeName, lNode if hasattr(lNode, 'vi'): lines.extend('\n\ndef loadSavedStates_%s(self=%s, event=None):\n'%(lNodeName, lNodeName) ) txt = lNode.vi.getObjectsStateDefinitionCode( viewerName='self.vi', indent=' ', withMode=False, includeBinding=False) lines.extend(txt) txt = lNode.vi.getViewerStateDefinitionCode( viewerName='self.vi', indent=' ', withMode=False, rootxy=False) lines.extend(txt) if lNode.__module__ == 'DejaVu.VisionInterface.DejaVuNodes': lines.extend('%s.restoreStates_cb = %s.restoreStatesFirstRun = loadSavedStates_%s\n'%(lNodeName, lNodeName, lNodeName) ) else: # 'Pmv.VisionInterface.PmvNodes': lines.extend('%s.restoreStates_cb = %s.restoreStatesFirstRun = loadSavedStates_%s\n'%(lNodeName, lNodeName, lNodeName) ) #lines.extend('%s.restoreStates_cb = loadSavedStates_%s\n'%(lNodeName, lNodeName) ) lines.extend('%s.menu.add_separator()\n'%lNodeName ) lines.extend("%s.menu.add_command(label='Restore states', command=%s.restoreStates_cb)\n"%(lNodeName, lNodeName) ) # finally, clear the list ed._tmpListOfSavedNodes = {} if copyright is True: lines += """ if __name__=='__main__': from sys import argv lNodePortValues = [] if (len(argv) > 1) and argv[1].startswith('-'): lArgIndex = 2 else: lArgIndex = 1 while lArgIndex < len(argv) and argv[lArgIndex][-3:]!='.py': lNodePortValues.append(argv[lArgIndex]) lArgIndex += 1 masterNet.setNodePortValues(lNodePortValues) if '--help' in argv or '-h' in argv: # show help masterNet.helpForNetworkAsApplication() elif '-w' in argv: # run without Vision and exit # create communicator from NetworkEditor.net import Communicator masterNet.communicator = Communicator(masterNet) print 'Communicator listening on port:', masterNet.communicator.port import socket f = open(argv[0]+'.sock', 'w') f.write("%s %i"%(socket.gethostbyname(socket.gethostname()), masterNet.communicator.port)) f.close() masterNet.run() else: # stand alone application while vision is hidden if '-e' in argv: # run and exit masterNet.run() elif '-r' in argv or len(masterNet.userPanels) == 0: # no user panel => run masterNet.run() mainLoopVisionToRunNetworkAsApplication(masterNet.editor) else: # user panel mainLoopVisionToRunNetworkAsApplication(masterNet.editor) """ for n in self.nodes: n.nameInSavedFile = None return lines ## ## functions used to run networks as programs ## def helpForNetworkAsApplication(self): help_msg = """Run the network without displaying the Vision network editor. (If there is a User panel the network will just load and not run) usage: %s