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

# Source Code for Module nMOLDYN.GUI.AnalysisDialog

      1  """This modules implements I{Analysis-->selected analysis} dialog.
      2
      3  Classes:
      4      * AnalysisDialog: creates I{Analysis-->selected analysis} dialog used to setup and/orrun
      5        an analysis.
      6  """
      7
      8  # The python distribution modules
      9  import os
     10  import sys
     11
     12  # The Tcl/Tk modules
     13  from Tkinter import *
     14  from tkMessageBox import askyesno
     15
     16  # The MMTK modules.
     17  from MMTK.Trajectory import Trajectory
     18
     19  # The nMOLDYN modules
     20  import nMOLDYN
     21  from nMOLDYN.Analysis.Template import *
     22  from nMOLDYN.Core.Error import Error
     23  from nMOLDYN.Core.Logger import LogMessage
     24  from nMOLDYN.Core.Preferences import PREFERENCES
     25  from nMOLDYN.GUI.PlotNetCDFVariableDialog import PlotNetCDFVariableDialog
     26  from nMOLDYN.GUI.PyroServerDialog import PyroServerDialog
     27  from nMOLDYN.GUI.SelectionDialog import SelectionDialog
     28  from nMOLDYN.GUI.Widgets import *
     29
     30 -class AnalysisDialog(Toplevel):
     31      """Builds the dialog for nMOLDYN analysis.
     32      """
     33
     34 -    def __init__(self, parent, analysis, trajectory):
     35          """The constructor.
     36
     37          @param parent: the parent widget.
     38
     39          @param analysis: the analysis to setup.
     40          @type analysis: a class object of one of the nMOLDYN.Analysis subclasses.
     41
     42          @param trajectory: the loaded trajectory.
     43          @type trajectory: instance of MMTK.Trajectory.Trajectory class
     44          """
     45
     46          Toplevel.__init__(self, parent)
     47          self.transient(parent)
     48
     49          self.parent = parent
     50          self.analysis = analysis
     51          self.actualAnalysis = None
     52          if isinstance(trajectory, Trajectory):
     53              self.trajectory = trajectory
     54
     55          else:
     56              return
     57
     58          self.title(self.analysis.__name__)
     59
     60          statusFrame = LabelFrame(self, text = 'Job status', relief = GROOVE, bd = 2)
     61          self.statusBar = StatusBar(statusFrame)
     62
     63          body = Frame(self)
     64          self.initial_focus = self.body(body)
     65          body.grid(row = 0, column = 0, sticky = EW)
     66
     67          self.buttonbox()
     68
     69          statusFrame.grid(row = 2, padx = 3, pady = 3, sticky = EW)
     70          statusFrame.grid_columnconfigure(0, weight = 1)
     71
     72          self.statusBar.grid(row = 0, column = 0)
     73
     74          self.grab_set()
     75
     76          if not self.initial_focus:
     77              self.initial_focus = self
     78
     79          self.protocol("WM_DELETE_WINDOW", self.cancel)
     80
     81          self.resizable(width = NO, height = NO)
     82
     83          self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))
     84
     85          self.initial_focus.focus_set()
     86
     87          self.wait_window(self)
     88
     89 -    def body(self, master):
     90          """Create dialog body. Return widget that should have initial focus.
     91          """
     92
     93          # The frame that will contain the widgets whose the analysis dialog is built up.
     94          widgetsFrame = LabelFrame(master, text = 'Setup', bd = 2, relief = GROOVE)
     95          widgetsFrame.grid(row = 0, column = 0, padx = 3, pady = 3, sticky = EW)
     96          widgetsFrame.grid_columnconfigure(0, weight = 1)
     97
     98          self.widgets = {}
     99
    100          for widget in self.analysis.inputParametersNames:
    101              widget = widget.lower()
    102
    103              if widget == 'armodelorder':
    104                  self.widgets[widget] = ComboIntegerEntry(widgetsFrame,\
    105                                                           frameLabel = "Model order", \
    106                                                           tagName = 'model_order',\
    107                                                           contents = 50)
    108
    109              elif widget == 'atomorder':
    110                  self.widgets[widget] = ComboStringEntry(widgetsFrame, \
    111                                                          frameLabel = "Atom order",\
    112                                                          tagName = 'atom_order',\
    113                                                          contents = "no")
    114
    115              elif widget == 'bond':
    116                  self.widgets[widget] = ComboButton(widgetsFrame,\
    117                                                     frameLabel = "Bond selection",\
    118                                                     tagName = 'bond_selection',\
    119                                                     contents = "Select",\
    120                                                     withEntry = 'all',\
    121                                                     command = lambda : SelectionDialog(self, 'bond', self.trajectory.universe.nmoldyncontents))
    122
    123              elif widget == 'deuteration':
    124                  self.widgets[widget] = ComboButton(widgetsFrame,\
    125                                                     frameLabel = "Deuteration selection",\
    126                                                     tagName = 'deuteration_selection',\
    127                                                     contents = "Select",\
    128                                                     withEntry = 'no',\
    129                                                     command = lambda : SelectionDialog(self, 'deuteration', self.trajectory.universe.nmoldyncontents))
    130
    131              elif widget == 'differentiation':
    132                  if 'velocities' in self.trajectory.variables():
    133                      diffOrder = (0,1,2,3,4,5)
    134                  else:
    135                      diffOrder = (1,2,3,4,5)
    136                  self.widgets[widget] = ComboSpinbox(widgetsFrame,\
    137                                                      frameLabel = "Differentiation order",\
    138                                                      tagName = 'differentiation_order',\
    139                                                      contents = diffOrder)
    140
    141              elif widget == 'fftwindow':
    142                  self.widgets[widget] = ComboFloatEntry(widgetsFrame,\
    143                                                         frameLabel = "FFT window (% of trajectory length)",\
    144                                                         tagName = 'fft_window',\
    145                                                         contents = 10.0)
    146
    147              elif widget == 'filter':
    148                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    149                                                          frameLabel = "Pass-Band filter (in tHz)",\
    150                                                          tagName = 'pass_band_filer',\
    151                                                          contents = "0.0:1000.0")
    152
    153              elif widget == 'group':
    154                  self.widgets[widget] = ComboButton(widgetsFrame,\
    155                                                     frameLabel = "Group selection",\
    156                                                     tagName = 'group_selection',\
    157                                                     contents = "Select",\
    158                                                     withEntry = 'all',\
    159                                                     command = lambda : SelectionDialog(self, 'group', self.trajectory.universe.nmoldyncontents))
    160
    161              elif widget == 'normalize':
    162                  self.widgets[widget] = ComboCheckbutton(widgetsFrame,\
    163                                                          frameLabel = "Normalize",\
    164                                                          tagName = 'normalize',\
    165                                                          onvalue = "yes",\
    166                                                          offvalue = "no")
    167
    168              elif widget == 'phivalues':
    169                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    170                                                          frameLabel = "Phi values (in deg)",\
    171                                                          tagName = 'phi_values',\
    172                                                          contents = "-180.0:180.0:10.0")
    173
    174              elif widget == 'projection':
    175                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    176                                                          frameLabel = "Project displacement on",\
    177                                                          tagName = 'project_displacement_on',\
    178                                                          contents = "no")
    179
    180              elif widget == 'pyroserver':
    181                  self.widgets[widget] = ComboButton(widgetsFrame,\
    182                                                     frameLabel = "Pyro server",\
    183                                                     tagName = 'pyro_server',\
    184                                                     contents = "Select",\
    185                                                     withEntry = 'monoprocessor',\
    186                                                     command = lambda : PyroServerDialog(self))
    187
    188              elif widget == 'qshellvalues':
    189                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    190                                                          frameLabel = "Q values (in nm-1)",\
    191                                                          tagName = 'qshell_values',\
    192                                                          contents = "0.0:100.0:1.0")
    193
    194              elif widget == 'qshellwidth':
    195                  self.widgets[widget] = ComboFloatEntry(widgetsFrame,\
    196                                                         frameLabel = "Q shell width (in nm-1)",\
    197                                                         tagName = 'qshell_width',\
    198                                                         contents = 1.0)
    199
    200              elif widget == 'qvectorsdirection':
    201                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    202                                                          frameLabel = "Q vectors direction",\
    203                                                          tagName = 'qvectors_direction',\
    204                                                          contents = "no")
    205
    206              elif widget == 'qvectorsgenerator':
    207                  self.widgets[widget] = ComboRadiobutton(widgetsFrame,\
    208                                                          frameLabel = "Q vectors generator",\
    209                                                          tagName = 'qvectors_generator',\
    210                                                          contents = ["3D isotropic", "2D isotropic", "anisotropic"],\
    211                                                          layout = (1,3))
    212
    213              elif widget == 'qvectorspershell':
    214                  self.widgets[widget] = ComboIntegerEntry(widgetsFrame,\
    215                                                           frameLabel = "Q vectors per shell",\
    216                                                           tagName= 'qvectors_per_shell',\
    217                                                           contents = 50)
    218
    219              elif widget == 'referencedirection':
    220                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    221                                                          frameLabel = "Reference direction",\
    222                                                          tagName = 'reference_direction',\
    223                                                          contents = "0.0,0.0,1.0")
    224
    225              elif widget == 'referenceframe':
    226                  self.widgets[widget] = ComboIntegerEntry(widgetsFrame,\
    227                                                           frameLabel = "Reference frame",\
    228                                                           tagName= 'reference_frame',\
    229                                                           contents = 1)
    230
    231              elif widget == 'removetranslation':
    232                  self.widgets[widget] = ComboCheckbutton(widgetsFrame,\
    233                                                          frameLabel = "Remove translation",\
    234                                                          tagName = 'remove_translation',\
    235                                                          onvalue = "yes",\
    236                                                          offvalue = "no")
    237
    238              elif widget == 'rvalues':
    239                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    240                                                          frameLabel = "Distances (in nm)",\
    241                                                          tagName = 'distances',\
    242                                                          contents = "0.0:1.0:0.1")
    243
    244              elif widget == 'stepwiserbt':
    245                  self.widgets[widget] = ComboCheckbutton(widgetsFrame,\
    246                                                          frameLabel = "Stepwise RBT",\
    247                                                          tagName = 'stepwise_rbt',\
    248                                                          onvalue = "yes",\
    249                                                          offvalue = "no")
    250
    251              elif widget == 'storerbtdetails':
    252                  self.widgets[widget] = ComboCheckbutton(widgetsFrame,\
    253                                                          frameLabel = "Store RBT details",\
    254                                                          tagName = 'store_rbt_details',\
    255                                                          onvalue = "yes",\
    256                                                          offvalue = "no")
    257
    258              elif widget == 'subset':
    259                  self.widgets[widget] = ComboButton(widgetsFrame,\
    260                                                     frameLabel = "Subset selection",\
    261                                                     tagName = 'subset_selection',\
    262                                                     contents = "Select",\
    263                                                     withEntry = 'all',\
    264                                                     command = lambda : SelectionDialog(self, 'subset', self.trajectory.universe.nmoldyncontents))
    265
    266              elif widget == 'temperature':
    267                  self.widgets[widget] = ComboFloatEntry(widgetsFrame,\
    268                                                         frameLabel = "Temperature (in K)",\
    269                                                         tagName = 'temperature',\
    270                                                         contents = 1.0)
    271
    272              elif widget == 'triplet':
    273                  self.widgets[widget] = ComboButton(widgetsFrame,\
    274                                                     frameLabel = "Triplet selection",\
    275                                                     tagName = 'triplet_selection',\
    276                                                     contents = "Select",\
    277                                                     withEntry = 'all',\
    278                                                     command = lambda : SelectionDialog(self, 'triplet', self.trajectory.universe.nmoldyncontents))
    279
    280              elif widget == 'thetavalues':
    281                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    282                                                          frameLabel = "Theta values (in deg)",\
    283                                                          tagName = 'theta_values',\
    284                                                          contents = "0.0:180.0:10.0")
    285
    286              elif widget == 'timeinfo':
    287                  timeInfo = '%d:%d:%d' % (1, len(self.trajectory), 1)
    288                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    289                                                          frameLabel = "Frame selection",\
    290                                                          tagName = 'frame_selection',\
    291                                                          contents = timeInfo)
    292
    293              elif widget == 'trajectory':
    294                  self.widgets[widget] = ComboLabel(widgetsFrame,\
    295                                                    frameLabel = "Trajectory file",\
    296                                                    tagName = 'tajectory_file',\
    297                                                    contents = self.trajectory.filename)
    298
    299              elif widget == 'weights':
    300                  self.widgets[widget] = ComboRadiobutton(widgetsFrame,\
    301                                                          frameLabel = "Weights",\
    302                                                          tagName = 'weights',\
    303                                                          contents = ["equal", "mass", "coherent", "incoherent", "atomicNumber"],\
    304                                                          layout = (2,3))
    305
    306              elif widget == 'wignerindexes':
    307                  self.widgets[widget] = ComboStringEntry(widgetsFrame,\
    308                                                          frameLabel = "Wigner indexes",\
    309                                                          tagName = 'wigner_indexes',\
    310                                                          contents = "0,0,0")
    311
    312              # All the other case concerns the output file name.
    313              else:
    314                  # The basename of the input trajectory name.
    315                  baseName = widget.upper() + '_' + os.path.basename(self.trajectory.filename)
    316                  try:
    317                      outputFilename = os.path.join(PREFERENCES.outputfile_path,baseName)
    318                  except TypeError:
    319                      outputFilename = baseName
    320                  self.widgets[widget] = ComboFileBrowser(widgetsFrame,\
    321                                                          frameLabel = "%s output file" % widget.upper(),\
    322                                                          tagName= "%s_output_file" % widget.lower(),\
    323                                                          contents = outputFilename,\
    324                                                          save = True,\
    325                                                          filetypes = [("NetCDF file", ".nc"),])
    326
    327              # The tagName for the combo widget is set.
    328              setattr(self.widgets[widget],'tagName','%s_%s' % (self.analysis.shortName.lower(),self.widgets[widget].tagName.lower()))
    329
    330              # And displayed into the analysis dialog.
    331              self.widgets[widget].grid(column = 0, sticky = EW, padx = 2, pady = 2)
    332              self.widgets[widget].grid_columnconfigure(0, weight = 1)
    333
    334          if hasattr(self.analysis, 'default'):
    335              for k, v in self.analysis.default.items():
    336                  k = k.lower()
    337                  if k == 'weights':
    338                      self.widgets[k].setValue(v)
    339
    340          return None
    341
    342 -    def buttonbox(self):
    343          """Add standard button box.
    344          """
    345
    346          # The frame that contains the 'Cancel' and 'OK' buttons.
    347          box = LabelFrame(self, text = 'Actions', bd = 2, relief = GROOVE)
    348          box.grid(row = 1, column = 0, sticky = EW, padx = 3, pady = 3)
    349          box.grid_columnconfigure(0, weight = 1)
    350
    351          # The 'Cancel' button.
    352          w = Button(box, text = 'Cancel', width = 8, height = 3, command = self.cancel)
    353          w.grid(row = 0, column = 0, pady = 3, sticky = E)
    354
    355          # The 'Estimate' button.
    356          w = Button(box, text = 'Estimate', width = 8, height = 3, command = lambda : self.ok(runMode = 'estimate'))
    357          w.grid(row = 0, column = 1, pady = 3)
    358
    359          # The 'Save' button.
    360          w = Button(box, text = 'Save', width = 8, height = 3, command = lambda : self.ok(runMode = 'save'))
    361          w.grid(row = 0, column = 2, pady = 3)
    362
    363          # The 'Run' button.
    364          w = Button(box, text = 'Run', width = 8, height = 3, command = lambda : self.ok(runMode = 'run'), default = ACTIVE)
    365          w.grid(row = 0, column = 3, pady = 3)
    366
    367          # The 'Save and Run' button.
    368          w = Button(box, text = 'Save\nand\nRun', width = 8, height = 3, command = lambda : self.ok(runMode = 'saveandrun'))
    369          w.grid(row = 0, column = 4, pady = 3)
    370
    371          self.bind("<Return>", self.ok)
    372          self.bind("<Escape>", self.cancel)
    373
    374      # Standard button semantics.
    375 -    def ok(self, event = None, runMode = 'run'):
    376
    377          if not self.validate(runMode):
    378              self.initial_focus.focus_set()
    379              return
    380
    381          self.update_idletasks()
    382
    383          self.apply(runMode)
    384
    385 -    def cancel(self, event=None):
    386
    387          # Put focus back to the parent window
    388          self.parent.focus_set()
    389          self.destroy()
    390
    391      # Command hooks
    392 -    def validate(self, runMode):
    393
    394          try:
    395
    396              self.parameters = {'version' : nMOLDYN.__version__}
    397              # Loop over the self.widgets dictionnary.
    398              for widgetName, widget in self.widgets.items():
    399                  self.parameters[widgetName] = widget.getValue()
    400
    401          except:
    402              LogMessage('warning','Bad input for %s. Please try again.' % widgetName,['gui'])
    403              return False
    404
    405          return True
    406
    407 -    def apply(self, runMode):
    408
    409          try:
    410              # The widgets are disabled during the analysis.
    411              for w in self.winfo_children():
    412                  self.widgetsState(w, DISABLED)
    413
    414              if runMode == 'estimate':
    415                  self.estimateAnalysis()
    416
    417              elif runMode == 'save':
    418                  self.saveAnalysis()
    419
    420              elif runMode == 'run':
    421                  self.runAnalysis()
    422
    423              elif runMode == 'saveandrun':
    424                  self.saveAndRunAnalysis()
    425
    426              else:
    427                  raise
    428          except:
    429              # Do not print anythng in case of an error because, a message should have been displayed deeper.
    430              pass
    431
    432          # Whatever the results of the analysis (error or ok), the user gets the hands again on the widgets.
    433          finally:
    434              for w in self.winfo_children():
    435                  self.widgetsState(w, NORMAL)
    436
    437 -    def estimateAnalysis(self):
    438          """Estimates the time taken by the analysis directly from the GUI.
    439          """
    440
    441          self.parameters['estimate'] = 'yes'
    442          self.actualAnalysis = eval('%s_serial(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    443
    444          t = self.actualAnalysis.runAnalysis()
    445
    446          # The information dialog with the predicted analysis time.
    447          LogMessage('info','\nThe analysis should take:\n %(days)s days %(hours)s hours %(minutes)s minutes %(seconds)s seconds.\n' % t,['gui'])
    448
    449 -    def saveAnalysis(self):
    450          """Saves a python script of the analysis that can be run independantly of the GUI.
    451          """
    452
    453          # Dialog box to choose a name for the python run script.
    454          pythonScript = asksaveasfilename(filetypes = [("nMOLDYN autostart file", "*.py"), ("nMOLDYN input file", "*.nmi")],\
    455                                           title = "Browse python file",\
    456                                           initialdir = PREFERENCES.outputfile_path)
    457
    458          if not pythonScript:
    459              return
    460
    461          self.parameters['estimate'] = 'no'
    462
    463          if not self.parameters.has_key('pyroserver'):
    464              self.actualAnalysis = eval('%s_serial(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    465
    466          else:
    467              if self.parameters['pyroserver'].lower() == 'monoprocessor':
    468                  self.actualAnalysis = eval('%s_serial(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    469
    470              elif self.parameters['pyroserver'][:14].lower() == 'multiprocessor':
    471                  self.actualAnalysis = eval('%s_parallel(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    472
    473              elif self.parameters['pyroserver'][:7].lower() == 'cluster':
    474                  self.actualAnalysis = eval('%s_parallel(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    475              else:
    476                  raise Error('The pyro server was not set properly')
    477
    478          self.actualAnalysis.saveAnalysis(pythonScript)
    479
    480 -    def runAnalysis(self):
    481          """Runs the analysis directly from the GUI.
    482          """
    483
    484          self.parameters['estimate'] = 'no'
    485
    486          if not self.parameters.has_key('pyroserver'):
    487              self.actualAnalysis = eval('%s_serial(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    488
    489          else:
    490              if self.parameters['pyroserver'].lower() == 'monoprocessor':
    491                  self.actualAnalysis = eval('%s_serial(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    492
    493              elif self.parameters['pyroserver'][:14].lower() == 'multiprocessor':
    494                  self.actualAnalysis = eval('%s_parallel(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    495
    496              elif self.parameters['pyroserver'][:7].lower() == 'cluster':
    497                  self.actualAnalysis = eval('%s_parallel(parameters = self.parameters, statusBar = self.statusBar)' % self.analysis.__name__)
    498              else:
    499                  raise Error('The pyro server was not set properly')
    500
    501          t = self.actualAnalysis.runAnalysis()
    502          self.parent.info.insert(contents = '\n\n\nPERFORMED ANALYSIS:\n\n')
    503          self.parent.info.insert(contents = self.actualAnalysis.information)
    504
    505          message = 'Analysis run successfully in \n %(days)s days %(hours)s hours %(minutes)s minutes %(seconds)s seconds.\n\n' % t
    506          if self.actualAnalysis.toPlot is None:
    507              # The information dialog with the predicted analysis time.
    508              LogMessage('info',message,['gui'])
    509          else:
    510              message += 'Do you want to plot the results right now ?'
    511              q = askyesno(title = 'Question', message = message)
    512              if q:
    513                  PlotNetCDFVariableDialog(self, **self.actualAnalysis.toPlot)
    514
    515 -    def saveAndRunAnalysis(self):
    516          """
    517          This method is called when the user presses the 'Save and Run' button of an analysis dialog.
    518          It saves a python script of the analysis and run the analysis directly from the GUI.
    519          """
    520
    521          self.saveAnalysis()
    522          self.runAnalysis()
    523
    524 -    def widgetsState(self, widget, state = DISABLED):
    525          """
    526          This method is launched when an analysis is run from the GUI. Il will disable all the widgets of the analysis dialog.
    527          """
    528
    529          if widget.winfo_children():
    530              for w in widget.winfo_children():
    531                  self.widgetsState(w, state)
    532          else:
    533              try:
    534                  widget.config(state = state)
    535              except:
    536                  pass
    537              finally:
    538                  return
    539