Package nMOLDYN :: Package GUI :: Module ViewEffectiveModeDialog |
[hide private]
[frames] | no frames]

# Source Code for Module nMOLDYN.GUI.ViewEffectiveModeDialog

      1  """This modules implements I{View --> Effective Mode} dialog.
      2
      3  Classes:
      4      * ViewEffectiveModeDialog: creates I{View --> Effective Mode} dialog used to
      5        view an animation of the effective modes resulting from a Quasi Harmonic Analysis.
      6  """
      7
      8  # The python distribution modules
      9  import copy
     10  import os
     11  import sys
     12
     13  # The Tcl/Tk modules
     14  from tkFileDialog import askopenfilename
     15  from Tkinter import *
     16
     17  # The ScientificPython modules
     18  from Scientific import N as Num
     19  from Scientific.IO.NetCDF import NetCDFFile
     20
     21  # The MMTK distribution modules
     22  from MMTK import Configuration
     23  from MMTK import Skeleton
     24  from MMTK.ParticleProperties import ParticleVector
     25  from MMTK.Visualization import definePDBViewer, viewSequenceVMD
     26
     27  # The nMOLDYN modules
     28  from nMOLDYN.Core.Error import Error
     29  from nMOLDYN.Core.Logger import LogMessage
     30  from nMOLDYN.Core.Preferences import PREFERENCES
     31  from nMOLDYN.GUI.Widgets import ComboIntegerEntry, ComboFloatEntry, ComboFileBrowser, ComboListbox, ComboRadiobutton
     32
     33 -class ViewEffectiveModeDialog(Toplevel):
     34      """Sets up a dialog used to visualize the effective modes resulting from a QHA analysis.
     35      """
     36
     37 -    def __init__(self, parent, title = None):
     38          """The constructor.
     39
     40          @param parent: the parent widget.
     41
     42          @param title: a string specifying the title of the dialog.
     43          @type title: string
     44          """
     45
     46          Toplevel.__init__(self, parent)
     47          self.transient(parent)
     48
     49          if title:
     50              self.title(title)
     51
     52          self.parent = parent
     53
     54          body = Frame(self)
     55          self.initial_focus = self.body(body)
     56          body.grid(row = 0, column = 0, sticky = EW)
     57
     58          self.buttonbox()
     59
     60          self.grab_set()
     61
     62          if not self.initial_focus:
     63              self.initial_focus = self
     64
     65          self.protocol("WM_DELETE_WINDOW", self.cancel)
     66
     67          self.resizable(width = NO, height = NO)
     68
     69          self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))
     70
     71          self.initial_focus.focus_set()
     72
     73          self.wait_window(self)
     74
     75 -    def body(self, master):
     76          """
     77          Create dialog body. Return widget that should have initial focus.
     78          """
     79
     80          settingsFrame = LabelFrame(master, text = 'Settings', bd = 2, relief = GROOVE)
     81          settingsFrame.grid(row = 0, column = 0, sticky = EW, padx = 3, pady = 3)
     82          settingsFrame.grid_columnconfigure(0, weight = 1)
     83
     84          # The combo widget for the file browser.
     85          self.fileBrowser = ComboFileBrowser(settingsFrame,\
     86                                              frameLabel = "QHA input file",\
     87                                              tagName = 'view_effective_modes_qha_input_file',\
     88                                              contents = '',\
     89                                              save = False,\
     90                                              command = self.openNetCDFFile,\
     91                                              filetypes = [("NetCDF file", ".nc"),])
     92          self.fileBrowser.grid(row = 0, column = 0, sticky = EW, padx = 2, pady = 2)
     93          self.fileBrowser.grid_columnconfigure(0, weight = 1)
     94          self.fileBrowser.entry.bind('<Return>', self.openNetCDFFile)
     95
     96          # The combo listbox that will contain the X variables.
     97          self.selectedModeLb = ComboListbox(settingsFrame,\
     98                                             frameLabel = 'Quasi-Harmonic mode',\
     99                                             tagName = 'quasi_harmonic_mode',\
    100                                             contents = [])
    101          self.selectedModeLb.lb.config({'exportselection' : 0, 'width' : 22, 'height' : 8, 'selectmode' : MULTIPLE})
    102          self.selectedModeLb.grid(row = 1, column = 0, sticky = EW, padx = 2, pady = 2)
    103          self.selectedModeLb.grid_columnconfigure(0, weight = 1)
    104
    105          # The combo widget to set the number of frames for the animation.
    106          self.nFramesEntry = ComboIntegerEntry(settingsFrame,\
    107                                                frameLabel = 'Number of frames',\
    108                                                tagName = 'view_effective_modes_number_of_frames')
    109          self.nFramesEntry.grid(row = 2, column = 0, sticky = EW, padx = 2, pady = 2)
    110          self.nFramesEntry.grid_columnconfigure(0, weight = 1)
    111
    112          # The combo widget to set the amplitude of the effective mode to view.
    113          self.amplitudeEntry = ComboFloatEntry(settingsFrame,\
    114                                                frameLabel = 'Amplitude (in nm)',\
    115                                                tagName = 'view_effective_modes_amplitude')
    116          self.amplitudeEntry.grid(row = 3, column = 0, sticky = EW, padx = 2, pady = 2)
    117          self.amplitudeEntry.grid_columnconfigure(0, weight = 1)
    118
    119          return None
    120
    121 -    def buttonbox(self):
    122          """
    123          Add standard button box.
    124          """
    125
    126          # The frame that contains the 'Cancel' and 'OK' buttons.
    127          box = LabelFrame(self, text = 'Actions', bd = 2, relief = GROOVE)
    128          box.grid(row = 1, column = 0, sticky = EW, padx = 3, pady = 3)
    129          box.grid_columnconfigure(0, weight = 1)
    130
    131          w = Button(box, text = "Cancel", width=10, command = self.cancel)
    132          w.grid(row = 0, column = 0, sticky = E)
    133          w = Button(box, text = "OK", width=10, command = self.ok, default=ACTIVE)
    134          w.grid(row = 0, column = 1, sticky = E)
    135
    136          self.bind("<Return>", self.ok)
    137          self.bind("<Escape>", self.cancel)
    138
    139      # Standard button semantics.
    140 -    def ok(self, event = None):
    141
    142          if not self.validate():
    143              self.initial_focus.focus_set()
    144              return
    145
    146          self.update_idletasks()
    147
    148          self.apply()
    149
    150 -    def cancel(self, event=None):
    151
    152          # Put focus back to the parent window
    153          self.parent.focus_set()
    154          self.destroy()
    155
    156      # Command hooks
    157 -    def validate(self):
    158
    159          try:
    160
    161              if not self.selectedModeLb.lb.curselection():
    162                  LogMessage('warning','Please select a vibration mode.',['gui'])
    163                  raise
    164
    165              self.selectedMode = [int(v) - 1 for v in self.selectedModeLb.lb.curselection()]
    166
    167              self.amplitude = self.amplitudeEntry.getValue()
    168              self.nFrames = self.nFramesEntry.getValue()
    169
    170              if self.amplitude <= 0.0:
    171                  raise
    172
    173              if self.nFrames < 0:
    174                  raise
    175
    176          except:
    177              LogMessage('warning','Bad input. Please try again.',['gui'])
    178              return False
    179
    180          return True
    181
    182 -    def apply(self):
    183
    184          try:
    185              if os.path.exists(PREFERENCES.vmd_path):
    186                  definePDBViewer('vmd', PREFERENCES.vmd_path)
    187
    188              else:
    189                  raise
    190
    191          except:
    192              raise Error('Error when defining the PDB viewer from %s path.' % PREFERENCES.vmd_path)
    193
    194          try:
    195
    196              local = {}
    197              skeleton = eval(self.description, vars(Skeleton), local)
    198              universe = skeleton.make({}, self.avgStruct)
    199              universe.setCellParameters(self.cell)
    200
    201              avg = Configuration(universe, self.avgStruct)
    202              pseudoTraj = [avg]
    203
    204              for frame in range(self.nFrames):
    205                  vibr = copy.copy(avg)
    206                  for selMode in self.selectedMode:
    207                      dx = copy.copy(self.dx[selMode])
    208                      dx.shape = (universe.numberOfAtoms(), 3)
    209                      d = ParticleVector(universe, dx)
    210                      vibr += self.amplitude*Num.sin(2.0*Num.pi*float(frame)/self.nFrames)*d
    211
    212                  pseudoTraj.append(vibr)
    213
    214              viewSequenceVMD(universe, pseudoTraj, periodic = 1)
    215
    216          except:
    217              raise Error('Error when animating the selected mode(s).')
    218
    219 -    def openNetCDFFile(self, event = None):
    220          """
    221          This method open the NetCDF that contains the effective modes.
    222          Arguments:
    223              - event: Tkinter event.
    224          """
    225
    226          # Case where the user enters a file name directly in the entry widget without using the browser.
    227          if event is not None:
    228              if event.widget == self.fileBrowser.entry:
    229                  filename = self.fileBrowser.getValue()
    230              else:
    231                  return
    232
    233          else:
    234              # The name of the NetCDF file to load.
    235              filename = askopenfilename(parent = self,\
    236                                         filetypes = [('NetCDF file','*.nc')],\
    237                                         initialdir = PREFERENCES.trajfile_path)
    238
    239          # The file must exist.
    240          if filename:
    241              try:
    242                  self.netcdf = NetCDFFile(filename, 'r')
    243
    244              except IOError:
    245                  LogMessage('warning','Problem when reading the NetCDF file.',['gui'])
    246                  self.fileBrowser.setValue('')
    247                  self.selectedModeEntry.setValue('')
    248                  self.nFramesEntry.setValue('')
    249                  self.amplitudeEntry.setValue('')
    250
    251              try:
    252                  self.description = self.netcdf.variables['description'][:].tostring()
    253
    254                  # The frequencies values.
    255                  self.omega = self.netcdf.variables['omega'].getValue()
    256
    257                  # The displacements.
    258                  self.dx = self.netcdf.variables['dx'].getValue()
    259
    260                  # The average structure.
    261                  self.avgStruct = self.netcdf.variables['avgstruct'].getValue()
    262
    263              except KeyError:
    264                  LogMessage('warning','The NetCDF file %s miss some QHA analysis keywords.' % filename,['gui'])
    265
    266              self.fileBrowser.setValue(filename)
    267
    268              self.selectedModeLb.lb.delete(0, END)
    269
    270              for i in range(len(self.omega)):
    271                  ome = self.omega[i]
    272                  self.selectedModeLb.lb.insert(END, 'Mode %s (%s cm-1)' % (i+1, ome))
    273
    274              self.nFramesEntry.setValue(10)
    275              self.amplitudeEntry.setValue(0.1)
    276
    277              try:
    278                  self.cell = self.netcdf.variables['box_size'][:]
    279              except KeyError:
    280                  self.cell = None
    281
    282              self.netcdf.close()
    283
    284          return 'break'
    285