1
2
3
4
5
6
7
8
9
10
11
12
13
14 import types, Numeric, warnings
15
16 from ViewerFramework.VFCommand import Command, CommandGUI, \
17 InteractiveCmdCaller, ActiveObject
18 from MolKit.tree import TreeNode, TreeNodeSet
19 from MolKit.molecule import Molecule, Atom, BondSet
20 from MolKit.protein import Protein, Chain, Residue
21 from mglutil.util.uniq import uniq3
22
23
24 from mglutil.gui.InputForm.Tk.gui import InputFormDescr
25 import Pmw, Tkinter, string
26 from MolKit.molecule import AtomSet, Atom
27
29 """subclass InteractiveCmdCaller to add the concept of selection level
30 This object holds a dictionary of commands and a level. Both are active
31 objects.
32 """
33
35 apply( InteractiveCmdCaller.__init__, (self, vf) )
36 self.level = ActiveObject(level)
37 self.levelColors = {
38 'Atom':(1.,1.,0.),
39 'Residue':(0.,1.,0.),
40 'Chain':(0.,1.,1.),
41 'Molecule':(1.,0.,0.),
42 'Protein':(1.,0.,0.),
43 }
44 if self.vf.hasGui:
45 for c in self.vf.GUI.VIEWER.cameras:
46 c.selectionColor = self.levelColors[level.__name__]
47
48
50
51 if len(objects)==0: return
52 if len(self.commands.value)==0: return
53
54
55 if isinstance(objects, TreeNodeSet) and \
56 not isinstance(objects, BondSet) :
57
58 typ = self.level.value
59 if typ in [Molecule, Protein]:
60 nodes = objects.findType(Molecule)
61 if nodes is None:
62 nodes = objects.findType(Protein)
63 else:
64 nodes = objects.findType(typ)
65 nodes = nodes.uniq()
66
67
68
69 pickedInstances = {}
70 for n in nodes:
71 pickedInstances[n] = []
72 if self.level.value != Atom:
73 for atom in objects:
74 p = atom.parent
75 while p and not isinstance(p,self.level.value):
76 p = p.parent
77 if p:
78 pickedInstances[n].extend( atom.pickedInstances )
79
80 for n in nodes:
81 n.pickedInstances = pickedInstances[n]
82
83 else:
84 nodes = objects
85 if nodes:
86 InteractiveCmdCaller.execCmd(self, nodes)
87
88
89 - def setLevel(self, Klass, KlassSet=None):
90 if Klass==self.vf.ICmdCaller.level.value:
91 return self.vf.ICmdCaller.level.value
92 self.vf.ICmdCaller.level.Set(Klass)
93 if self.vf.hasGui:
94 for c in self.vf.GUI.VIEWER.cameras:
95 c.selectionColor = self.levelColors[Klass.__name__]
96
97
98 from ViewerFramework.VFCommand import ICOM
99
101 """Specialize ViewerFramework.VFCommand.ICOM class for a molecular viewer
102 """
103 pass
104
105
106
115
116
117
125
126
127
129 """
130 Base class for command objects for a molecule viewer.
131
132 Base class for command objects for a molecule viewer
133 This command class derives from the ViewerFramework.VFCommand.Command class.
134 Classes derived from that class can be added to a MoleculeViewer using the
135 addCommand method.
136 To write a command the programmer has to create a class MyCmd derived from the
137 MVCommand class and should overwrite the following methods when relevant.
138
139 Information on how to write an inputForm is also available at
140 http://www.scripps.edu/~sanner/software/python/inputform/tableOfContent.html
141
142 __init__(self, func=None)
143 The constructor has to be overwritten to set the self.flag attribute
144 properly.
145 self.objArgOnly is turned on when the doit only has one required
146 argument which is the current selection.
147 This will automatically make this command a Picking command
148 self.negateKw is turned on when the doit method takes a boolean
149 flag negate which makes this command undoable.
150
151 see Pmv.colorCommands.color command class for an example
152
153 __call__(self, *args, **kw):
154 Entrypoint to the command from the command line. It overloads
155 calling the object (command) as a function, which enables calling
156 an instance of a command as a method of a viewer once it it has
157 been loaded.
158
159 Typically, this method checks the arguments supplied and calls the
160 doitWrapper method with the arguments supplied.
161
162 This method needs to have the same signature than the doit.
163 A documentation string supplying the following information will be
164 displayed in a tooltip when calling the command from the python
165 idle shell.
166 This documentation string should provide the synopsis of the
167 command and a description of the arguments.
168
169 See Pmv.colorCommands.py, Pmv.displayCommands.py for good example
170 of documentation string.
171
172 guiCallback(self, event=None, *args, **kw):
173 This method is bound by default as the callback of the GUI item
174 associated with the command.
175 It is the entrypoint of the command through the GUI.
176
177 It typically creates an inputform allowing the user to specify any
178 argument required for the command.
179 The command method showForm is typically called with the name of
180 the form to be created and a bunch of optional argument described
181 later.
182 Once all the parameters are known, the doitWrapper method is
183 called to carry out the command.
184
185 The old behavior was to call self.vf.getUserInput. The showForm method
186 is a command method and replace the getUserInput method of the
187 ViewerFramework. Although some commands still implement the
188 getUserInput mechanism.
189
190
191 buildFormDescr(self, formName):
192 This method typically creates the inputform descriptor used by the
193 guiCallback method to get user input for the command.
194 The formName is a string which will be used as a key in the
195 cmdForms dictionary to store the form information.
196 This method is called by self.showForm
197 This method returns an instance of an InputFormDescr which is the
198 object describing a inputform in ViewerFramework.
199 More information can be found in mglutil/gui/InputForm/Tk/gui.py
200
201 For an example see:
202 Pmv.secondaryStructureCommands.py module
203 Almost all the PMV command implement an inputform.
204
205 doit(self, *args, **kw):
206 This method does the actual work. It should not implement any
207 functionality that should be available outside the application
208 for scripting purposes. This method should call such functions or
209 object.
210
211 setUpUndo(self, *args, **kw):
212 Typically this method should be implemented if the command is
213 undoable. It should have the same signature than the doit and the
214 __call__ method.
215
216 See displayCommands and colorCommands for a good example
217
218 Of course, one is free to overwrite any of these methods and for instance
219 rename doit using a more appropriate name. But, when doing so,
220 the programmer has to overwrite both __call__ and guiCallback to call
221 the right method to carry out the work.
222
223 Besides these methods which are required to be implemented in a command there
224 are the following optional methods:
225 strArg(self, arg):
226 Method to turn a command argument into a string. Used by the log
227 method to generate a log string for the command.
228 This method can be overwritten or extended by classes subclassing
229 Command in order to handle properly instances of objects defined
230 by the application when not handled properly by the base class.
231 This method has been overwritten to be 'Molecule' aware.
232 If you need to handle an argument a particular way this method should
233 be overwritten.
234
235 checkDependencies():
236 This method called when command is loaded. It typically checks for dependencies and
237 if all the dependencies are not found the command won't be loaded.
238 See msmsCommands for an example
239
240 onAddCmdToViewer():
241 (previously initCommand)
242 onAddCmdToViewer is called once when a command is loaded into a
243 Viewer.
244 Typically, this method :
245 takes care of dependencies (i.e. load other commands that
246 might be required)
247 creates/initializes variables.
248 see Pmv.traceCommands.py, Pmv.secondaryStructureCommands.py etc...
249
250 customizeGUI(self):
251 (obsolete)
252 method allowing to modify the GUI associated with a command
253
254 onAddObjectToViewer(self, obj):
255 (previously named initGeom)
256 When a command is loaded that implements an onAddObjectToViewer
257 function,this function id called for every object present in the
258 application.
259 Once the command is loaded, this function will be called for every
260 new object added to the application.
261 In general, onAddObjectToViewer is used by a command to add a new
262 geometry to the geometry container of an object.
263 It is also where picking events and building arrays of colors specific
264 to each geometry can be registered.
265
266 See secondaryStructureCommands, msmsCommadns etc.. for an example
267
268 onRemoveObjectFromViewer(self, obj):
269 In python if all references to an object are not deleted, memory
270 space occupied by that object even after its deletion is not freed.
271
272 This function will be called for every object removed (deleted) from
273 the application.
274 When references to an object are created inside a command which are
275 not related to the geomContainer of the object, in order to prevent
276 memory leak this command has to implement an onRemoveObjectFromViewer
277 to delete those references.
278 See secondaryStructureCommands for an example
279
280
281 The following methods are helper methods.
282 log(self, *args, **kw):
283 Method to log a command. args provides a list of positional
284 arguments and kw is a dictionary of named arguments. This method
285 loops over both args and kw and builds a string representation of
286 their values. When a class is passed as one the arguments, an
287 additional command is logged to load that class.
288 This method also sets the command's lastCmdLog member.
289
290 showForm(self, *args, **kw):
291
292 If the inputForm object associated with the given formName already
293 exists than it just deiconify the forms unless the force argument is
294 set to true.
295 Otherwise, showForm calls buildFormDescr with the given
296 formName, then it creates an instance of InputForm with the given
297 parameters and stores this object in the cmdForms dictionary, where
298 the key is the formName and the value the InputForm object.
299 (see the showForm documentation string for the description of the
300 arguments)
301 showForm will return the dictionary containing the values of the
302 widgets if modal or blocking other it will return the form object.
303
304 If a command generates geometries to be displayed in the camera, it is
305 expected to create the geometry objects and add them to the appropriate
306 GeometryContainer.
307
308 A Python module implementing commands should implement the following at the
309 end of the file so the commands are loadable in the application using
310 the browseCommands command.
311
312 commandList -- which is a list of dictionaries containing the following:
313 'name': command name (string) used as an alias to invoke
314 the command from the commandline.
315 'cmd' : Command Class
316 'gui' : Typically is a commandGUI object but can be None
317 if no GUI is associated with the Command
318
319 An initModule method
320 def initModule(viewer):
321 for dict in commandList:
322 viewer.addCommand(dict['cmd'], dict['name'], dict['gui'])
323
324 This method will be used by the browseCommands command to load the
325 given module into the application but also to determine which
326 modules implement commands.
327 """
329 self.nodeLogString = None
330 Command.__init__(self, func)
331
332
333 - def guiCallback(self,event=None, log=None, redraw=None):
334 kw = {}
335 if log!=None: kw['log']=log
336 if redraw!=None: kw['redraw']=redraw
337 args, kw = apply( self.getArguments, (), kw)
338 if not kw.has_key('redraw'): kw['redraw']=1
339 if not kw.has_key('log'): kw['log']=1
340 if event:
341 return apply( self.doitWrapper, (event,)+args, kw )
342 else:
343 return apply( self.doitWrapper, args, kw )
344
346 """
347 Method to turn a command argument into a string for logging purposes
348 Add support for TreeNodes and TreeNodeSets
349 """
350 if type(arg)==types.InstanceType:
351 if issubclass(arg.__class__, TreeNode):
352 return '"' + arg.full_name() + '", ', None
353
354 if issubclass(arg.__class__, TreeNodeSet):
355 stringRepr = arg.getStringRepr()
356 if stringRepr:
357 return '"' + stringRepr + '", ', None
358 else:
359 name = ""
360 mols, elems = self.vf.getNodesByMolecule(arg)
361 for elem in elems:
362 name = name + elem.full_name() +';'
363 return '"' + name + '", ', None
364
365 return Command._strArg(self, arg)
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
427 """Base class for GUI objects associated with MVCommands.
428 """
429 pass
430
431
432
434 """Print the name of molecular fragments. The name is a colon separated
435 list of string mol:chain:residue:atom. If the geoemtry depicting the fragments
436 have multiple instances a list of (geom,instance) tuples is printed after the
437 moelcular fragment name.
438 """
439
444
445
446 - def doit(self, nodes):
447 nodes = self.vf.expandNodes(nodes)
448 for n in nodes:
449 msgBase = n.full_name()
450 for instance in n.pickedInstances:
451 if max(instance[1])>0:
452 geomNames = instance[0].fullName.split('|')
453 msg = msgBase+' instance: ' + \
454 str(zip(geomNames,instance[1]))
455 self.vf.message( msg )
456 else:
457 self.vf.message( msgBase )
458
459
460 - def __call__(self, nodes, topCommand=0, **kw):
461
462 if type(nodes) is types.StringType:
463 self.nodeLogString = "'"+nodes+"'"
464
465 if not kw.has_key('topCommand'): kw['topCommand'] = topCommand
466 apply( self.doitWrapper, (nodes,), kw )
467
468
469
471 """Set the pivot point (i.e. center of rotation) of the entire scene to
472 the geometric center of the picked atoms. this command is aware of instance
473 matrices.
474 """
479
480
481 - def doit(self, nodes):
482 if len(nodes)==0:
483 return 'ERROR'
484 vt = self.vf.transformedCoordinatesWithInstances(nodes)
485 g = [0,0,0]
486 i = 0
487 for v in vt:
488 g[0] += v[0]
489 g[1] += v[1]
490 g[2] += v[2]
491 i+=1
492 g[0] = g[0]/i
493 g[1] = g[1]/i
494 g[2] = g[2]/i
495
496 vi = self.vf.GUI.VIEWER
497 root = vi.rootObject
498 self.vf.centerGeom( root, g, topCommand=0, log=1, setupUndo=1)
499
500
502
503 kw['topCommand']=0
504 kw['busyIdle']=1
505 if type(nodes) is types.StringType:
506 self.nodeLogString = "'"+nodes+"'"
507 apply( self.doitWrapper, (nodes,), kw )
508
509
510 from DejaVu.colorTool import TkColor
511
513
515 if not (self.vf.ICmdCaller.level.value in [Molecule, Protein] and \
516 Klass in [Molecule, Protein]) and \
517 self.vf.ICmdCaller.level.value != Klass:
518 self.addUndoCall( (self.vf.ICmdCaller.level.value,),
519 {'KlassSet':None}, self.name )
520
521
522 - def doit(self, Klass, KlassSet=None):
523
524 if Klass is Protein: Klass = Molecule
525 self.vf.ICmdCaller.setLevel(Klass, KlassSet=None)
526 if hasattr(self.vf,'ICOMbar'):
527 col =self.vf.ICmdCaller.levelColors[Klass.__name__]
528 c = (col[0]/1.5,col[1]/1.5,col[2]/1.5)
529 self.vf.ICOMbar.LevelOption.setvalue(Klass.__name__)
530 self.vf.ICOMbar.LevelOption._menubutton.configure(
531 background = TkColor(c),
532 activebackground = TkColor(col))
533
534
535
536
537 - def __call__(self, Klass, KlassSet=None, **kw):
538 """None <- setIcomLevel(Klass, KlassSet=None, **kw)
539 set the current IcomLevel level
540 """
541 kw['KlassSet'] = KlassSet
542 apply( self.doitWrapper, (Klass,), kw)
543
544