Package Pmv :: Module controlPanelCommands
[hide private]
[frames] | no frames]

Source Code for Module Pmv.controlPanelCommands

   1  ########################################################################### 
   2  # 
   3  # Author: Michel F. SANNER, Yong Zhao 
   4  # 
   5  # Copyright: M. Sanner TSRI 2006 
   6  # 
   7  ############################################################################# 
   8   
   9  # 
  10  # $Header 
  11  #  
  12  # $Id 
  13  # 
  14   
  15  ## BUGS 
  16  # - checkbutton canvas glides further than header at far right (when xscroll 
  17  #   is enabled) 
  18  # - order of chained command is no longer guaranteed as we use a dict 
  19  # 
  20  ##    
  21  ## TODO 
  22  ## 
  23  # - Support return key for jumping to next match 
  24  #   This might be doable using iterators or generators 
  25  # - support sets in selector 
  26  # - allow to embbed into Pmv GUI 
  27  # - allow somehow to select  node based on PMV selection 
  28  # - make secondary structure glyphs pickable for selection 
  29  # 
  30  #   Partially done 
  31  # - make checkbuttons reflect current state 
  32  #      labels not done yet 
  33  #      color commands do not work with undo because the undo command uses 
  34  #      self.vf.color command with a list of colors and the coloring type  
  35  #      is lost 
  36  # 
  37  ########################################################################## 
  38  ## 
  39  ## Tree customization  stuff 
  40  ## 
  41  ########################################################################## 
  42  import Tkinter, Image, ImageTk, os, Pmw, types, re 
  43   
  44  from mglutil.gui.BasicWidgets.Tk.TreeWidget.tree import TreeView, Node, OFFSET 
  45  from mglutil.util.callback import CallbackFunction 
  46   
  47  from MolKit.tree import TreeNode, TreeNodeSet 
  48  from MolKit.molecule import Atom, AtomSet, Molecule, MoleculeSet 
  49  from MolKit.protein import Chain, ChainSet, Residue, ResidueSet, Protein, ProteinSet 
  50   
  51  from Pmv.displayCommands import DisplayCommand 
  52   
53 -class NodeWithCheckbuttons(Node):
54
55 - def __init__(self, name, object=None, mouseBinding=None, 56 hasChildren=False, firstExpand_cb=None):
57 58 Node.__init__(self, name, object=object, mouseBinding=mouseBinding, 59 hasChildren=hasChildren, firstExpand_cb=firstExpand_cb) 60 61 self.chkbtVar = [] 62 self.chkbt = []
63 64
65 - def createTkVar(self, nbColumns):
66 # create all the buttons intially, so we can set the variables and keep 67 # them in sync with Pmv 68 for i in range(nbColumns): 69 self.chkbtVar.append(Tkinter.IntVar())
70 71
72 - def displayValue(self):
73 """Add things to the rigth side of the tree 74 """ 75 if len(self.chkbtVar)==0: return 76 master = self.tree.master 77 canvas = self.tree.scrolledCanvas.interior() 78 tree = self.tree 79 dx = self.x-self.tree.offx 80 level = dx/OFFSET 81 col = ['gray75', 'red', 'cyan', 'green', 'yellow'] 82 83 for i in range(tree.nbColumns): 84 if not tree.colHasButtton(i, self.objectKey.__class__): 85 self.chkbt.append(None) 86 continue 87 88 v = self.chkbtVar[i] 89 cb = CallbackFunction(self.buttonClick, self, i ) 90 button = Tkinter.Checkbutton(canvas, variable=v, command=cb, 91 padx=0, pady=0, 92 background=col[level-1], 93 activeforeground='pink') 94 95 self.chkbt.append(button) 96 cid = canvas.create_window( 20 + self.tree.offx + 175 + i*35, 97 self.y, window=button, 98 width=20, height=15) 99 self.canvasIDs.append(cid) 100 101 # add secondary structure glyph 102 molFrag = self.objectKey 103 if isinstance(molFrag, Residue): 104 if hasattr(molFrag, 'secondarystructure'): 105 ssname = molFrag.secondarystructure.name 106 if ssname[:6]=='Strand': color = '#FFF700' 107 elif ssname[:4]=='Coil': color = 'grey45' 108 elif ssname[:5]=='Helix': color = '#FF198C' 109 elif ssname[:4]=='Turn': color = 'blue' 110 111 cid = canvas.create_rectangle( 112 150 + self.tree.offx, self.y-10, 113 160 + self.tree.offx, self.y+10, outline=color,fill=color) 114 self.canvasIDs.append(cid) 115 116 cid = canvas.create_text( 162 + self.tree.offx, 117 self.y, text=ssname, anchor=Tkinter.W) 118 # fill=color) 119 self.canvasIDs.append(cid) 120 121 func = tree.buttonValFunc[i] 122 if func: 123 func(self)
124 125
126 - def buttonClick(self, node, column):
127 # get called for each checkbutton 128 tree = node.tree 129 tree.inChain = False 130 tree.initCallback(node, column) 131 tree.callbacks[column](node, column) 132 tree.chainCommands(node, column)
133 134
135 - def getNodes(self, column):
136 # return the objects associated with this node 137 # handle the backbone, sidechain and both value for the command 138 result = molFrag = self.objectKey 139 140 bbmode = self.tree.bbmodevar.get() 141 if bbmode=='Cmd setting': 142 #print 'Cmd setting found' 143 bbmode = self.tree.bbmode[column] 144 145 #print 'bbmode in getNode', column, bbmode 146 if bbmode!='All': 147 if molFrag.findType(Chain)[0].isProteic(): 148 atoms = molFrag.findType(Atom) 149 if bbmode=='Backbone': 150 result = atoms.get('backbone') 151 elif bbmode=='Sidechain+CA': 152 result = atoms.get('sidechain')+atoms.get('CA') 153 else: 154 result = atoms.get('sidechain') 155 try: 156 return result.setClass([result]) 157 except KeyError: 158 return result 159 160 try: 161 return result.setClass([result]) 162 except KeyError: 163 return result
164 165
166 - def getObjects(self, column):
167 # return a list of objects associated with this node and possibly 168 # other seleted nodes. For selection we return a list for each type 169 # ( i.e. Atom, Residue, etc..) 170 # if the node is selected, collect object from all other selected nodes 171 resultAtoms = AtomSet([]) 172 resultResidues = ResidueSet([]) 173 resultChains = ChainSet([]) 174 resultMolecules = MoleculeSet([]) 175 buttonValue = self.chkbtVar[column].get() 176 if self.selected: 177 for node in self.tree.list_selected: 178 node.chkbtVar[column].set(buttonValue) 179 result = node.getNodes(column) 180 obj = result[0] 181 if isinstance(obj, Atom): 182 resultAtoms += result 183 elif isinstance(obj, Residue): 184 resultResidues += result 185 elif isinstance(obj, Chain): 186 resultChains += result 187 elif isinstance(obj, Molecule) or isinstance(obj, Protein): 188 resultMolecules += result 189 result = [] 190 if len(resultAtoms): result.append(resultAtoms) 191 if len(resultResidues): result.append(resultResidues) 192 if len(resultChains): result.append(resultChains) 193 if len(resultMolecules): result.append(resultMolecules) 194 return result 195 else: 196 return [self.getNodes(column)]
197 198
199 - def _matchName(self, pat):
200 if self.parent: 201 name = self.parentFullname.replace('|','').lower() 202 if pat.match(name+(self.name).lower()): 203 return self 204 else: 205 if pat.match(self.name.lower()): 206 return self 207 if self.expanded: 208 for c in self.children: 209 n = c._matchName(pat) 210 if n: return n 211 return None
212 213
214 -class KeySelectable:
215 """Adds the ability to use keystrokes to quickly select items in a list. 216 root has to be a widget supporting .bind .after 217 """ 218
219 - def __init__(self, root):
220 self.root = root 221 self.afterID = None 222 self.matchString = '' 223 self.lastMatchString = '' 224 root.bind('<KeyPress>', self.key_cb) 225 root.bind('<KeyRelease>', self.keyUp_cb) 226 self.isControl = False 227 self.isShift = False 228 self.isAlt = False 229 self.ctrlModCallback = None 230 self.shiftModCallback = None 231 self.altModCallback = None
232 233
234 - def timeOut(self, event=None):
235 """resets self.matchCharIndex to 0, called after a short period of 236 time if no new character has been typed""" 237 #print 'timeout' 238 self.lastMatchString = self.matchString 239 self.matchString = '' 240 self.matchCharIndex = 1 241 self.afterID = None
242 243
244 - def keyUp_cb(self, event=None):
245 if event.keysym=='Control_L' or event.keysym=='Control_R': 246 self.isControl = False 247 elif event.keysym=='Shift_L' or event.keysym=='Shift_R': 248 self.isShift = False 249 elif event.keysym=='Alt_L' or event.keysym=='Alt_R': 250 self.isAlt = False
251 252
253 - def key_cb(self, event=None):
254 # use key strokes to select entry in listbox 255 # strokes placed within 500 miliseconds are concatenated 256 #print self.matchCharIndex, '|', self.matchString, '|', event.keysym 257 if event.keysym=='Control_L' or event.keysym=='Control_R': 258 self.isControl = True 259 return 260 elif event.keysym=='Shift_L' or event.keysym=='Shift_R': 261 self.isShift = True 262 return 263 elif event.keysym=='Alt_L' or event.keysym=='Alt_R': 264 self.isAlt = True 265 return 266 267 if self.isControl: 268 if self.ctrlModCallback: 269 self.ctrlModCallback(event) 270 return 271 elif self.isShift: 272 if self.shiftModCallback: 273 self.shiftModCallback(event) 274 return 275 elif self.isAlt: 276 if self.altModCallback: 277 self.altModCallback(event) 278 return 279 280 if event.keysym=='Return': 281 str = self.lastMatchString 282 else: 283 str = self.matchString + event.keysym 284 #print str 285 item = self.match(str) 286 if item: 287 self.selectItem(item) 288 if self.afterID is not None: 289 self.root.after_cancel(self.afterID) 290 self.afterID = self.root.after(1000, self.timeOut) 291 self.matchString = str
292 293 # SUBCLASS THIS
294 - def match(self, name):
295 """has to return None if no match or an object that matches""" 296 return None
297 298
299 - def selectItem(self, item):
300 """do what has to be done to show what matches the typed string""" 301 print 'selecting item', item
302 303 304
305 -class TreeViewWithCheckbuttons(TreeView, KeySelectable):
306 """Tree Widget class 307 A TreeWiget contains tree nodes (object of Class Node). 308 Each node can have children. Nodes that do have children can be expanded 309 and collapsed using the + - icon placed before de nodes' icon. Each no has an 310 icon and a name. It is possible to associate an arbitrary Python object to each 311 node. The node in the tree is the graphical representation of the object. 312 """
313 - def __init__(self, master=None, name='Tree', multi_choice=False, 314 width=800, height=200, treeWidth=140, treeHeight=100, 315 historyWidth=100, historyHeight=100, mode='Extended', 316 historyVisible=False,nohistory=False, 317 mouseBinding=None,obj2Node=True, displayValue=False, 318 offx=0, offy=0):
319 320 TreeView. __init__(self, master=master, name=name, 321 multi_choice=multi_choice, 322 width=width, height=height, 323 treeWidth=treeWidth, treeHeight=treeHeight, 324 historyWidth=historyWidth, 325 historyHeight=historyHeight, 326 mode=mode, historyVisible=historyVisible, 327 nohistory=nohistory, 328 mouseBinding=mouseBinding, obj2Node=obj2Node, 329 displayValue=displayValue, 330 offx=offx, offy=offy, canvasHeaderCol=True) 331 332 KeySelectable.__init__(self, self.canvas) 333 # assign method that need to be overriden 334 self.match = self.findFirstMatchNodeFromName 335 self.selectItem = self.showNode 336 self.ctrlModCallback = self.handleControlKey 337 338 self.sets = None # used to save pmv.sets so that the selector can 339 # use it to allow selecting sets 340 self.balloons = Pmw.Balloon(master, yoffset=0) 341 342 self.inChain = False # true when calling chained commands. 343 # used to prevent calling chained or chained 344 345 nbcol = self.nbColumns = 15 346 #nbcol += 10 347 self.callbacks = [None]*nbcol 348 # will be the list of call backs associated with 349 # columns of checkbuttons 350 self.names = [None]*nbcol 351 # will be the list of names associated with 352 # columns of checkbuttons 353 self.colHeaderButtons = [None]*nbcol 354 self.colHeaderTkVars = [None]*nbcol 355 self.colOptionPanels = [None]*nbcol 356 self.pmvcmd = [None]*nbcol 357 self.chainWith = [None]*nbcol 358 for i in range(nbcol): 359 self.chainWith[i] = [None]*nbcol 360 self.balloonText = ['No Help']*nbcol 361 362 self.buttonValFunc = [None]*nbcol # function for getting the current values 363 364 # force molecule visible when selecting, displaying or labeling 365 # parts of molecules 366 for i in range(1,9): # chains command 0 after command 1 through 9 367 # 'Checked': call 0 when command 1 is checked 368 # 'True': always check the button of the chained command 369 # 'All': apply to the whole moelcular fragment 370 self.chainWith[i][0] = ('Checked', 'True', 'All') 371 372 # make display lines, S&B and CPK mutually exclusive 373 for i in range(2,5): 374 for j in range(2,5): 375 if i==j: continue 376 self.chainWith[i][j] = ('Checked','False','All') 377 378 # make color commands (9-14) radio 379 for i in range(9,15): 380 for j in range(9,15): 381 if i==j: continue 382 self.chainWith[i][j] = ('Checked','False','All') 383 384 self.bbmode = ['All']*nbcol 385 self.colHeaderTkVars = [None]*nbcol # list of button in the header 386 387 from mglutil.util.packageFilePath import findFilePath 388 self.ICONPATH = os.path.abspath(findFilePath('Icons', 'Pmv')) 389 self.ICONDIR = os.path.join(self.ICONPATH, '32x32') 390 self.iconList = [None]*nbcol # save references to icon images to 391 # prevent them from being garbage collected 392 393 # add the backbone menu option 394 self.bbmodevar = Tkinter.StringVar() 395 self.bbmodevar.set("Cmd setting") 396 self.bbmode_menu = Pmw.OptionMenu( 397 self.master, labelpos = 'w', label_text = 'Protein:', 398 menubutton_textvariable = self.bbmodevar, 399 items = ['Cmd setting', 'Backbone', 'Sidechain', 400 'Sidechains+CA', 'All'], 401 menubutton_width = 8 402 ) 403 cid = self.canvasHeaderCol.create_window( 404 10, 15, window=self.bbmode_menu, anchor='w') 405 self.proteinHelp = """This option menu allows to specify for peptidic molecular fragments 406 whether the command should be applied to the backbone atoms only, 407 the side chain atoms only, the sidechain atoms and CA atoms or the 408 full molecular frament.""" 409 self.balloons.bind(self.bbmode_menu, self.proteinHelp) 410 411 # add a compound selector entry 412 self.selectorEntry = Pmw.EntryField( 413 self.master, labelpos = 'w', label_text = 'Select: ', 414 entry_width=12, validate = None, command=self.selectFromString) 415 416 cid = self.canvasHeaderCol.create_window( 417 10, 40, window=self.selectorEntry, anchor='w') 418 self.selectorHelp = """This entry allows selecting enties in the Tree using a Pmv compound selector. 419 Only expanded nodes will be selected. Selected nodes are outlined with a 420 yellow selection box. When a button is checked for a selected node, the 421 command is applied to all selecte nodes. 422 The syntax for a compound selector is a ; separated list of expressions. 423 Each expression is a : separated list of selectors applying at the various 424 levels of the tree. 425 for instance: 426 :::CA selects all carbon alpha atoms 427 :A::CA selects all CA in chain A 428 ::CYS* selects all cysteins""" 429 self.balloons.bind(self.selectorEntry, self.selectorHelp) 430 431 from MolKit.stringSelector import CompoundStringSelector 432 self.selector = CompoundStringSelector()
433 434
435 - def handleControlKey(self, event):
436 if event.keysym in ['z', 'Z']: 437 self.undoSelect()
438 439
440 - def colHasButtton(self, column, klass):
441 """returns True if a given column has check buttons for a given klass 442 """ 443 if column==0: 444 if klass not in [Molecule, Protein, MoleculeSet, ProteinSet]: 445 return False 446 return True
447 448
449 - def findFirstMatchNodeFromName(self, name):
450 """walks the tree and find the first node whose fullname 451 matches name""" 452 453 pat = re.compile('.*'+name.lower()+'.*') 454 for root in self.roots: 455 n = root._matchName(pat) 456 if n: 457 return n 458 return None
459 460
461 - def selectFromString(self):
462 value = self.selectorEntry.getvalue() 463 allMols = self.roots[0].objectKey 464 molFrag = self.selector.select(allMols, value, self.sets) 465 self.selectNodes(molFrag[0])
466 467
468 - def setCallback(self, column, pmvcmd, iconName=None, balloonText=None, 469 name='command', function=None, bValFunc=None):
470 """define a callback for the checkbuttons in a given column. 471 pmvcmd is the Pmv command to run in the callback associated with the 472 checkbuttons and used to get default values using the 'default form. 473 name is a string describing the command in that column, defaults to cmd.name 474 iconName is the name of the icon to be used for the button 475 balloonText is the tooltip, defaults to name. 476 function is an option callback to override the default callback. 477 It gets called with (node, column, pmvcmd) 478 """ 479 assert column<1000 480 481 482 def callback(node, column): 483 # default call back 484 485 # search for button because some commands do not have 486 # buttons at all levels 487 while node and not node.chkbt[column]: 488 node = node.parent 489 if node is None: return 490 491 # get Pmv's command default arguments 492 pmvcmd = node.tree.pmvcmd[column] 493 defaultValues = pmvcmd.getLastUsedValues() 494 495 # handle negate key to reflect state of checkbutton 496 val = node.chkbtVar[column].get() 497 if defaultValues.has_key('negate'): 498 defaultValues['negate'] = not val 499 500 defaultValues['callListener'] = False 501 502 # apply the command at each level of the current selection 503 for obj in node.getObjects(column): 504 apply( pmvcmd, (obj,), defaultValues) 505 506 if pmvcmd.lastUsedValues['default'].has_key('callListener'): 507 del pmvcmd.lastUsedValues['default']['callListener']
508 509 if function is None: 510 function = callback 511 512 self.callbacks[column] = function 513 self.buttonValFunc[column] = bValFunc 514 515 if name is None: 516 if hasattr(function, 'name'): 517 name = function.name 518 self.names[column] = name 519 self.pmvcmd[column] = pmvcmd 520 if balloonText is None: 521 balloonText = name 522 self.balloonText[column] = balloonText 523 if iconName: 524 iconfile = os.path.join(self.ICONDIR, iconName) 525 image = Image.open(iconfile) 526 im = ImageTk.PhotoImage(image=image) 527 self.iconList[column] = im 528 529 v = Tkinter.IntVar() 530 cb = CallbackFunction(self.editColumn, column) 531 button = Tkinter.Checkbutton( 532 self.master, variable=v, command=cb, height=32, width=32, 533 indicatoron=0, image=im) 534 535 self.colHeaderButtons[column] = button 536 self.colHeaderTkVars[column] = v 537 538 cid = self.canvasHeaderCol.create_window( 539 20 + self.offx + 175 + column*35, 32, window=button, 540 width=40, height=40, anchor='center') 541 542 self.balloons.bind(button, self.balloonText[column]) 543 else: 544 cid = self.canvasHeaderCol.create_text( 545 20 + self.offx + 175 + column*35, 5, text=name, anchor='n')
546 547
548 - def editColumn(self, column):
549 if self.colHeaderTkVars[column].get()==0: # editor is shown 550 self.colOptionPanels[column].hide() 551 else: 552 if self.colOptionPanels[column]: 553 self.colOptionPanels[column].show() 554 else: 555 self.colOptionPanels[column] = CPCommandEditor(column, self)
556 557
558 - def initCallback(self, node, column):
559 self.manageChildren(node, column)
560 561
562 - def manageChildren(self, node, column):
563 # sets the checkbutton of all children to value of parent 564 if len(node.children)==0: 565 return 566 567 val = node.chkbtVar[column].get() 568 for c in node.children: 569 c.chkbtVar[column].set(val) 570 self.manageChildren(c, column)
571 572
573 - def chainCommands(self, node, column):
574 if self.inChain is True: 575 return 576 self.inChain = True 577 578 buttonValue = node.chkbtVar[column].get() 579 chainWith = self.chainWith[column] 580 581 for col in range(self.nbColumns): 582 # find out with what argument this col's command should be called 583 val = chainWith[col] 584 if val is None: continue 585 run, arg, prot = val 586 587 if run=='Checked' and not buttonValue: continue 588 if run=='Unchecked' and buttonValue: continue 589 590 if arg=='Same': value = buttonValue 591 elif arg=='Opposite': value= not buttonValue 592 elif arg=='True': value= 1 593 elif arg=='False': value= 0 594 595 # temporarly overwrite protin mode 596 bbmode = self.bbmodevar 597 oldbbmode = bbmode.get() 598 bbmode.set(prot) 599 600 #print 'col', col, 'run', run, 'arg', arg, 'prot', prot 601 if node.selected: 602 for n in self.list_selected: 603 while n and not n.chkbt[col]: 604 n = n.parent 605 if n: 606 n.chkbtVar[col].set(value) 607 if n: 608 self.callbacks[col](n, col) 609 self.manageChildren(n, col) 610 else: 611 n = node 612 while n and not n.chkbt[col]: 613 n = n.parent 614 if n: 615 n.chkbtVar[col].set(value) 616 self.callbacks[col](n, col) 617 self.manageChildren(n, col) 618 619 # restore bbmode 620 bbmode.set(oldbbmode)
621 622
623 - def addNodeTree(self, obj):
624 """recursively add a nodes for a hierarchy of objects rooted at obj 625 """ 626 node = self.addNode(obj.name, object=obj, parent=obj.parent, 627 hasChildren=len(obj.children), 628 nodeClass=NodeWithCheckbuttons) 629 node.createTkVar(self.nbColumns) 630 for child in obj.children: 631 self.addNodeTree(child)
632 633
634 -class CPCommandEditor:
635 """Object displaying an editor panel for a given column of button. 636 The editor allows to set default options for the command 637 and to associate other columns with with this one 638 """
639 - def __init__(self, column, tree, master=None):
640 self.column = column 641 self.tree = tree 642 self.editable = column < 14 643 644 if master is None: 645 self.root = Tkinter.Toplevel(tree.master) 646 self.root.title('Editor for command: %s'%\ 647 self.tree.names[column].replace('\n', ' ')) 648 self.ownsRoot = True 649 self.root.protocol("WM_DELETE_WINDOW", 650 tree.colHeaderButtons[column].invoke) 651 else: 652 self.root = master 653 self.ownsRoot = False 654 655 self.callWhenHelp = """This option menu allows to specify when to call the chained command. 656 The chained command can be called wither when the command's 657 checkbutton in the control panel is: checked, unchecked, or always 658 (i.e. independently of the state of the checkbuton).""" 659 660 self.argHelp = """The option menu allows to specify how to invokd the chained command. 661 Chaining a command is equivalent to simulating clicking on the chained 662 command's checkbutton in the control panel. Here you can control if 663 the chained command checkbutton should be checked, unchecked, have the 664 same or opposite state as the checkbutton of the command it is chained to.""" 665 666 self.chainHelp = """Check the button of command to be called after this command executes. 667 The argument passed to the chained command can be set usign the call 668 with option menu. Only one lvel of chaining will be carried out to 669 avoid endless loops.""" 670 671 self.optionHelp = """This buttons allows displays the options 672 panel of the corresponding Pmv command""" 673 674 self.buildGUI()
675 676
677 - def setChain(self, chainCol, event=None):
678 cmdCol = self.column 679 tree = self.tree 680 if self.chainVars[chainCol].get(): # button is checked 681 chainWhen = self.chainWhenVars[chainCol].get() 682 chainArg = self.chainArgVars[chainCol].get() 683 chainProt = self.chainProtVars[chainCol].get() 684 tree.chainWith[cmdCol][chainCol] = (chainWhen,chainArg,chainProt) 685 else: 686 tree.chainWith[cmdCol][chainCol] = None
687 688
689 - def bbmode_cb(self, tag):
690 # This is called whenever the user clicks on radio button 691 # in a single select RadioSelect widget. 692 self.tree.bbmode[self.column] = tag
693 694
695 - def buildGUI(self):
696 tree = self.tree 697 698 if tree.pmvcmd[self.column]: 699 b = Tkinter.Button(self.root, command=self.getOpt, height=32, 700 width=32, image=tree.iconList[self.column]) 701 b.grid(row=0, column=0, sticky='e') 702 703 tree.optionButton = Tkinter.Button( 704 self.root, text='Set default Options for command', 705 command=self.getOpt) 706 tree.optionButton.grid(row=0, column=1, sticky='w') 707 tree.balloons.bind(tree.optionButton, self.optionHelp) 708 709 self.bbmodevar = Tkinter.StringVar() 710 self.bbmodevar.set("All") 711 bbmode_menu = Pmw.OptionMenu( 712 self.root, labelpos='w', label_text='Protein:', 713 menubutton_textvariable=self.bbmodevar, 714 items=['Backbone', 'Sidechain', 'Sidechains+CA', 'All'], 715 menubutton_width=8, command=self.bbmode_cb, 716 ) 717 bbmode_menu.grid(row=0, column=2, sticky='ew', padx=10, pady=10) 718 tree.balloons.bind(bbmode_menu, tree.proteinHelp) 719 720 self.chainedPanelVar = Tkinter.IntVar() 721 w = Pmw.Group(self.root, 722 tag_pyclass = Tkinter.Checkbutton, 723 tag_text='show command chaining interface', 724 tag_variable=self.chainedPanelVar, 725 tag_foreground='blue') 726 self.chainedPanelVar.set(0) 727 w.toggle() 728 self.chainedPanelGroup = w 729 730 def toggle(event=None): 731 self.chainedPanelGroup.toggle() 732 if self.chainedPanelVar.get(): 733 self.chainedPanelGroup.configure( 734 tag_text='hide command chaining interface', 735 tag_foreground='blue') 736 else: 737 self.chainedPanelGroup.configure( 738 tag_text='show command chaining interface', 739 tag_foreground='blue')
740 741 742 w.configure(tag_command = toggle) 743 # tag_text='Check commands to run after %s:'%\ 744 # tree.names[self.column]) 745 746 w.grid(row=1, column=0, columnspan=3, sticky='ew', padx=5, pady=5) 747 tree.balloons.bind(w, self.chainHelp) 748 749 groupMaster = w.interior() 750 751 # add argument option menu 752 ## self.argVar = Tkinter.StringVar() 753 ## self.argOptMenu = Pmw.OptionMenu( 754 ## groupMaster, labelpos = 'w', 755 ## label_text = 'Argument to be used passed to chained command:', 756 ## menubutton_textvariable = self.argVar, 757 ## items = ['N/A', 'True', 'False', 'Same', 'Opposite'], 758 ## menubutton_width = 10, 759 ## ) 760 ## self.argOptMenu.grid(row=0, column=0, columnspan=4) 761 762 column1 = Tkinter.Frame(groupMaster) 763 column2 = Tkinter.Frame(groupMaster) 764 765 self.chainVars = [] 766 self.chainWhenVars = [] 767 self.chainArgVars = [] 768 self.chainProtVars = [] 769 chainList = tree.chainWith[self.column] 770 771 col = -1 772 halfLength = int(round(tree.nbColumns*0.5)) 773 for i in range(tree.nbColumns): 774 row=(i% halfLength)+1 775 if row==1: 776 col += 4 777 master = Tkinter.Frame(groupMaster, relief='ridge') 778 l = Tkinter.Label(master, text='Cmd:') 779 l.grid(row=0, column=col-3) 780 l = Tkinter.Label( 781 master, text='Call when cmd\nbutton is:') 782 l.grid(row=0, column=col-2) 783 l = Tkinter.Label(master, text='Set chained\ncmd button to:') 784 l.grid(row=0, column=col-1) 785 l = Tkinter.Label(master, text='Apply to:') 786 l.grid(row=0, column=col) 787 master.pack(side='left', padx=10, anchor='n') 788 789 v = Tkinter.IntVar() 790 self.chainVars.append(v) 791 792 whenVar = Tkinter.StringVar() 793 whenVar.set('Checked') 794 self.chainWhenVars.append(whenVar) 795 796 argVar = Tkinter.StringVar() 797 argVar.set('Same') 798 self.chainArgVars.append(argVar) 799 800 protVar = Tkinter.StringVar() 801 protVar.set('All') 802 self.chainProtVars.append(protVar) 803 804 status = 'normal' 805 if i==self.column: 806 status = 'disabled' 807 v.set(0) 808 elif chainList[i]: 809 run, arg, prot = chainList[i] 810 v.set(1) 811 whenVar.set(run) 812 argVar.set(arg) 813 protVar.set(prot) 814 else: 815 v.set(0) 816 817 cb = CallbackFunction(self.setChain, i) 818 if tree.iconList[i]: 819 b = Tkinter.Checkbutton(master, variable=v, state=status, 820 command=cb, height=32, width=32, 821 indicatoron=0, image=tree.iconList[i]) 822 else: 823 l = Tkinter.Label(master, state=status, 824 text=tree.names[i].replace('\n', ' ')) 825 l.grid(column=4*col-3, row=row, sticky='e') 826 b = Tkinter.Checkbutton(master, variable=v, state=status, 827 command=cb) 828 b.grid(column=col-3, row=row, sticky='w') 829 tree.balloons.bind(b, tree.balloonText[i]) 830 831 argMenuCall = Pmw.OptionMenu( 832 master, labelpos = 'w', 833 menubutton_textvariable = whenVar, 834 items = ['Checked', 'Unchecked', 'Always'], 835 menubutton_width = 7, command=cb 836 ) 837 argMenuCall.grid(column=col-2, row=row, sticky='w') 838 tree.balloons.bind(argMenuCall, self.callWhenHelp) 839 840 argMenuVar = Pmw.OptionMenu( 841 master, labelpos = 'w', 842 menubutton_textvariable = argVar, 843 items = ['True', 'False', 'Same', 'Opposite'], 844 menubutton_width = 6, command=cb 845 ) 846 argMenuVar.grid(column=col-1, row=row, sticky='w') 847 tree.balloons.bind(argMenuVar, self.argHelp) 848 849 argMenuVar = Pmw.OptionMenu( 850 master, labelpos = 'w', 851 menubutton_textvariable = protVar, 852 items = ['All', 'Backbone', 'Sidechain+CA', 'Sidechain'], 853 menubutton_width = 10, command=cb 854 ) 855 argMenuVar.grid(column=col, row=row, sticky='w') 856 tree.balloons.bind(argMenuVar, tree.proteinHelp) 857 858 859 if self.editable: 860 pass 861 #cmdList = ['select', 'displayLines', 'displaySticksAndBalls', 862 # 'displayCPK', 'displayExtrudedSS', 'displayMSMS', 863 # 'LabelAtoms', 'LabelResidues', '', ] 864 self.OK = Tkinter.Button(self.root, text='OK', 865 command=tree.colHeaderButtons[self.column].invoke) 866 self.OK.grid(row=2, column=0, columnspan=3, sticky='ew')
867 868
869 - def getOpt(self):
870 # FIXME what is not pmvcmd ?? 871 cmd = self.tree.pmvcmd[self.column] 872 values = cmd.showForm() 873 cmd.lastUsedValues['default'].update(values) 874 return values
875 876
877 - def show(self):
878 if self.ownsRoot: 879 self.root.deiconify()
880
881 - def hide(self):
882 if self.ownsRoot: 883 self.root.withdraw()
884 885 886 from Pmv.mvCommand import MVCommand, MVCommandGUI 887
888 -class ControlPanel(MVCommand):
889 890 """Display a widget showing a tree representation of the molecules in the Viewer and check buttons allowing to carry out command on parts of molecules directly. 891 Certain commands such as coloring or displaying lines, CPK and S&B are implmented as mutually exclusive (i.e. like radio buttons. 892 """ 893
894 - def hide(self):
895 self.vf.GUI.toolbarCheckbuttons['Control_Panel']['Variable'].set(0) 896 self.tree.master.withdraw()
897
898 - def show(self):
899 self.tree.master.deiconify()
900 901
902 - def onAddCmdToViewer(self):
903 if not self.vf.hasGui: return 904 905 self.vf.browseCommands('displayCommands', package='Pmv', log=0) 906 self.vf.browseCommands('secondaryStructureCommands', package='Pmv', 907 log=0) 908 self.hasMSMS = True 909 try: 910 import mslib 911 self.vf.browseCommands('msmsCommands', package='Pmv', log=0) 912 except: 913 self.hasMSMS = False 914 915 self.vf.browseCommands('colorCommands', package='Pmv', log=0) 916 self.vf.browseCommands('labelCommands', package='Pmv', log=0) 917 self.vf.browseCommands('selectionCommands', package='Pmv', log=0) 918 919 # register intrest in other commands 920 self.vf.cmdsWithOnRun[self.vf.showMolecules] = [self] 921 self.vf.cmdsWithOnRun[self.vf.select] = [self] 922 self.vf.cmdsWithOnRun[self.vf.clearSelection] = [self] 923 self.vf.cmdsWithOnRun[self.vf.displayLines] = [self] 924 self.vf.cmdsWithOnRun[self.vf.displaySticksAndBalls] = [self] 925 self.vf.cmdsWithOnRun[self.vf.displayCPK] = [self] 926 self.vf.cmdsWithOnRun[self.vf.displayExtrudedSS] = [self] 927 if self.hasMSMS: 928 self.vf.cmdsWithOnRun[self.vf.displayMSMS] = [self] 929 self.vf.cmdsWithOnRun[self.vf.labelByProperty] = [self] 930 self.vf.cmdsWithOnRun[self.vf.colorByAtomType] = [self] 931 self.vf.cmdsWithOnRun[self.vf.colorByChains] = [self] 932 self.vf.cmdsWithOnRun[self.vf.colorByMolecules] = [self] 933 self.vf.cmdsWithOnRun[self.vf.colorBySecondaryStructure] = [self] 934 self.vf.cmdsWithOnRun[self.vf.colorAtomsUsingDG] = [self] 935 self.vf.cmdsWithOnRun[self.vf.colorByResidueType] = [self] 936 937 from ViewerFramework.VF import DeleteAtomsEvent, AddAtomsEvent 938 self.vf.registerListener(DeleteAtomsEvent, self.handleDeleteEvents) 939 self.vf.registerListener(AddAtomsEvent, self.handleAddEvents) 940 941 # build the tree 942 master = Tkinter.Toplevel() 943 master.withdraw() 944 master.protocol('WM_DELETE_WINDOW',self.hide) 945 self.tree = tvolist = TreeViewWithCheckbuttons( 946 name='Control Panel', 947 displayValue=True, # needed to show buttons 948 multi_choice=True, 949 master = master, 950 offy=0, offx=0) 951 self.tree.sets = self.vf.sets 952 953 self.msmsDefaultValues = {} #cache used to decide if we re-compute 954 # custom call back function for display MSMS column 955 def displayMS_cb(node, column): 956 val = node.chkbtVar[column].get() 957 defaultValues = node.tree.pmvcmd[column].getLastUsedValues() 958 oldDefaultValues = self.msmsDefaultValues 959 recompute = False 960 if len(oldDefaultValues)==0: 961 recompute=True 962 else: 963 for k,v in oldDefaultValues.items(): 964 nv = defaultValues.get(k, None) 965 if nv!=v: 966 recompute=True 967 break 968 969 molat = {} 970 for obj in node.getObjects(column): 971 molecules, atmSets = self.vf.getNodesByMolecule(obj, Atom) 972 for mol, atoms in zip(molecules, atmSets): 973 if molat.has_key(mol): 974 molat[mol] += atoms 975 else: 976 molat[mol] = atoms 977 978 for mol, atoms in molat.items(): 979 if not mol.geomContainer.geoms.has_key('MSMS-MOL'): 980 recompute=True 981 if len(defaultValues): 982 if defaultValues['perMol']==0 and val: 983 recompute=True 984 if type(defaultValues['surfName'])==types.TupleType: 985 defaultValues['surfName']=defaultValues['surfName'][0] 986 987 if recompute: 988 apply( self.vf.computeMSMS, (atoms,), defaultValues) 989 else: 990 pmvcmd = self.vf.displayMSMS 991 defaultValues['callListener'] = False 992 pmvcmd(atoms, negate= not val, callListener=False) 993 if pmvcmd.lastUsedValues['default'].has_key('callListener'): 994 del pmvcmd.lastUsedValues['default']['callListener'] 995 996 if recompute: 997 self.msmsDefaultValues.update(defaultValues)
998 999 1000 def label_cb(node, column, level=Atom, property=['name']): 1001 val = node.chkbtVar[column].get() 1002 for obj in node.getObjects(column): 1003 nodes = obj.findType(level) 1004 pmvcmd = self.vf.labelByProperty 1005 pmvcmd(nodes, negate=not val, callListener=False, 1006 properties=property) 1007 if pmvcmd.lastUsedValues['default'].has_key('callListener'): 1008 del pmvcmd.lastUsedValues['default']['callListener']
1009 # self.labelByProperty("1crn::;", textcolor=(1.0, 1.0, 1.0,), log=0, format=None, only=0, location='Center', negate=0, font='Helvetica12', properties=['name']) 1010 1011 # custom callback for color commands which runs command only if 1012 # button is checked 1013 def color_cb(node, column): 1014 val = node.chkbtVar[column].get() 1015 cmd = node.tree.pmvcmd[column] 1016 if val: 1017 for obj in node.getObjects(column): 1018 apply( cmd, (obj,), {'callListener':False}) 1019 if cmd.lastUsedValues['default'].has_key('callListener'): 1020 del pmvcmd.lastUsedValues['default']['callListener'] 1021 else: 1022 if node.selected: 1023 for node in self.tree.list_selected: 1024 node.chkbtVar[column].set(0) 1025 1026 1027 def showHideVal(node): 1028 if node.name == 'All': 1029 return 1030 mols = [node.objectKey.top] 1031 1032 vis = 0 1033 for m in mols: 1034 n = self.tree.objToNode[m] 1035 v = m.geomContainer.geoms['master'].visible 1036 if v: 1037 vis = 1 1038 n.chkbtVar[0].set(v) 1039 #print node.name, vis hum not sure why the following line does not work 1040 self.tree.roots[0].chkbtVar[0].set(vis) 1041 1042 col = 0 1043 tvolist.setCallback(col, self.vf.showMolecules, 'eye.png', 1044 'show/Hide molecule', bValFunc=showHideVal) 1045 col += 1 1046 tvolist.setCallback(col, self.vf.select,'select.png', 1047 'select molecular fragment') 1048 1049 col += 1 1050 tvolist.setCallback(col, self.vf.displayLines, 'lines.png', 1051 'Display/Undisplay Lines representation', 1052 'Display Lines') 1053 1054 col += 1 1055 tvolist.setCallback(col, self.vf.displaySticksAndBalls, 'sb.png', 1056 'Display/Undisplay atoms as small spheres and bonds as cylinders', 1057 'Display Sticks and Balls') 1058 1059 col += 1 1060 tvolist.setCallback(col, self.vf.displayCPK, 'cpk.png', 1061 'Display/Undisplay atoms as spheres', 1062 'Display CPK') 1063 1064 col += 1 1065 tvolist.setCallback(col, self.vf.displayExtrudedSS, 'ss.png', 1066 'Display/Undisplay secondary structure', 1067 'Display Ribbon', ) 1068 if self.hasMSMS: 1069 col += 1 1070 tvolist.setCallback(col, self.vf.computeMSMS, 'ms.png', 1071 'Compute and display molecular surface', 1072 'Display molecular surface', 1073 displayMS_cb) 1074 1075 col += 1 1076 cb = CallbackFunction( label_cb, level=Atom) 1077 tvolist.setCallback(col, None, 'labelAtom.png', 'Label atoms', 1078 'Label Atoms', cb) 1079 1080 col += 1 1081 cb = CallbackFunction( label_cb, level=Residue) 1082 tvolist.setCallback(col, None, 'labelRes.png', 'label Residues', 1083 'Label Residues', cb) 1084 1085 col += 1 1086 tvolist.setCallback(col, self.vf.colorByAtomType, 'colorAtom.png', 1087 'Color all representations by atoms type', 1088 'Color by Atom Type', color_cb) 1089 col += 1 1090 tvolist.setCallback(col, self.vf.colorByChains, 'colorChain.png', 1091 'Color all representations by chain', 1092 'Color by Chain', color_cb) 1093 col += 1 1094 tvolist.setCallback(col, self.vf.colorByMolecules, 'colorMol.png', 1095 'Color all representations by molecule', 1096 'Color by Molecule', color_cb) 1097 col += 1 1098 tvolist.setCallback(col, self.vf.colorBySecondaryStructure, 1099 'colorSS.png', 1100 'Color all representations by secondary structure element type', 1101 'Color by Secondary Structure', color_cb) 1102 col += 1 1103 tvolist.setCallback(col, self.vf.colorAtomsUsingDG, 'colorDG.png', 1104 'Color using the David Goodsell scheme\nin which atoms are either neutral,\nor slightly or strongly charged', 'Color by D. Goodsell', color_cb) 1105 1106 col += 1 1107 tvolist.setCallback(col, self.vf.colorByResidueType, 'colorRes.png', 1108 'Color all representations by residue\nusing the RASMOL coloring scheme', 1109 'color Residues by Rasmol', color_cb) 1110 #col += 1 1111 #tvolist.setCallback(col, self.vf.colorResiduesUsingShapely, .... 1112 # 'color\nby\nShapely') 1113 1114 # add current molecules 1115 n = NodeWithCheckbuttons 1116 hasChildren = len(self.vf.Mols)>0 1117 node = tvolist.addNode('All', parent=None, object=self.vf.Mols, 1118 nodeClass=n, hasChildren=hasChildren) 1119 node.createTkVar(self.tree.nbColumns) 1120 #if len(self.vf.Mols): 1121 # self.addMolecules(self.vf.Mols) 1122 1123
1124 - def addMolecules(self, mols):
1125 tvolist = self.tree 1126 n = NodeWithCheckbuttons 1127 hasChildren = [True]*len(mols) 1128 nodes = tvolist.addNodeSet(parent='All', 1129 name=[m.name for m in mols], object=list(mols), 1130 nodeClass=n, hasChildren=hasChildren) 1131 1132 for node in nodes: 1133 node.createTkVar(self.tree.nbColumns) 1134 1135 for m in mols: 1136 chains = m.chains 1137 hasChildren = [True]*len(chains) 1138 nodes = tvolist.addNodeSet( 1139 parent=m, name=chains.name, object=list(chains), 1140 nodeClass=n, hasChildren=hasChildren) 1141 for node in nodes: 1142 node.createTkVar(self.tree.nbColumns) 1143 for c in chains: 1144 residues = c.residues 1145 hasChildren = [True]*len(residues) 1146 nodes = tvolist.addNodeSet( 1147 parent=c, name=residues.name, object=list(residues), 1148 nodeClass=n, hasChildren=hasChildren) 1149 for node in nodes: 1150 node.createTkVar(self.tree.nbColumns) 1151 for r in residues: 1152 atoms = r.atoms 1153 hasChildren = [False]*len(atoms) 1154 nodes = tvolist.addNodeSet( 1155 parent=r, name=atoms.name, object=list(atoms), 1156 nodeClass=n, hasChildren=hasChildren) 1157 for node in nodes: 1158 node.createTkVar(self.tree.nbColumns)
1159 1160
1161 - def guiCallback(self):
1162 if self.vf.GUI.toolbarCheckbuttons['Control_Panel']['Variable'].get(): 1163 self.show() 1164 else: 1165 self.hide()
1166 1167
1168 - def onAddObjectToViewer(self, obj):
1169 """ 1170 Add the new molecule to the tree 1171 """ 1172 self.addMolecules([obj])
1173 1174
1175 - def onRemoveObjectFromViewer(self, obj):
1176 node = self.tree.objToNode.get(obj, None) 1177 if node: 1178 self.tree.deleteNode(node) 1179 1180
1181 - def handleDeleteEvents(self, event):
1182 """Function to update tree when molecualr fragments are delete. 1183 """ 1184 # split event.objects into atoms sets per molecule 1185 tree = self.tree 1186 for obj in event.objects: 1187 node = tree.objToNode[obj] 1188 tree.deleteNode(node)
1189
1190 - def handleAddEvents(self, event):
1191 """Function to update tree when molecualr fragments are added. 1192 """ 1193 # split event.objects into atoms sets per molecule 1194 tree = self.tree 1195 for obj in event.objects: 1196 self.tree.addNodeTree(obj)
1197 1198
1199 - def onCmdRun(self, command, *args, **kw):
1200 if command==self.vf.showMolecules: 1201 column=0 1202 return 1203 elif command==self.vf.clearSelection: 1204 node = self.tree.roots[0] 1205 node.chkbtVar[1].set(0) 1206 node.tree.manageChildren(node, 1) 1207 return 1208 elif command==self.vf.select: 1209 column = 1 1210 cmdType='useNegate' 1211 elif command==self.vf.displayLines: 1212 column = 2 1213 cmdType='useNegate' 1214 elif command==self.vf.displaySticksAndBalls: 1215 column = 3 1216 cmdType='useNegate' 1217 elif command==self.vf.displayCPK: 1218 column = 4 1219 cmdType='useNegate' 1220 elif command==self.vf.displayExtrudedSS: 1221 column = 5 1222 cmdType='useNegate' 1223 elif command==self.vf.displayMSMS: 1224 column = 6 1225 cmdType='useNegate' 1226 elif command==self.vf.labelByProperty: 1227 column = 7 1228 return 1229 elif command==self.vf.colorByAtomType: 1230 column = 9 1231 cmdType='radioGroupColor' 1232 elif command==self.vf.colorByChains: 1233 column = 10 1234 cmdType='radioGroupColor' 1235 elif command==self.vf.colorByMolecules: 1236 column = 11 1237 cmdType='radioGroupColor' 1238 elif command==self.vf.colorBySecondaryStructure: 1239 column = 12 1240 cmdType='radioGroupColor' 1241 elif command==self.vf.colorAtomsUsingDG: 1242 column = 13 1243 cmdType='radioGroupColor' 1244 elif command==self.vf.colorByResidueType: 1245 column = 14 1246 cmdType='radioGroupColor' 1247 1248 molFrag = args[0] 1249 if molFrag==self.vf.Mols: 1250 node=self.tree.roots[0] 1251 if cmdType=='useNegate': 1252 negate = not kw['negate'] 1253 node.chkbtVar[column].set(negate) 1254 node.tree.manageChildren(node, column) 1255 elif cmdType=='radioGroupColor': 1256 node.chkbtVar[column].set(1) 1257 node.tree.manageChildren(node, column) 1258 for col in range(9,15): 1259 if col==column: continue 1260 node.chkbtVar[col].set(0) 1261 node.tree.manageChildren(node, col) 1262 else: 1263 for o in molFrag: 1264 if self.tree.objToNode.has_key(o): 1265 node = self.tree.objToNode[o] 1266 else: 1267 return 1268 if cmdType=='useNegate': 1269 negate = not kw['negate'] 1270 node.chkbtVar[column].set(negate) 1271 node.tree.manageChildren(node, column) 1272 elif cmdType=='radioGroupColor': 1273 node.chkbtVar[column].set(1) 1274 #print 'value', node.chkbtVar[column].get() 1275 node.tree.manageChildren(node, column) 1276 for col in range(9,15): 1277 if col==column: continue 1278 node.chkbtVar[col].set(0) 1279 node.tree.manageChildren(node, col)
1280 1281 1282 ControlPanel_GUI = MVCommandGUI() 1283 from moleculeViewer import ICONPATH 1284 ControlPanel_GUI.addToolBar('Control_Panel', icon1 = 'view_tree.gif', 1285 icon_dir=ICONPATH, balloonhelp='Control Panel Widget', index=8) 1286 1287 commandList = [ 1288 {'name':'controlPanel', 'cmd':ControlPanel(), 'gui':ControlPanel_GUI} 1289 ] 1290
1291 -def initModule(viewer):
1292 for dict in commandList: 1293 viewer.addCommand(dict['cmd'], dict['name'], dict['gui'])
1294