1 import copy
2 from Tkinter import *
3 import Pmw
4 import tkMessageBox
5 from teamwork.math.Keys import LinkKey
6 from teamwork.math.probability import Distribution
7 from teamwork.widgets.pmfScale import PMFScale
8 from DynamicsDialog import DynamicsDialog
9 from teamwork.widgets.images import loadImages
10
12 """Widget for the display and editing of inter-agent relationships
13 """
15 self.images = loadImages({'new': 'icons/chain--plus.png',
16 'del': 'icons/chain--minus.png',
17 'tree': 'icons/application-tree.png'})
18 optiondefs = (
19 ('entity', None, Pmw.INITOPT),
20 ('society', {}, None),
21 ('balloon', None, Pmw.INITOPT),
22 ('expert', False, self.setExpert),
23 ('generic', False, Pmw.INITOPT),
24 ('network', None, None),
25 )
26 self.defineoptions(kw,optiondefs)
27 Pmw.ScrolledFrame.__init__(self,parent)
28 toolbar = Frame(self.interior(),bd=2,relief='raised')
29 toolbar.grid(row=0,column=0,sticky='ew')
30 if self['generic']:
31
32 button = Label(toolbar)
33 if self.images.has_key('new'):
34 button.configure(bd=0,image=self.images['new'])
35 else:
36 button.configure(text='New')
37 button.bind('<ButtonRelease-1>',self.addRelation)
38 button.pack(side='left',ipadx=5)
39 if self['balloon']:
40 self['balloon'].bind(button,'Create new relationship type')
41
42 button = Label(toolbar)
43 if self.images.has_key('del'):
44 button.configure(bd=0,image=self.images['del'])
45 else:
46 button.configure(text='Delete')
47 button.bind('<ButtonRelease-1>',self.delete)
48 button.pack(side='left',ipadx=5)
49 if self['balloon']:
50 self['balloon'].bind(button,'Delete relationship type')
51
52 button = Label(toolbar)
53 if self.images.has_key('tree'):
54 button.configure(bd=0,image=self.images['tree'])
55 else:
56 button.configure(text='Dynamics')
57 button.bind('<ButtonRelease-1>',self.dynamics)
58 button.pack(side='left',ipadx=5)
59 if self['balloon']:
60 self['balloon'].bind(button,'View dynamics of selected relationship')
61
62 book = self.createcomponent('Relationship Book',(),None,
63 Pmw.NoteBook,(self.interior(),))
64 if self['generic']:
65 book.configure(raisecommand=self.selectPage)
66 self.links = self['entity'].getLinkTypes()
67 if not self['entity']._supportFeature in self.links:
68 self.links.append(self['entity']._supportFeature)
69 if not self['entity']._trustFeature in self.links:
70 self.links.append(self['entity']._trustFeature)
71 self.links.sort(lambda x,y:cmp(x.lower(),y.lower()))
72 for relation in self.links:
73 self.addDynamic(relation)
74 for relation in self['entity'].relationships.keys():
75 self.addStatic(relation)
76 book.grid(row=1,column=0,columnspan=3,sticky='wens',padx=10,pady=10)
77 self.interior().grid_columnconfigure(0,weight=1)
78 self.interior().grid_rowconfigure(1,weight=1)
79 self.initialiseoptions()
80
82 try:
83 dialog = self.component('newdialog')
84 except KeyError:
85 dialog = self.createcomponent('newdialog',(),None,
86 Pmw.PromptDialog,
87 (self.interior(),),
88 title='New Relationship Type',
89 entryfield_labelpos = 'n',
90 label_text='Relationship:',
91 command=self.addRelation,
92 defaultbutton=0,
93 buttons=('OK','Cancel'))
94 frame = dialog.component('dialogchildsite')
95 self.dynamicVar = IntVar()
96 dialog.createcomponent('dynamic',(),None,Checkbutton,(frame,),
97 text='Dynamic',variable=self.dynamicVar)
98 dialog.component('dynamic').pack()
99 if isinstance(confirm,str):
100 dialog.deactivate()
101 if confirm == 'OK':
102 name = dialog.get()
103
104 if len(name) == 0:
105 tkMessageBox.showerror('Empty Relationship',
106 'No name entered!')
107 elif name in self['entity'].getLinkTypes() or \
108 self['entity'].relationships.has_key(name):
109 tkMessageBox.showwarning('Duplicate Relationship','This agent already has a relationship named "%s"' % (name))
110 elif self.dynamicVar.get():
111
112 self['entity'].linkTypes.append(name)
113 self['entity'].linkDynamics[name] = {}
114 self.addDynamic(name)
115 self.component('Relationship Book').selectpage(name)
116 else:
117
118 self['entity'].relationships[name] = []
119 self.addStatic(name)
120 self.component('Relationship Book').selectpage(name)
121 else:
122 dialog.activate(geometry = 'centerscreenalways')
123
126
128 """Adds a page for a dynamic relationship"""
129 book = self.component('Relationship Book')
130 try:
131 page = book.page(relation)
132 except ValueError:
133 page = book.insert(relation,index)
134 if self['generic'] and not 'new %s' % (relation) in self.components():
135
136 g = Pmw.Group(page,tag_text='New Link')
137 palette = Pmw.Color.getdefaultpalette(self.component('hull'))
138 b = self.createcomponent('new %s' % (relation),(),None,
139 Pmw.ComboBox,(g.interior(),),
140 labelpos='n',label_text='Add link to:',
141 autoclear=True,history=True,unique=True,
142 entry_state='disabled',
143 entry_disabledforeground=palette['foreground'],
144 entry_disabledbackground=palette['background'],
145 )
146 b.component('popup').bind('<Map>',lambda event,s=self,r=relation:
147 s.newLinkOptions(r))
148 b.pack(side='left')
149 Button(g.interior(),command=lambda s=self,r=relation:
150 s.addLinkee(r),text='Add').pack(side='left')
151 g.pack(side='top',fill='x',expand='yes')
152 try:
153 frame = self.component('%s frame' % (relation))
154 except KeyError:
155 frame = self.createcomponent('%s frame' % (relation),(),None,
156 Pmw.ScrolledFrame,(page,),
157 vertflex='expand',horizflex='expand')
158 frame.interior().columnconfigure(1,weight=1)
159 frame.pack(side='top',fill='both',expand='yes')
160 linkees = self['entity'].getLinkees(relation)
161 linkees.sort(lambda x,y:cmp(x.lower(),y.lower()))
162
163 widgetList = filter(lambda w:self.componentgroup(w) == relation,
164 self.components())
165 leftover = linkees[:]
166 for name in linkees:
167 key = self['entity'].getLinkKey(relation,name)
168 try:
169 widgetList.remove(str(key))
170 leftover.remove(name)
171 except ValueError:
172 pass
173
174 for name in widgetList:
175 self.destroycomponent(name)
176
177 last = None
178 for name in leftover:
179 key = self['entity'].getLinkKey(relation,name)
180 try:
181 widget = self.component(str(key))
182 if last:
183 widget.pack(after=last,side='top',fill='x')
184 else:
185 widget.pack(side='top',fill='x')
186 except KeyError:
187 widget = self.addScale(name,relation)
188 last = widget
189 self.component('Relationship Book').setnaturalsize()
190
191 - def selectPage(self,relation):
193
194
195
196
197
198
199
200
202
203 relation = self.component('Relationship Book').getcurselection()
204 dynamics = self['entity'].linkDynamics[relation]
205 roles = ['actor','object','self']
206 roles += self['entity'].relationships.keys()
207 roles.sort(lambda x,y:cmp(x.lower(),y.lower()))
208 for entity in self['entity'].getEntityBeliefs():
209 for newRole in entity.relationships.keys():
210 if not newRole in roles:
211 roles.append(newRole)
212 self.createcomponent('dialog',(),None,DynamicsDialog,
213 (self.interior(),),expert=self['expert'],
214 buttons=('OK','Cancel'),
215 defaultbutton='OK',
216 title='Dynamics of %s of %s' % \
217 (relation,self['entity'].name),
218 feature=relation,editor_roles=roles,
219 key=LinkKey({'subject':'self',
220 'verb':relation,
221 'object':'actor'}),
222 society=self['entity'].hierarchy,
223 dynamics=copy.deepcopy(dynamics),
224 command=self.changeDynamics,
225 ).activate()
226
228 """Upon clicking OK in dynamics dialog, change entity's dynamics accordingly
229 @param button: the button pressed to close the dialog (generated by Tk callback)
230 @type button: str
231 """
232 dialog = self.component('dialog')
233 if button == 'OK':
234 dynamics = self['entity'].linkDynamics[dialog['feature']]
235 dynamics.clear()
236 dynamics.update(dialog['dynamics'])
237 dialog.deactivate()
238 self.destroycomponent('dialog')
239
241 names = filter(lambda n: not n in self['entity'].getLinkees(relation),
242 self['entity'].hierarchy.keys())
243 widget = self.component('new %s' % (relation))
244 names.sort(lambda x,y:cmp(x.lower(),y.lower()))
245 widget.component('scrolledlist').setlist(names)
246
248 """Add a new dynamic relationship between this entity and the currently selected one in the new link widget
249 """
250 widget = self.component('new %s' % (relation))
251 name = widget.get()
252 if name:
253 widget.clear()
254 self['entity'].setLink(relation,name,0.)
255 self.addScale(name,relation)
256 if self['network']:
257 self['network'].setview()
258 else:
259 tkMessageBox.showerror('Missing Agent','You have not selected an agent to link to.')
260
262 value = self['entity'].getLink(relation,name)
263 distribution = Distribution({value:1.})
264 key = self['entity'].getLinkKey(relation,name)
265 page = self.component('%s frame' % (relation)).interior()
266 widget = self.createcomponent(str(key),(),relation,
267 PMFScale,(page,),
268 distribution=distribution,
269 )
270 widget.configure(command=self.updateLink)
271
272 widgetList = filter(lambda w:self.componentgroup(w) == relation,
273 self.components())
274 widgetList.sort()
275 index = widgetList.index(str(key))
276 Label(page,text=name,justify='left').grid(row=index+1,column=0,
277 sticky='ewns')
278 widget.grid(row=index+1,column=1,sticky='ew')
279 return widget
280
287
300
302 """Adds a new button to all of the static relationship selection panes
303 @type agent: str
304 """
305 for name in filter(lambda n:self.componentgroup(n) == 'static',
306 self.components()):
307 widget = self.component(name)
308 widget.add(agent)
309
310 choices = self['entity'].hierarchy.keys()
311 choices.sort(lambda x,y: cmp(x.lower(),y.lower()))
312 widget.component(agent).grid(row=choices.index(agent))
313
315 """Removes appropriate fillers from all relationship panes
316 @type agent: str
317 """
318 for name in filter(lambda n:self.componentgroup(n) == 'static',
319 self.components()):
320 widget = self.component(name)
321 widget.destroycomponent(agent)
322 if agent in self['entity'].relationships[name]:
323 self['entity'].relationships[name].remove(agent)
324 for relation in self['entity'].getLinkTypes():
325
326 if agent in self['entity'].getLinkees(relation):
327 key = self['entity'].getLinkKey(relation,agent)
328 self.destroycomponent(str(key))
329 self['entity'].removeLink(relation,agent)
330
332 book = self.component('Relationship Book')
333 page = book.add(relation)
334 if self['generic']:
335 choices = self['entity'].hierarchy.keys()
336 else:
337 try:
338 choices = self['entity'].entities.keys()
339 except AttributeError:
340
341 choices = self['entity'].relationships[relation][:]
342 frame = Pmw.ScrolledFrame(page)
343 menu = self.createcomponent(relation, (), 'static',
344 Pmw.RadioSelect,
345 (frame.interior(),),
346 buttontype='checkbutton',
347 orient='vertical',
348 selectmode='multiple',
349 )
350
351 choices.sort()
352
353 if self['generic']:
354 menu.configure(command=self.selectRelatee)
355 activity = 'normal'
356 else:
357 activity = 'disabled'
358 map(lambda n:menu.add(n),choices)
359 menu.setvalue(self['entity'].relationships[relation][:])
360 map(lambda n:menu.component(n).configure(state=activity),choices)
361 menu.pack(side='left',fill='both',expand='yes')
362 frame.pack(side='top',fill='both',expand='yes')
363
365 relation = self.component('Relationship Book').getcurselection()
366 if value:
367 if name in self['entity'].relationships[relation]:
368
369 raise NameError,'Adding duplicate relationship %s to %s' % \
370 (relation,name)
371 else:
372 self['entity'].relationships[relation].append(name)
373 else:
374 self['entity'].relationships[relation].remove(name)
375
377 """Deletes the selected relationship"""
378 relation = self.component('Relationship Book').getcurselection()
379 result = tkMessageBox.askyesno('Confirm Delete','Are you sure you wish to delete this relationship?')
380 if result:
381 if self['entity'].relationships.has_key(relation):
382
383 self.destroycomponent(relation)
384 del self['entity'].relationships[relation]
385 else:
386
387 del self['entity'].linkDynamics[relation]
388 self['entity'].linkTypes.remove(relation)
389 self.destroycomponent('%s frame' % (relation))
390 self.component('Relationship Book').delete(relation)
391
414