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