Scripts/MMTK: NormalModesTable.py

File NormalModesTable.py, 4.6 KB (added by Conrad Huang, 16 years ago)

Code to display MMTK normal mode results as Chimera trajectory

Line 
1from chimera.baseDialog import ModelessDialog
2
3# This class is only needed for the GUI. The SortableTable
4# API is much simpler if column values are attributes (like
5# "frequency", "index" and "active")
6class _NormalModeProxy:
7
8 def __init__(self, modes, index, active):
9 self.modes = modes
10 self.index = index
11 self.frequency = str(self.mode().frequency)
12 self.active = active
13
14 def mode(self):
15 return self.modes[self.index]
16
17# This class is needed to interface with MovieDialog
18class NormalModeTraj:
19 def __len__(self):
20 return len(self.molecule.coordSets)
21 def __getitem__(self, key):
22 return None
23
24class NormalModesTableDialog(ModelessDialog):
25 """Display MMTK normal modes by reconstructing a Chimera
26 (trajectory) model from the MMTK universe and one particular
27 normal mode."""
28
29 buttons = ( "Show", "Quit" )
30 title = "Normal Modes"
31 oneshot = True
32
33 def __init__(self, modes, *args, **kw):
34 # We can override window title with
35 # self.title = XXX
36 # if we only knew what XXX should be.
37 # Create proxy normal mode objects to simplify use
38 # with SortableTable.
39 self.modeData = [ _NormalModeProxy(modes, i, False)
40 for i in range(len(modes)) ]
41 # Save modes. Currently we only use it to get
42 # at the universe.
43 self.modes = modes
44 ModelessDialog.__init__(self, *args, **kw)
45 self._createMovieDialog()
46
47 def fillInUI(self, parent):
48 from CGLtk.Table import SortableTable
49 import Pmw
50 # scaleWidget allows user to scale the displacement
51 # vector for display purposes.
52 self.scaleWidget = Pmw.Counter(parent,
53 labelpos = 'w',
54 label_text = 'Scale factor:',
55 label_justify = 'right',
56 entryfield_value = '1.0',
57 datatype = {'counter':'real'},
58 entryfield_validate = {'validator':'real',
59 'min' : '1.0', 'max' : '100.0'},
60 increment=1.0)
61 self.scaleWidget.pack(expand=False, fill="x")
62 t = SortableTable(parent)
63 t.pack(expand=True, fill="both")
64 # addColumn arguments are: (column title, attribute associated
65 # with column, layout format). format=None means use default
66 # display string. format=bool means display using checkbutton
67 # that can be used to change the value of the variable.
68 t.addColumn("Mode", "index", format="%d")
69 t.addColumn("Frequency", "frequency", format=None)
70 t.addColumn("Active", "active", format=bool)
71 t.setData(self.modeData)
72 t.launch()
73 self.modesTable = t
74
75 def Show(self):
76 from chimera import UserError
77 # For now, only allow one mode to be selected
78 which = None
79 for nmp in self.modeData:
80 if not nmp.active:
81 continue
82 if which is not None:
83 raise UserError("Only one mode may be selected")
84 else:
85 which = nmp
86 if which is None:
87 raise UserError("Please select normal mode to show.")
88 sf = float(self.scaleWidget.get())
89 self._updateTrajectory(which, sf)
90
91 def Quit(self):
92 if self.movieDialog:
93 self.movieDialog.Quit()
94 self.movieDialog = None
95 self.Close()
96
97 def _createMovieDialog(self):
98 # First create the reference molecule
99 from MMTK2Molecule import convert
100 m, self._mmtk2chimera = convert(self.modes.universe,
101 defaultCS=1)
102
103 # Next we need to add the extra coordinate sets showing
104 # normal mode extreme positions. The trajectory consists
105 # of 4 coordinate sets. CS 1 was created above and is
106 # the reference coordinate set. CS 2 and 4 are the two
107 # extreme positions while CS 3 is another reference.
108 # When played back, you can see the periodic motion.
109 # Initially, we set CS 2 and CS 4 to be reference also.
110 cs2 = m.newCoordSet(2) # + displacement
111 cs3 = m.newCoordSet(3) # reference
112 cs4 = m.newCoordSet(4) # - displacement
113 for a in m.atoms:
114 p = a.coord()
115 a.setCoord(p, cs2)
116 a.setCoord(p, cs3)
117 a.setCoord(p, cs4)
118
119 # Finally we need to create the MovieDialog for the
120 # trajectory we just made
121 ensemble = NormalModeTraj()
122 ensemble.name = "Normal mode"
123 keys = m.coordSets.keys()
124 ensemble.startFrame = min(keys)
125 ensemble.endFrame = max(keys)
126 ensemble.molecule = m
127 from Movie.gui import MovieDialog
128 self.movieDialog = MovieDialog(ensemble)
129
130 def _updateTrajectory(self, nmp, sf):
131 # Assume that the trajectory has been created and has
132 # exactly 4 coordinate sets. See above. We update
133 # only the two non-reference coordinate sets.
134 from chimera import Vector
135 mode = nmp.mode()
136 m = self.movieDialog.ensemble.molecule
137 cs1 = m.findCoordSet(1) # reference
138 cs2 = m.findCoordSet(2) # + displacement
139 cs4 = m.findCoordSet(4) # - displacement
140 for ma in self.modes.universe.atomList():
141 x, y, z = mode[ma] * 10 * sf
142 v = Vector(x, y, z)
143 a = self._mmtk2chimera[ma]
144 p = a.coord(cs1)
145 a.setCoord(p + v, cs2)
146 a.setCoord(p - v, cs4)