Package teamwork :: Package widgets :: Package PsychGUI :: Package AgentWindow :: Module RelationshipPane
[hide private]
[frames] | no frames]

Source Code for Module teamwork.widgets.PsychGUI.AgentWindow.RelationshipPane

  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   
11 -class RelationFrame(Pmw.ScrolledFrame):
12 """Widget for the display and editing of inter-agent relationships 13 """
14 - def __init__(self,parent=None,**kw):
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 # Button for adding new state feature 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 # Button for deleting state feature 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 # Button for viewing dynamics 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 # Pages of relationships 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
81 - def addRelation(self,confirm=None):
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 # Check whether new relationship is legal 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 # Create new dynamic link 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 # Create new static relationship 118 self['entity'].relationships[name] = [] 119 self.addStatic(name) 120 self.component('Relationship Book').selectpage(name) 121 else: 122 dialog.activate(geometry = 'centerscreenalways')
123
124 - def setExpert(self):
125 pass
126
127 - def addDynamic(self,relation,index=Pmw.END):
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 # Button for adding relationship 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 # Let's figure out what we already have 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 # Leftover widgets are no longer relevant 174 for name in widgetList: 175 self.destroycomponent(name) 176 # Leftover names have no widgets 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):
192 pass
193 # if relation in self['entity'].getLinkTypes() and \ 194 # relation != self['entity']._supportFeature and \ 195 # relation != self['entity']._trustFeature: 196 # # Edit dynamics only if dynamic but not built-in 197 # self.component('edit').configure(state='normal') 198 # else: 199 # self.component('edit').configure(state='disabled') 200
201 - def dynamics(self):
202 # Create the dynamics dialog 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
227 - def changeDynamics(self,button):
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
240 - def newLinkOptions(self,relation):
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
247 - def addLinkee(self,relation):
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
261 - def addScale(self,name,relation):
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 # Insert scale into correct slot 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
281 - def update(self):
282 for relation in self['entity'].getLinkTypes(): 283 for name in self['entity'].getLinkees(relation): 284 key = self['entity'].getLinkKey(relation,name) 285 value = self['entity'].getLink(relation,name) 286 self.component(str(key))['distribution'] = Distribution({value:1.})
287 300
301 - def addStaticFiller(self,agent):
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 # Assume we are in generic society viewing 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
314 - def removeFiller(self,agent):
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 # Check dynamics relationships 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
331 - def addStatic(self,relation):
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 # Hacky way of detecting lightweight agent 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 # Add relevant object entities 351 choices.sort() 352 # For now, we don't allow changing relationships on instantiated agents 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
364 - def selectRelatee(self,name,value):
365 relation = self.component('Relationship Book').getcurselection() 366 if value: 367 if name in self['entity'].relationships[relation]: 368 # This should never happen... 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
376 - def delete(self):
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 # Static relationship 383 self.destroycomponent(relation) 384 del self['entity'].relationships[relation] 385 else: 386 # Dynamic relationship 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
392 - def renameEntity(self,old,new):
393 for relation,fillers in self['entity'].relationships.items(): 394 # Check static relationships 395 if new in fillers: 396 menu = self.component(relation) 397 # RadioSelect doesn't provide individual deletion! 398 # The following is adapted from source of deletall() 399 menu.destroycomponent(old) 400 menu._buttonList.remove(old) 401 try: 402 menu.selection.remove(old) 403 except ValueError: 404 # Wasn't selected 405 pass 406 menu.add(new) 407 menu.setvalue(fillers[:]) 408 for relation in self['entity'].getLinkTypes(): 409 # Check dynamics relationships 410 if new in self['entity'].getLinkees(relation): 411 key = self['entity'].getLinkKey(relation,old) 412 self.destroycomponent(str(key)) 413 self.addScale(new,relation)
414