1 """
2 The GUI model editor.
3
4 Tools: for the editor menu bar
5 Objects: that can be manipulated in the canvas
6 Window: window and canvas
7
8 Originally written by Alan Lindsay.
9
10 $Id: editor.py 11310 2010-07-27 16:56:14Z ceball $
11 """
12 __version__='$Revision: 8989 $'
13
14 from inspect import getdoc
15 import math
16
17 from Tkinter import Button, Label, Frame, TOP, LEFT, RIGHT, BOTTOM, E, LAST, FIRST, OptionMenu, StringVar,Canvas,X,GROOVE,RAISED,Checkbutton,Menu,Scrollbar, YES,Y,END,BOTH
18 from tkFileDialog import asksaveasfilename
19
20
21 import param
22 from param import tk,parameterized,normalize_path
23 from param.external import Combobox
24
25 import topo
26 from topo.command.analysis import update_activity
27 from topo.misc.util import shortclassname
28 from topo.base.simulation import EventProcessor
29
30
31
32
33 from topo.base.sheet import Sheet
34 from topo.base.projection import Projection
35 from topo.base.cf import CFProjection
36
37
38
39
40
41
42
43
44 canvas_width = 1200
45 scaling_factor = topo.sim.item_scale
46 enlarging_factor = 1.25
47
48 canvas_region = (0, 0, canvas_width, canvas_width)
49 """Size of the canvas, as a bounding box (xl yl xh yh)."""
50
51
53
54 """
55 EditorCanvas extends the Tk Canvas class.
56 There are 3 modes that determine the effect of mouse events in the Canvas
57 A Canvas can accept new objects, move objects and make connections
58 between them. The intended use of this class is as the main
59 canvas in a Topographica model-editing GUI.
60 """
61
62 - def __init__(self, root = None, width = 600, height = 600):
63 Canvas.__init__(self, root, width = width, height = height,bg='white')
64
65 self.panel = Frame(root)
66 self.panel.pack(side = TOP, fill = X)
67
68 Button(self.panel,text="Refresh", command=self.refresh).pack(side=LEFT)
69 Button(self.panel,text="Reduce", command=self.reduce_scale).pack(side=LEFT)
70 Button(self.panel,text="Enlarge", command=self.enlarge_scale).pack(side=LEFT)
71 self.auto_refresh = False
72 self.console = topo.guimain
73 self.auto_refresh_checkbutton = Checkbutton(self.panel,text="Auto-refresh",
74 command=self.toggle_auto_refresh)
75 self.auto_refresh_checkbutton.pack(side=LEFT)
76
77 self.normalize_checkbutton = Checkbutton(self.panel, text="Normalize",
78 command=self.toggle_normalize)
79
80 self.normalize_checkbutton.pack(side=LEFT)
81 if EditorSheet.normalize == True:
82 self.normalize_checkbutton.select()
83
84
85 self.scaling_factor = topo.sim.item_scale
86 self.current_object = None
87 self.current_connection = None
88 self.focus = None
89
90 self.object_list = []
91
92 self.display_mode = 'video'
93 self.mode = "ARROW"
94 self.MAX_VIEWS = 5
95
96 self.simulation = topo.sim
97
98
99 self.option_add("*Menu.tearOff", "0")
100 self.item_menu = Menu(self)
101 self.view = Menu(self.item_menu)
102
103 self.item_menu.insert_command(END, label = 'Properties',
104 command = lambda: self.show_properties(self.focus))
105 self.item_menu.add_cascade(label = 'Change View', menu = self.view, underline = 0)
106 self.item_menu.insert_command(END, label = 'Move Forward',
107 command = lambda: self.move_forward(self.focus))
108 self.item_menu.insert_command(END, label = 'Move to Front',
109 command = lambda: self.move_to_front(self.focus))
110 self.item_menu.insert_command(END, label = 'Move to Back',
111 command = lambda: self.move_to_back(self.focus))
112 self.item_menu.insert_command(END, label = 'Delete',
113 command = lambda: self.delete_focus(self.focus))
114
115 self.object_indices = [2,3,4]
116
117 self.canvas_menu = Menu(self)
118 self.sheet_options = Menu(self.canvas_menu)
119 mode_options = Menu(self.canvas_menu)
120 self.canvas_menu.add_command(label = 'Export as PostScript image', command = self.save_snapshot)
121 self.canvas_menu.add_cascade(label = 'Select Mode', menu = mode_options)
122 mode_options.add_command(label = 'Video', command = lambda: self.set_display_mode('video'))
123 mode_options.add_command(label = 'Normal', command = lambda: self.set_display_mode('normal'))
124 mode_options.add_command(label = 'Printing', command = lambda:
125 self.set_display_mode('printing'))
126 self.canvas_menu.add_cascade(label = 'Sheet options', menu = self.sheet_options, underline = 0)
127 self.sheet_options.add_command(label = 'Toggle Density Grid', command =
128 self.toggle_object_density)
129 self.sheet_options.add_command(label = 'Toggle Activity', command = self.toggle_object_activity)
130
131
132 self.bind('<KeyPress>', self.key_press)
133
134 self.bind('<Button-1>', self.left_click)
135 self.bind('<B1-Motion>', self.left_click_drag)
136 self.bind('<Double-1>', self.left_double_click)
137 self.bind('<ButtonRelease-1>', self.left_release)
138
139 self.bind('<<right-click>>', self.right_click)
140
141 self.item_menu.bind('<<right-click-release>>', self.right_release)
142
143
144 self.config(scrollregion = canvas_region)
145 vertical_scrollbar = Scrollbar(root)
146 horizontal_scrollbar = Scrollbar(root, orient = 'horizontal')
147 vertical_scrollbar.config(command = self.yview)
148 horizontal_scrollbar.config(command = self.xview)
149 self.config(yscrollcommand=vertical_scrollbar.set)
150 self.config(xscrollcommand=horizontal_scrollbar.set)
151 vertical_scrollbar.pack(side = RIGHT, fill = Y)
152 horizontal_scrollbar.pack(side = BOTTOM, fill = X)
153
155 "What happens when a key is pressed."
156 self.change_mode(event.char)
157
158
159
160
162 "What is to happen if the left button is pressed."
163
164 x,y = self.canvasx(event.x), self.canvasy(event.y)
165 {"ARROW" : self.init_move,
166 "MAKE" : self.none,
167 "CONNECTION" : self.init_connection
168 }[self.mode](x,y)
169
171 "What is to happen if the mouse is dragged while the left button is pressed."
172
173 x,y = self.canvasx(event.x), self.canvasy(event.y)
174 {"ARROW" : self.update_move,
175 "MAKE" : self.none,
176 "CONNECTION" : self.update_connection
177 }[self.mode](x,y)
178
180 "What is to happen when the left mouse button is released."
181
182 x,y = self.canvasx(event.x), self.canvasy(event.y)
183 {"ARROW" : self.end_move,
184 "MAKE" : self.create_object,
185 "CONNECTION" : self.end_connection
186 }[self.mode](x,y)
187
189 """
190 What is to happen if the left button is double clicked.
191 The same for all modes - show the properties for the clicked item.
192 Gets object or connection at this point and gives it the focus.
193 """
194 focus = self.get_xy(event.x, event.y)
195 if (focus != None):
196 focus.set_focus(True)
197
198 self.show_properties(focus)
199
200
201
202
204 "What is to happen if the right button is pressed."
205 self.show_hang_list(event)
206
208 "What is to happen when the right mouse button is released (bound to the menu)."
209 if (self.focus != None) :
210 self.focus.set_focus(False)
211
212
213
214
216 "Changes the mode of the canvas, i.e., what mouse events will do."
217
218 if not char in ('c', 'm', 'a') : return
219
220 {"ARROW" : self.arrow_tool.set_focus,
221 "MAKE" : self.object_tool.set_focus,
222 "CONNECTION" : self.connection_tool.set_focus
223 }[self.mode](False)
224
225
226 if (char == 'c'):
227 mode = "CONNECTION"
228 bar = self.connection_tool
229 elif (char == 'm'):
230 mode = "MAKE"
231 bar = self.object_tool
232 elif (char == 'a'):
233 mode = "ARROW"
234 bar = self.arrow_tool
235
236 bar.set_focus(True)
237 self.mode = mode
238
239
240
241
243 for obj in self.object_list:
244 obj.set_focus(True)
245 obj.set_focus(False)
246 for obj in self.object_list:
247 connection_list = obj.from_connections[:]
248 connection_list.reverse()
249 for con in connection_list:
250 con.move()
251
255
259
266
270
271
272
273
274
275
276
277
279 "Determine if click was on an object."
280
281 self.current_object = self.get_object_xy(x, y)
282 if (self.current_object != None) :
283
284 self.current_object.set_focus(True)
285
287 "If dragging an object, refresh its position"
288
289 if (self.current_object != None):
290 self.current_object.move(x, y)
291
293 "If dropping an object, remove focus and refresh."
294
295 if (self.current_object != None):
296 self.current_object.set_focus(False)
297 self.current_object.move(x, y)
298
299 self.redraw_objects()
300 self.current_object = None
301
302
303
304
305
307 "Determine if click was on an object, and retain if so."
308
309 current_object = self.get_object_xy(x, y)
310 if (current_object == None) :
311 self.change_mode('a')
312 else :
313 self.current_connection = self.connection_tool.new_cover(current_object)
314 self.current_connection.set_focus(True)
315
317 "Update connection's position."
318 self.current_connection.update_position((x, y))
319
321 "Determine if the connection has been dropped on an object."
322
323 obj = self.get_object_xy(x, y)
324 if (obj != None) :
325 if (self.current_connection != None):
326 connected = self.connection_tool.create_connection(self.current_connection, obj)
327 if connected:
328 self.current_connection.set_focus(False)
329 else :
330 connected=False
331 if (self.current_connection != None):
332 self.current_connection.remove()
333 if connected:
334 self.redraw_objects()
335
336 self.current_connection = None
337
339 "Return connection at given x, y (None if no connection)."
340
341 for obj in self.object_list:
342 connection_list = obj.from_connections[:]
343 connection_list.reverse()
344 for con in connection_list:
345 if (con.in_bounds(x, y)):
346 return con
347 return None
348
349
350
352 "Create a new object."
353 self.add_object(self.object_tool.create_node(x, y))
354
356 "Add a new object to the Canvas."
357
358 self.object_list = [obj] + self.object_list
359
361 "Add a new object to the Canvas at back of the list."
362
363 self.object_list = self.object_list + [obj]
364
366 "Remove an object from the canvas."
367
368 for i in range(len(self.object_list)) :
369 if (obj == self.object_list[i]) : break
370 else : return
371 del self.object_list[i]
372 return i
373
380
387
389 "Return object at given x, y (or None if no object)."
390
391
392
393 for obj in self.object_list:
394 if (obj.in_bounds(x, y)):
395 break
396 else : return None
397 return obj
398
399
406
415
416
417
418
419
420
422 """
423 Redraw all the objects in the canvas.
424
425 If non-None, the index specifies that only the objects below
426 that index need drawing.
427 """
428
429 if (index == None or index < 0) : index = len(self.object_list)
430 for i in range(index ,0, -1):
431 self.object_list[i-1].draw()
432
437
439 for i in range(len(self.object_list)) :
440 if (obj == self.object_list[i]) : break
441 else : return
442
443 a = self.object_list[(i-1) : (i+1)]
444 a.reverse()
445 self.object_list[(i-1):(i+1)] = a
446 self.redraw_objects(i+1)
447
452
453
454
455
456
457
458
460
461
462 self.change_mode('a')
463 x, y = self.canvasx(event.x), self.canvasy(event.y)
464
465 focus = self.get_connection_xy(x, y)
466 for i in range(self.MAX_VIEWS) :
467 self.view.delete(END)
468 if (focus == None):
469
470 focus = self.get_object_xy(x, y)
471
472 for i in self.object_indices:
473 self.item_menu.entryconfig(i, foreground = 'Black', activeforeground = 'Black')
474 else:
475
476 for i in self.object_indices:
477 self.item_menu.entryconfig(i,foreground = 'Gray', activeforeground = 'Gray')
478 if (focus != None):
479 for (label, function) in focus.viewing_choices:
480 self.view.add_command(label = label, command = function)
481
482 focus.set_focus(True)
483 self.focus = focus
484
485 self.item_menu.tk_popup(event.x_root, event.y_root)
486 else:
487 self.canvas_menu.tk_popup(event.x_root, event.y_root)
488
489
490
492 POSTSCRIPT_FILETYPES = [('Encapsulated PostScript images','*.eps'),
493 ('PostScript images','*.ps'),('All files','*')]
494 snapshot_name = asksaveasfilename(filetypes=POSTSCRIPT_FILETYPES,
495 initialdir=normalize_path(),
496 initialfile=topo.sim.basename()+".ps")
497
498 if snapshot_name:
499 self.postscript(file=snapshot_name)
500
502 self.display_mode = mode
503 for obj in self.object_list:
504 obj.set_mode(mode)
505
516
517
518 - def none(self, x, y) : pass
519
529
530
531
532
533
534
536 """
537 This class constructs the main editor window. It uses a instance
538 of GUICanvas as the main editing canvas and inserts the
539 three-option toolbar in a Frame along the left side of the window.
540 """
541
543 parameterized.Parameterized.__init__(self,**params)
544
545
546 root = tk.AppWindow(master)
547 root.title("Model Editor")
548
549 canvas_frame = Frame(root,bg = 'white')
550 canvas_frame.pack(side='right',fill = BOTH, expand = YES)
551
552 toolbar_frame = Frame(root, bg = 'light grey', bd = 2)
553 toolbar_frame.pack(side=LEFT,fill=Y)
554
555 self.canvas = EditorCanvas(canvas_frame)
556 self.canvas.pack(fill = BOTH, expand = YES)
557
558
559
560
561 parameters_tool = ParametersTool(toolbar_frame)
562 arrow_tool=ArrowTool(self.canvas,toolbar_frame,parameters_tool)
563 object_tool=NodeTool(self.canvas,toolbar_frame,parameters_tool)
564 connection_tool=ConnectionTool(self.canvas,toolbar_frame,parameters_tool)
565
566 arrow_tool.pack(side='top')
567 object_tool.pack(side='top')
568 connection_tool.pack(side='top')
569 parameters_tool.pack(side='top')
570
571
572
573 self.canvas.set_tool_bars(arrow_tool, connection_tool, object_tool)
574
575
576 self.canvas.focus_set()
577
578
579
580 self.xstart = 100
581 self.ystart = 100
582 self.next_x = self.xstart+100
583 self.next_y = self.ystart
584 self.xstep = 150
585 self.ystep = 150
586
587 self.import_model()
588
590
591 sim = self.canvas.simulation
592 node_dictionary = sim.objects(EventProcessor)
593 node_list = node_dictionary.values()
594
595
596 for node in node_list:
597
598 if (hasattr(node,'layout_location') and node.layout_location!=(-1,-1)):
599 x, y = node.layout_location
600
601
602 else:
603 x, y = self.next_x , self.next_y
604 self.next_y += self.ystep
605 if self.next_y > canvas_region[3]:
606 self.next_y = self.ystart
607 self.next_x += self.xstep
608 if self.next_x > canvas_region[2]:
609 self.next_x = self.xstart + 15
610 self.next_y = self.ystart + 15
611
612
613 if isinstance(node,Sheet):
614 editor_node = EditorSheet(self.canvas, node, (x, y), node.name)
615 else:
616 editor_node = EditorEP(self.canvas, node, (x, y), node.name)
617
618 node.layout_location=(x,y)
619 self.canvas.add_object(editor_node)
620
621
622
623 for editor_node in self.canvas.object_list:
624 for con in editor_node.simobj.out_connections:
625
626 if isinstance(con,CFProjection):
627 editor_connection = EditorProjection("", self.canvas, editor_node)
628 else:
629 editor_connection = EditorEPConnection("", self.canvas, editor_node)
630
631
632 for dest in self.canvas.object_list:
633 if (dest.simobj == con.dest):
634
635 editor_connection.connect(dest, con)
636 break
637 else:
638 self.warning("The model editor cannot draw connection", con.name,
639 "because", con.dest.name, "is not drawn in the editor.")
640
641
642
643 self.canvas.redraw_objects()
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
715
716
717
718
719
723
724
829
830
831
832
833
942
943
944
978
979
980
981
982
983
984
985
987 """
988 Anything that can be added and manipulated in an EditorCanvas. Every EditorCanvas
989 has a corresponding Topo object associated with it. An instance of this class can
990 have the focus.
991 """
992 FROM = 0
993 TO = 1
994
995 - def __init__(self, name, canvas,**params):
996 self.canvas = canvas
997 self.name = name
998 self.focus = False
999 self.viewing_choices = []
1000
1002 "Draw the object at the current x, y position."
1003 pass
1004
1006 """Documentation string for this object."""
1007
1008
1009 return self.name + " is of type " + \
1010 shortclassname(self.simobj) + \
1011 ":\n\n" + str(getdoc(self.simobj))
1012
1033
1036
1040
1042 self.focus = focus
1043
1045 "Update position of object and redraw."
1046 pass
1047
1049 "Remove this object from the canvas and from the Topographica simulation."
1050 pass
1051
1053 "Return true if x,y lies within this gui object's boundary."
1054 pass
1055
1056
1057
1059 """
1060 An EditorNode is used to cover any topographica node, presently this can only be a sheet.
1061 It is a sub class of EditorObject and supplies the methods required by any node to be used
1062 in a EditorCanvas. Extending classes will supply a draw method and other type specific
1063 attributes.
1064 """
1065
1066 - def __init__(self, canvas, simobj, pos, name):
1067 EditorObject.__init__(self, name, canvas)
1068 self.from_connections = []
1069 self.to_connections = []
1070 self.x = pos[0]
1071 self.y = pos[1]
1072 self.mode = canvas.display_mode
1073 self.simobj = simobj
1074
1075
1076
1078 if (from_to == self.FROM):
1079 if (con.from_node == con.to_node):
1080 self.from_connections = [con] + self.from_connections
1081 else:
1082 self.from_connections = self.from_connections + [con]
1083 else:
1084 if (con.from_node == con.to_node):
1085 self.to_connections = [con] + self.to_connections
1086 else:
1087 self.to_connections = self.to_connections + [con]
1088
1090 if (from_to):
1091 l = len(self.to_connections)
1092 for i in range(l):
1093 if (con == self.to_connections[i]) : break
1094 else : return
1095 del self.to_connections[i]
1096 else:
1097 l = len(self.from_connections)
1098 for i in range(l):
1099 if (con == self.from_connections[i]) : break
1100 else : return
1101 del self.from_connections[i]
1102
1103
1104
1105
1107 return (self.x, self.y)
1108
1110 EditorObject.show_properties(self)
1111 self.parameter_frame.set_PO(self.simobj)
1112 Label(self.parameter_window, text = '\n\nConnections').pack(side = TOP)
1113 connections = list(set(self.to_connections).union(set(self.from_connections)))
1114 connection_list = [con.name for con in connections]
1115
1116 self.connection_var = StringVar()
1117 self.connection_var.set(connection_list[0])
1118
1119 connection_menu = OptionMenu(self.parameter_window,self.connection_var,*connection_list)
1120
1121 self.connection_var.trace_variable('w',self.view_connection_parameters)
1122
1123 connection_menu.pack(side = TOP)
1124
1126 con_sel = self.connection_var.get()
1127 for con in self.to_connections + self.from_connections:
1128 if con.name == con_sel:
1129 break
1130 else :
1131 return
1132 con.show_properties()
1133
1134
1135
1136
1138 """
1139 Represents any topo EventProcessor as a small, fixed-size oval by default.
1140 """
1141
1142 - def __init__(self, canvas, simobj, pos, name):
1152
1153
1154
1155
1165
1170
1172 colour = {'video':('dark red','black'),
1173 'normal':('slate blue', 'lavender'),
1174 'printing':('grey','white')}
1175 self.colour = colour[self.mode]
1176
1178 if focus : label_colour = colour
1179 else : label_colour = 'black'
1180 h, w = 0.5*self.height, 0.5*self.width
1181
1182 x, y = self.x, self.y
1183 x1,y1 = (x-w,y-h)
1184 x2,y2 = (x+w,y+h)
1185
1186 self.id = [self.canvas.create_oval(x1,y1,x2,y2,fill=colour,outline="black")]
1187 dX = w + 5
1188 self.label = self.canvas.create_text(x - dX, y, anchor = E, fill = label_colour, text = self.name)
1189
1191
1192 max_val = pow(16, length) - 1
1193 fmt = '%%0%dx'%length
1194 return fmt % (val*max_val)
1195
1196 - def draw(self, x = 0, y = 0):
1197
1198 if not(x == y == 0):
1199 for id in self.id:
1200 self.canvas.move(id, x, y)
1201 self.canvas.move(self.label, x, y)
1202 for id in self.id:
1203 self.canvas.tag_raise(id)
1204 self.canvas.tag_raise(self.label)
1205
1206 for con in self.to_connections :
1207 if (con.from_node == con.to_node):
1208 con.move()
1209 for con in self.to_connections:
1210 if (not(con.from_node == con.to_node)):
1211 con.move()
1212 for con in self.from_connections:
1213 if (not(con.from_node == con.to_node)):
1214 con.move()
1215
1216
1217
1218
1220 l = len(self.from_connections)
1221 for index in range(l):
1222 self.from_connections[0].remove()
1223 l = len(self.to_connections)
1224 for index in range(l):
1225 self.to_connections[0].remove()
1226 for id in self.id:
1227 self.canvas.delete(id)
1228 self.canvas.delete(self.label)
1229 self.canvas.remove_object(self)
1230 del topo.sim[self.simobj.name]
1231
1232 - def move(self, x, y):
1233
1234 old = self.x, self.y
1235 self.x = x
1236 self.y = y
1237 self.simobj.layout_location = (self.x,self.y)
1238 self.draw(self.x - old[0], self.y - old[1])
1239
1240
1241
1242
1244 EditorNode.remove_connection(self, con, from_to)
1245 if from_to:
1246 node = con.from_node
1247 else:
1248 node = con.to_node
1249 index = con.draw_index
1250
1251 for connection in self.from_connections:
1252 if (node == connection.to_node and connection.draw_index >= index):
1253 connection.decrement_draw_index()
1254 if connection.to_node == connection.from_node : return
1255 for connection in self.to_connections :
1256 if (node == connection.from_node and connection.draw_index >= index):
1257 connection.decrement_draw_index()
1258
1260 count = 0
1261 for con in self.from_connections:
1262 if (con.to_node == node):
1263 count += 1
1264 for con in self.to_connections:
1265 if (con.from_node == node):
1266 count += 1
1267 if node == self : count /= 2
1268 return count
1269
1270
1271
1272
1274 x = self.x - pos_x; y = self.y - pos_y
1275
1276 return ((pos_x> self.x-self.width) and (pos_x<= self.x+self.width) and
1277 (pos_y> self.y-self.height) and (pos_y<= self.y+self.height))
1278
1279
1284
1285
1287 self.mode = mode
1288 self.set_colours()
1289
1290 for id in self.id:
1291 self.canvas.delete(id)
1292 self.canvas.delete(self.label)
1293 self.init_draw(self.colour[not self.focus], self.focus)
1294 for con in self.to_connections:
1295 con.set_mode(mode)
1296 for con in self.from_connections:
1297 con.draw()
1298
1299
1300
1301
1303 """
1304 Represents any topo sheet. It is a subclass of EditorEP and fills in the
1305 methods that are not defined. It is represented by a Parallelogram in its
1306 Canvas. The colours used for drawing can be set. Uses bounding box to
1307 determine if x, y coord is within its boundary.
1308 """
1309 normalize = param.Boolean(default=False)
1310 show_density = param.Boolean(default=False)
1311 view = param.ObjectSelector(default='activity',objects=['normal','activity'])
1312
1313 - def __init__(self, canvas, simobj, pos, name):
1327
1328
1329
1330
1340
1342 self.id = []
1343 if focus : label_colour = colour
1344 else : label_colour = 'black'
1345 factor = self.canvas.scaling_factor
1346 h, w = 0.5 * self.height * factor, 0.5 * self.width *factor
1347 if not(self.focus):
1348 if self.view == 'activity':
1349 colour = ''
1350 x, y = self.x - w + h, self.y - h
1351
1352
1353 update_activity()
1354 m = self.simobj.sheet_views['Activity'].view()[0]
1355 if self.normalize == True:
1356 m = self.normalize_plot(m)
1357 matrix_width, matrix_height = self.element_count
1358 dX, dY = (w * 2)/ matrix_width, (h * 2) / matrix_height
1359 for i in range(matrix_height):
1360 for j in range(matrix_width):
1361 a = i * dY
1362 x1, y1 = x - a + (j * dX), y + a
1363 x2, y2 = x1 - dY, y1 + dY
1364 x3, x4 = x2 + dX, x1 + dX
1365 point = m[i][j]
1366 if point < 0 : point = 0.0
1367 if point > 1 : point = 1.0
1368 col = '#' + (self.dec_to_hex_str(point, 3)) * 3
1369 self.id = self.id + [self.canvas.create_polygon
1370 (x1, y1, x2, y2, x3, y2, x4, y1, fill = col, outline = col)]
1371 x, y = self.x, self.y
1372 x1,y1 = (x - w - h, y + h)
1373 x2,y2 = (x - w + h, y - h)
1374 x3,y3 = x2 + (w * 2), y2
1375 x4,y4 = x1 + (w * 2), y1
1376 self.id = self.id + [self.canvas.create_polygon(x1, y1, x2, y2, x3, y3, x4, y4,
1377 fill = colour , outline = "black")]
1378 dX = w + 5
1379 self.label = self.canvas.create_text(x - dX, y, anchor = E, fill = label_colour, text = self.name)
1380
1381 if self.show_density:
1382 x, y = self.x - w + h, self.y - h
1383 matrix_width, matrix_height = self.element_count
1384 dX, dY = (w * 2)/ matrix_width, (h * 2) / matrix_height
1385 for i in range(matrix_height + 1):
1386 x1 = x - (i * dY)
1387 x2 = x1 + (w * 2)
1388 y1 = y + (i * dY)
1389 self.id = self.id + [self.canvas.create_line(x1, y1, x2, y1, fill = 'slate blue')]
1390 for j in range(matrix_width + 1):
1391 x1 = x + (j * dX)
1392 x2 = x1 - (h * 2)
1393 y1 = y
1394 y2 = y1 + (h * 2)
1395 self.id = self.id + [self.canvas.create_line(x1, y1, x2, y2, fill = 'slate blue')]
1396
1398 """
1399 Normalize an array s.
1400 In case of a constant array, ones is returned for value greater than zero,
1401 and zeros in case of value inferior or equal to zero.
1402 """
1403
1404 from numpy.oldnumeric import zeros, ones, Float, divide
1405 a_offset = a-min(a.ravel())
1406 max_a_offset = max(a_offset.ravel())
1407 if max_a_offset>0:
1408 a = divide(a_offset,float(max_a_offset))
1409 else:
1410 if min(a.ravel())<=0:
1411 a=zeros(a.shape,Float)
1412 else:
1413 a=ones(a.shape,Float)
1414 return a
1415
1416
1417
1419
1420
1421
1422 x = self.x - pos_x; y = self.y - pos_y
1423 w = 0.5 * self.width * self.canvas.scaling_factor
1424 h = 0.5 * self.height * self.canvas.scaling_factor
1425 A = (x - w - h, y + h)
1426 B = (x - w + h, y - h)
1427 C = B[0] + (2 * w), B[1]
1428 D = A[0] + (2 * w), A[1]
1429
1430
1431 a_AB = A[1] + A[0]
1432 a_CD = C[1] + C[0]
1433
1434
1435
1436
1437
1438 if ((D[1] >= 0) and (B[1] <= 0) and (a_AB <= 0) and (a_CD >= 0)):
1439 return True
1440 return False
1441
1442
1448
1459
1460
1461
1462
1464
1465 """
1466 A connection formed between 2 EditorNodes on a EditorCanvas. A EditorConnection is used
1467 to cover any topographica connection (connection / projection), and extending
1468 classes will supply a draw method and other type specific attributes.
1469 """
1470 - def __init__(self, name, canvas, from_node):
1471 EditorObject.__init__(self, name, canvas)
1472 self.from_node = from_node
1473 self.to_node = None
1474
1475 self.to_position = from_node.get_pos()
1476 self.mode = canvas.display_mode
1477
1478
1479
1480
1482 EditorObject.set_focus(self, focus)
1483 self.draw()
1484
1485
1486
1487
1491
1493 self.to_position = pos
1494 self.draw()
1495
1496 - def connect(self, to_node, con) :
1497 self.simobj = con
1498 if (self.name == ""):
1499 self.name = con.name
1500 self.to_node = to_node
1501 self.to_position = None
1502 self.from_node.attach_connection(self, self.FROM)
1503 self.to_node.attach_connection(self, self.TO)
1504
1506
1507
1508
1509
1510
1511 if hasattr(self,'simobj'):
1512 self.simobj.remove()
1513
1514
1515
1516
1520
1521
1522
1524 """
1525 Represents any topo EPConnection using a line with an arrow head in the middle.
1526 """
1527 - def __init__(self, name, canvas, from_node):
1542
1543
1544
1545
1550
1552 colours = {'video' : ('dark red', 'blue', 'yellow'),
1553 'normal': ('dark red', 'blue', 'yellow'),
1554 'printing': ('grey', 'black', 'black')}
1555 self.colour = colours[self.mode]
1556
1558
1559 for id in self.id :
1560 self.canvas.delete(id)
1561 self.canvas.delete(self.label)
1562 from_position = self.from_node.get_pos()
1563 if (self.to_node == None) :
1564 to_position = self.to_position
1565 else:
1566 to_position = self.to_node.get_pos()
1567
1568 self.draw_fn(from_position, to_position)
1569
1570 - def draw_line(self,from_position, to_position):
1571
1572 if (self.focus) :
1573 text_col = col = self.colour[0]
1574 lateral_colour = self.from_node.colour[0]
1575 else:
1576 text_col = 'black'
1577 col = self.colour[1]
1578 lateral_colour = ''
1579 middle = self.get_middle(from_position, to_position)
1580 factor = self.canvas.scaling_factor
1581 if (to_position == from_position) :
1582 deviation = self.draw_index * 15 * factor
1583 x1 = to_position[0] - ((20 * factor) + deviation)
1584 y2 = to_position[1]
1585 x2 = x1 + (40 * factor) + (2 * deviation)
1586 y1 = y2 - ((30 * factor) + deviation)
1587 midX = self.get_middle((x1,0),(x2,0))[0]
1588
1589 self.id = (self.canvas.create_oval(x1, y1, x2, y2, outline = col),
1590 self.canvas.create_line(midX, y1, midX+1, y1, arrow = FIRST, fill = col))
1591
1592 self.label = self.canvas.create_text(middle[0] -
1593 (20 + len(self.name)*3), middle[1] - (30 + deviation) , text = self.name)
1594 else :
1595
1596 dev = self.deviation
1597 from_pos = from_position[0] + self.deviation, from_position[1]
1598 mid = middle[0] + 0.5 * dev, middle[1]
1599 self.id = (self.canvas.create_line(from_pos, mid , arrow = LAST, fill = col),
1600 self.canvas.create_line(mid, to_position, fill = col))
1601
1602 dX = 20 * factor
1603 dY = self.draw_index * 20 * factor
1604 self.label = self.canvas.create_text(middle[0] - dX,
1605 middle[1] - dY, fill = text_col, text = self.name, anchor = E)
1606
1607
1608
1619
1620
1626
1633
1643
1644
1645
1647 return (pos1[0] + (pos2[0] - pos1[0])*0.5, pos1[1] + (pos2[1] - pos1[1])*0.5)
1648
1650 """Not implemented in this class"""
1651 return (0,0)
1652
1655
1657 n = self.draw_index
1658 sign = math.pow(-1, n)
1659 self.deviation = sign * width + (-sign) * math.pow(0.5, math.ceil(0.5 * (n))) * width
1660
1661
1662
1664 """Not implemented in this class"""
1665 return (1,1)
1666
1668 factor = self.canvas.scaling_factor
1669
1670
1671 to_position = self.to_node.get_pos()
1672 from_position = self.from_node.get_pos()
1673 if (self.to_node == self.from_node):
1674 dev = self.draw_index * 15 * factor
1675 middle = (to_position[0], to_position[1] - ((30 * factor) + dev))
1676 else:
1677 dev = self.deviation * 0.5
1678 middle = self.get_middle(from_position, to_position)
1679 if ((x < middle[0] + 10 + dev) & (x > middle[0] - 10 + dev) & (y < middle[1] + 10) & (y > middle[1] - 10)):
1680 return True
1681 return False
1682
1687
1688
1689
1691 """
1692 Represents any topo CFProjection. It is a subclass of EditorEPConnection and fills
1693 in the methods that are not defined. Can be represented by a representation of a
1694 projection's receptive field or by a line with an arrow head in the middle;
1695 lateral projections are represented by a dotted ellipse around the center.
1696 Can determine if x,y coord is within the triangular receptive field or within an
1697 area around the arrow head. The same can be determined for a lateral projection
1698 ellipse.
1699 """
1700
1701 - def __init__(self, name, canvas, from_node, receptive_field = True):
1712
1713
1714
1715
1723
1724
1726
1727 if (self.focus) :
1728 text_col = col = self.colour[0]
1729 lateral_colour = self.from_node.colour[0]
1730 else:
1731 text_col = 'black'
1732 col = self.colour[1]
1733 lateral_colour = ''
1734
1735 middle = self.get_middle(from_position, to_position)
1736 factor = self.canvas.scaling_factor
1737 if (to_position == from_position) :
1738 a, b = self.get_radius()
1739 x1 = to_position[0] - a
1740 y1 = to_position[1] + b
1741 x2 = to_position[0] + a
1742 y2 = to_position[1] - b
1743 self.id = (self.canvas.create_oval(x1, y1, x2, y2, fill = lateral_colour,
1744 dash = (2,2), outline = self.colour[2], width = 2), None)
1745
1746
1747
1748
1749 else :
1750 x1, y1 = to_position
1751 x2, y2 = from_position
1752
1753 radius_x, radius_y = self.get_radius()
1754 self.id = (self.canvas.create_line(x1, y1, x2 - radius_x, y2, fill = col),
1755 self.canvas.create_line(x1, y1, x2 + radius_x, y2, fill = col),
1756 self.canvas.create_oval(x2 - radius_x, y2 - radius_y,
1757 x2 + radius_x, y2 + radius_y, outline = col))
1758
1759 dX = 20
1760 dY = self.draw_index * 20
1761 self.label = self.canvas.create_text(middle[0] - dX,
1762 middle[1] - dY, fill = text_col, text = self.name, anchor = E)
1763
1764
1766
1767 if (self.focus) :
1768 text_col = col = self.colour[0]
1769 lateral_colour = self.from_node.colour[0]
1770 else:
1771 text_col = 'black'
1772 col = self.colour[1]
1773 lateral_colour = ''
1774
1775 middle = self.get_middle(from_position, to_position)
1776 if (to_position == from_position) :
1777 a, b = self.factor
1778 x1 = to_position[0] - a
1779 y1 = to_position[1] + b
1780 x2 = to_position[0] + a
1781 y2 = to_position[1] - b
1782 self.id = (self.canvas.create_oval(x1, y1, x2, y2, fill = lateral_colour,
1783 dash = (2,2), outline = self.colour[2], width = 2), None)
1784
1785
1786
1787 else :
1788 x1, y1 = to_position
1789 x2, y2 = from_position
1790 x2 += self.deviation
1791 radius = self.normal_radius
1792 self.id = (self.canvas.create_line(x1, y1, x2 - radius, y2, fill = col),
1793 self.canvas.create_line(x1, y1, x2 + radius, y2, fill = col),
1794 self.canvas.create_oval(x2 - radius, y2 - (0.5 * radius),
1795 x2 + radius, y2 + (0.5 * radius), outline = col))
1796
1797 dX = 20
1798 dY = self.draw_index * 20
1799 self.label = self.canvas.create_text(middle[0] - dX,
1800 middle[1] - dY, fill = text_col, text = self.name, anchor = E)
1801
1802
1803
1804
1806 factor = self.canvas.scaling_factor
1807 if self.to_node == None:
1808 return (factor * self.normal_radius, factor * self.normal_radius *
1809 self.from_node.height / self.from_node.width)
1810 node = self.from_node
1811 node_bounds = node.simobj.bounds.aarect().lbrt()
1812
1813 try:
1814 bounds = self.simobj.bounds_template.lbrt()
1815 except AttributeError:
1816 return (factor * self.normal_radius, factor * self.normal_radius *
1817 self.from_node.height / self.from_node.width)
1818 radius_x = factor * (node.width / 2) * (bounds[2] - bounds[0]) / (node_bounds[2] - node_bounds[0])
1819 radius_y = radius_x * node.height / node.width
1820 return radius_x, radius_y
1821
1822
1823
1825 factor = self.canvas.scaling_factor
1826 w = factor * self.from_node.width; h = factor * self.from_node.height
1827 a = 20; n = (w / 2) - 10; b = (n - a)
1828 major = a + (b * (1 - pow(0.8, self.draw_index)))
1829 a = 20 * h / w; n = (h / 2) - 10; b = (n - a)
1830 minor = a + (b * (1 - pow(0.8, self.draw_index)))
1831 return major, minor
1832
1835
1836
1837
1839 factor = self.canvas.scaling_factor
1840 if self.view == 'radius':
1841 A = self.to_node.get_pos()
1842 T = self.from_node.get_pos()
1843 B = (T[0] - self.radius[0], T[1])
1844 C = (T[0] + self.radius[0], T[1])
1845 else:
1846 A = self.to_node.get_pos()
1847 T = (self.from_node.get_pos()[0] + self.deviation ,self.from_node.get_pos()[1])
1848 B = (T[0] - (factor * self.normal_radius), T[1])
1849 C = (T[0] + (factor * self.normal_radius), T[1])
1850 den_BA = (A[0] - B[0])
1851 if not(den_BA == 0):
1852 m_BA = (A[1] - B[1]) / den_BA
1853 else :
1854 m_BA = 99999
1855 den_CA = (A[0] - C[0])
1856 if not(den_CA == 0):
1857 m_CA = (A[1] - C[1]) / den_CA
1858 else :
1859 m_CA = 99999
1860 return (m_BA, m_CA)
1861
1863 factor = self.canvas.scaling_factor
1864 if self.view == 'line':
1865 return EditorEPConnection.in_bounds(self,x,y)
1866 else:
1867
1868 if (self.to_node == None or self.to_node == self.from_node):
1869 if self.view == 'radius':
1870 a, b = self.get_radius()
1871 else:
1872 a, b = self.factor
1873 x, y = x - self.to_node.get_pos()[0], y - self.to_node.get_pos()[1]
1874 if (x > a or x < -a):
1875 return False
1876 pY = math.sqrt(pow(b,2) * (1 - (pow(x,2)/pow(a,2))))
1877 if (y > pY or y < -pY):
1878 return False
1879 return True
1880
1881
1882
1883
1884 to_position = self.to_node.get_pos()
1885 from_position = self.from_node.get_pos()
1886 if self.view == 'radius':
1887 A = (to_position[0] - x, to_position[1] - y)
1888 T = (from_position[0] + (self.deviation * factor) - x, from_position[1] - y)
1889 B = (T[0] - self.radius[0], T[1])
1890 C = (T[0] + self.radius[0], T[1])
1891 else:
1892 A = (to_position[0] - x, to_position[1] - y)
1893 T = (from_position[0] + (self.deviation * factor) - x, from_position[1] - y)
1894 B = (T[0] - (self.normal_radius * factor), T[1])
1895 C = (T[0] + (self.normal_radius * factor), T[1])
1896
1897 if (((A[1] < B[1]) and (B[1] < 0 or A[1] > 0)) or
1898 ((A[1] >= B[1]) and (B[1] > 0 or A[1] < 0))):
1899 return False
1900
1901 a_BA = A[1] - (self.gradient[0] * A[0])
1902 a_CA = A[1] - (self.gradient[1] * A[0])
1903
1904
1905
1906
1907
1908 if (((0 - a_CA) / self.gradient[1] >= 0) and ((0 - a_BA) / self.gradient[0] <= 0)):
1909 return True
1910 return False
1911