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

# Source Code for Module nMOLDYN.GUI.MainDialog

      1  """This is where the main window of nMOLDYN is defined.
      2
      3  Classes:
      4      * MainDialog: The class that defines the nMOLDYN GUI main window and its associated actions.
      5  """
      6
      7  # The python distribution modules.
      8  import inspect
      9  import os
     10  import re
     11  import subprocess
     12  import sys
     13  import webbrowser
     14
     15  from tkMessageBox import askyesno
     16
     17  # The ScientificPython modules.
     18  from Scientific import N
     19  from Scientific.IO.NetCDF import NetCDFFile, _NetCDFFile
     20
     21  # The MMTK distribution modules.
     22  from MMTK.Trajectory import Trajectory, trajectoryInfo, TrajectorySet
     23
     24  from Tkinter import *
     25  from tkFileDialog import askopenfilename
     26
     27  # The nMOLDYN modules.
     28  from nMOLDYN.Analysis.Analysis import setUniverseContents
     29  from nMOLDYN.Analysis.Dynamics import *
     30  from nMOLDYN.Analysis.NMR import *
     31  from nMOLDYN.Analysis.Scattering import *
     32  from nMOLDYN.Analysis.Structure import *
     33  from nMOLDYN.Core.Error import Error
     34  from nMOLDYN.Core.Logger import LogMessage
     35  from nMOLDYN.Core.Preferences import PREFERENCES
     36
     37  from nMOLDYN.GUI.AnalysisDialog import AnalysisDialog
     38  from nMOLDYN.GUI.AnimationDialog import AnimationDialog
     39  from nMOLDYN.GUI.AnalysisBenchmarkDialog import AnalysisBenchmarkDialog
     40  from nMOLDYN.GUI.ASCIIToNetCDFConversionDialog import ASCIIToNetCDFConversionDialog
     41  from nMOLDYN.GUI.PreferencesDialog import PreferencesDialog
     42  from nMOLDYN.GUI.GeneralInformationsDialog import GeneralInformationsDialog
     43  from nMOLDYN.GUI.NetCDFToASCIIConversionDialog import NetCDFToASCIIConversionDialog
     44  from nMOLDYN.GUI.PDBSnapshotGeneratorDialog import PDBSnapshotGeneratorDialog
     45  from nMOLDYN.GUI.PlotNetCDFVariableDialog import PlotNetCDFVariableDialog
     46  from nMOLDYN.GUI.TrajectoryConversionDialog import *
     47  from nMOLDYN.GUI.ViewEffectiveModeDialog import ViewEffectiveModeDialog
     48  from nMOLDYN.GUI.Widgets import ComboText
     49
     50  # Package path
     51  nmoldyn_package_path = os.path.dirname(os.path.split(__file__)[0])
     52
     53  from Tkinter import *
     54
     55 -class MainDialog(Tk):
     56      """
     57      This is the base class for the nMoldyn GUI. It launches the main window of nMoldyn from which
     58      different menus can be accessed.
     59      """
     60
     61 -    def __init__(self, netcdf_filename = None):
     62          """
     63          The constructor. Displays the main window.
     64
     65          @param netcdf_filename: a string specifying a NetCDF file, nMOLDYN should be started with.
     66          @rtype: string
     67          """
     68
     69          Tk.__init__(self)
     70
     71          self.option_add('*Dialog.msg.wrapLength', '30i')
     72
     73          if netcdf_filename is not None:
     74              netcdf_filename = os.path.abspath(netcdf_filename)
     75              if os.path.exists(netcdf_filename):
     76                  self.netcdf_filename = netcdf_filename
     77              else:
     78                  self.netcdf_filename = None
     79                  LogMessage('warning', 'The NetCDF input file %s does not exist.' % netcdf_filename, ['console'])
     80
     81          else:
     82              self.netcdf_filename = None
     83
     84          self.netcdf = None
     85
     86          self.initial_focus = self.body(self)
     87
     88          self.grab_set()
     89
     90          self.protocol("WM_DELETE_WINDOW", self.cancel)
     91
     92          # Tcl/Tk specificities for MacOS platform.
     93          if sys.platform == 'darwin':
     94              try:
     95                  self.tk.call('package', 'require', 'tile')
     96                  self.tk.call('namespace', 'import', '-force', 'ttk::*')
     97                  self.tk.call('tile::setTheme', 'aqua')
     98              except TclError:
     99                  pass
    100
    101          self.resizable(width = NO, height = NO)
    102
    103          self.title('nMOLDYN')
    104
    105          self.geometry("+20+20")
    106
    107          self.focus_set()
    108
    109          # Wait 1s before checking for missing program.
    110          self.after(1000, self.checkConfiguration)
    111
    112          self.wait_window(self)
    113
    114 -    def body(self, master):
    115
    116          mainMenu = Menu(master)
    117
    118          if sys.platform == 'darwin':
    119              accel = 'Cmd-'
    120              bindKey = 'Command-'
    121          else:
    122              accel = 'Ctrl-'
    123              bindKey = 'Control-'
    124
    125          # ####################
    126          # The 'File' Menu.
    127          # ####################
    128          self.fileMenu = Menu(mainMenu, tearoff = 0)
    129
    130          # Opens a netcdf file.
    131          self.fileMenu.add_command(label = 'Load NetCDF file', command = self.loadNetCDF, accelerator = accel + 'O')
    132          master.bind('<%s>' % (bindKey+'o',), self.loadNetCDF)
    133
    134          self.fileMenu.add_separator()
    135
    136          conversionMenu = Menu(self.fileMenu, tearoff = 0)
    137          self.fileMenu.add_cascade(label = 'Trajectory conversion', menu = conversionMenu)
    138          conversionMenu.add_command(label = 'Amber NetCDF to MMTK', command = lambda : AmberNetCDFConverterDialog(master))
    139          conversionMenu.add_command(label = 'CHARMM/X-PLOR to MMTK', command = lambda : CHARMMConverterDialog(master))
    140          conversionMenu.add_command(label = 'DL_POLY to MMTK', command = lambda : DL_POLYConverterDialog(master))
    141
    142          materialsStudioMenu = Menu(conversionMenu, tearoff = 0)
    143          conversionMenu.add_cascade(label = 'MaterialsStudio', menu = materialsStudioMenu)
    144          materialsStudioMenu.add_command(label = 'Discover to MMTK', command = lambda : MaterialsStudioConverterDialog(master, 'Discover'))
    145          materialsStudioMenu.add_command(label = 'Forcite to MMTK', command = lambda : MaterialsStudioConverterDialog(master, 'Forcite'))
    146
    147          conversionMenu.add_command(label = 'NAMD to MMTK', command = lambda : NAMDConverterDialog(master))
    148          conversionMenu.add_command(label = 'VASP to MMTK', command = lambda : VASPConverterDialog(master))
    149
    150          # Opens a dialog to extract a selected frame from a given trajectory into a PDB file.
    151          self.fileMenu.add_command(label = 'Frame snapshot', command = self.extractTrajectoryFrame, accelerator = accel + 'R')
    152          master.bind('<%s>' % (bindKey+'r',), self.extractTrajectoryFrame)
    153
    154          self.fileMenu.add_separator()
    155
    156          # Opens the NetCDF to ASCII file conversion dialog.
    157          self.fileMenu.add_command(label = 'Convert NetCDF to ASCII', command = self.convertNetCDFToASCII, accelerator = accel + 'U')
    158          master.bind('<%s>' % (bindKey+'u',), self.convertNetCDFToASCII)
    159
    160          # Opens the NetCDF to ASCII file conversion dialog.
    161          self.fileMenu.add_command(label = 'Convert ASCII to NetCDF', command = self.convertASCIIToNetCDF, accelerator = accel + 'I')
    162          master.bind('<%s>' % (bindKey+'i',), self.convertASCIIToNetCDF)
    163
    164          self.fileMenu.add_separator()
    165
    166          # This open the configuration file editor used to set up some of the nMOLDYN environment variables.
    167          self.fileMenu.add_command(label = 'Preferences', command = self.setPreferences, accelerator = accel + 'E')
    168          master.bind('<%s>' % (bindKey+'e',), self.setPreferences)
    169
    170          self.fileMenu.add_separator()
    171
    172          # Close the nMOLDYN apllication.
    173          self.fileMenu.add_command(label = 'Quit', command = self.cancel, accelerator = accel + 'Q')
    174          master.bind('<%s>' % (bindKey+'q',), self.cancel)
    175
    176          # This submenu is popped up when clicking on 'File' item of the main menu.
    177          mainMenu.add_cascade(label = "File", menu = self.fileMenu)
    178
    179          # #####################
    180          # The 'Analysis' Menu.
    181          # #####################
    182          self.analysisMenu = Menu(mainMenu, tearoff = 0)
    183
    184          # #########################################
    185          # The 'Analysis->Dynamics' Menu.
    186          # #########################################
    187          dynamicsMenu = Menu(self.analysisMenu, tearoff = 0)
    188          # The contents of the 'Dynamics' submenu.
    189          self.analysisMenu.add_cascade(label = 'Dynamics', menu = dynamicsMenu)
    190
    191          # The Mean-Square Displacement entry.
    192          dynamicsMenu.add_command(label = 'Mean-Square Displacement',
    193                                   command = lambda : self.analysisDialog(MeanSquareDisplacement))
    194          # The Root Mean Square Deviation entry.
    195          dynamicsMenu.add_command(label = 'Root Mean-Square Deviation',
    196                                   command = lambda : self.analysisDialog(RootMeanSquareDeviation))
    197          # The Gyration Radius entry.
    198          dynamicsMenu.add_command(label = 'Radius Of Gyration',
    199                                   command = lambda : self.analysisDialog(RadiusOfGyration))
    200
    201          dynamicsMenu.add_command(label = 'Angular Correlation',
    202                                   command = lambda : self.analysisDialog(AngularCorrelation))
    203
    204          dynamicsMenu.add_command(label = 'Velocity AutoCorrelation Function',
    205                                   command = lambda : self.analysisDialog(CartesianVelocityAutoCorrelationFunction))
    206
    207          dynamicsMenu.add_command(label = 'Density Of States',
    208                                   command = lambda : self.analysisDialog(CartesianDensityOfStates))
    209
    210          dynamicsMenu.add_separator()
    211
    212          dynamicsMenu.add_command(label = 'Pass-Band Filtered Trajectory',
    213                                   command = lambda : self.analysisDialog(PassBandFilteredTrajectory))
    214          dynamicsMenu.add_command(label = 'Global Motion Filtered Trajectory',
    215                                   command = lambda : self.analysisDialog(GlobalMotionFilteredTrajectory))
    216
    217          # The Rigid-Body Trajectory Analysis entry.
    218          dynamicsMenu.add_command(label = 'Rigid-Body Trajectory',
    219                                   command = lambda : self.analysisDialog(RigidBodyTrajectory))
    220
    221          # The Center Of Mass Trajectory Analysis entry.
    222          dynamicsMenu.add_command(label = 'Center Of Mass Trajectory',
    223                                   command = lambda : self.analysisDialog(CenterOfMassTrajectory))
    224
    225          dynamicsMenu.add_separator()
    226
    227          # The AutoRegressive Analysis entry.
    228          dynamicsMenu.add_command(label = 'Auto-Regressive Analysis',
    229                                   command = lambda : self.analysisDialog(AutoRegressiveAnalysis))
    230
    231          dynamicsMenu.add_separator()
    232
    233          # The Quasi-Harmonic Analysis entry.
    234          dynamicsMenu.add_command(label = 'Quasi-Harmonic Analysis',
    235                                   command = lambda : self.analysisDialog(QuasiHarmonicAnalysis))
    236
    237          dynamicsMenu.add_separator()
    238
    239          # The Reorientational Correlation Function Analysis entry.
    240          dynamicsMenu.add_command(label = 'Reorientational Correlation Function',
    241                                   command = lambda : self.analysisDialog(ReorientationalCorrelationFunction))
    242
    243          # The Angular Velocity Autocorrelation Function.
    244          dynamicsMenu.add_command(label = 'Angular Velocity AutoCorrelation Function',
    245                                   command = lambda : self.analysisDialog(AngularVelocityAutoCorrelationFunction))
    246
    247
    248          # The Angular Density Of States.
    249          dynamicsMenu.add_command(label = 'Angular Density Of States',
    250                                   command = lambda : self.analysisDialog(AngularDensityOfStates))
    251
    252          # #########################################
    253          # The 'Analysis->Scattering' Menu.
    254          # #########################################
    255          scatteringMenu = Menu(self.analysisMenu, tearoff = 0)
    256          # The contents of the 'Scattering' submenu.
    257          self.analysisMenu.add_cascade(label = 'Scattering', menu = scatteringMenu)
    258
    259          # The Coherent Structure Factor Analysis entry.
    260          scatteringMenu.add_command(label = 'Dynamic Coherent Structure Factor',
    261                                     command = lambda : self.analysisDialog(DynamicCoherentStructureFactor))
    262
    263          # The Coherent Structure Factor Analysis (AR Model) entry.
    264          scatteringMenu.add_command(label = 'Dynamic Coherent Structure Factor (AR Model)',
    265                                     command = lambda : self.analysisDialog(DynamicCoherentStructureFactorAR))
    266
    267          scatteringMenu.add_separator()
    268
    269          # The Incoherent Structure Factor Analysis entry.
    270          scatteringMenu.add_command(label = 'Dynamic Incoherent Structure Factor',
    271                                     command = lambda : self.analysisDialog(DynamicIncoherentStructureFactor))
    272
    273          # The Incoherent Structure Factor (AR Model) Analysis entry.
    274          scatteringMenu.add_command(label = 'Dynamic Incoherent Structure Factor (AR Model)',
    275                                     command = lambda : self.analysisDialog(DynamicIncoherentStructureFactorAR))
    276
    277          # The Incoherent Structure Factor (Gaussian approximation) Analysis entry.
    278          scatteringMenu.add_command(label = 'Dynamic Incoherent Structure Factor (Gaussian Approximation)',
    279                                     command = lambda : self.analysisDialog(DynamicIncoherentStructureFactorGaussian))
    280
    281          scatteringMenu.add_separator()
    282
    283          # The Elastic Incoherent Structure Factor Analysis entry.
    284          scatteringMenu.add_command(label = 'Elastic Incoherent Structure Factor',
    285                                     command = lambda : self.analysisDialog(ElasticIncoherentStructureFactor))
    286
    287          scatteringMenu.add_separator()
    288
    289          # The Static Incoherent Structure Factor Analysis entry.
    290          scatteringMenu.add_command(label = 'Static Coherent Structure Factor',
    291                                     command = lambda : self.analysisDialog(StaticCoherentStructureFactor))
    292
    293          # The Static Coherent Structure Factor Analysis entry.
    294          scatteringMenu.add_command(label = 'Smoothed Static Coherent Structure Factor',
    295                                     command = lambda : self.analysisDialog(SmoothedStaticCoherentStructureFactor))
    296
    297          # #########################################
    298          # The 'Analysis->Structure' Menu.
    299          # #########################################
    300          structureMenu = Menu(self.analysisMenu, tearoff = 0)
    301          # The contents of the 'Structure' submenu.
    302          self.analysisMenu.add_cascade(label = 'Structure', menu = structureMenu)
    303
    304          # The Pair Distribution Function Analysis entry.
    305          structureMenu.add_command(label = 'Pair Distribution Function',
    306                                    command = lambda : self.analysisDialog(PairDistributionFunction))
    307
    308          # The Coordination Number Analysis entry.
    309          structureMenu.add_command(label = 'Coordination Number',
    310                                    command = lambda : self.analysisDialog(CoordinationNumber))
    311
    312          # The Coordination Number Analysis entry.
    313          structureMenu.add_command(label = 'Spatial Density',
    314                                    command = lambda : self.analysisDialog(SpatialDensity))
    315
    316          structureMenu.add_separator()
    317
    318          # The Screw fit Analysis entry.
    319          structureMenu.add_command(label = 'ScrewFit Analysis',
    320                                    command = lambda : self.analysisDialog(ScrewFitAnalysis))
    321
    322          # #########################################
    323          # The 'Analysis->NMR' Menu.
    324          # #########################################
    325          nmrMenu = Menu(self.analysisMenu, tearoff = 0)
    326          # The contents of the 'NMR' submenu.
    327          self.analysisMenu.add_cascade(label = 'NMR', menu = nmrMenu)
    328
    329          # The P2 Order Parameter Analysis entry.
    330          nmrMenu.add_command(label = 'Order Parameter', command = lambda : self.analysisDialog(OrderParameter))
    331
    332          # The S2 Order Parameter Analysis entry.
    333          nmrMenu.add_command(label = 'Order Parameter (Contact Model)', command = lambda : self.analysisDialog(OrderParameterContactModel))
    334
    335          # This submenu is popped up when clicking on |Analysis| item of the main menu.
    336          mainMenu.add_cascade(label = "Analysis", menu = self.analysisMenu)
    337
    338          self.analysisMenu.entryconfig(0, state = DISABLED)
    339          self.analysisMenu.entryconfig(1, state = DISABLED)
    340          self.analysisMenu.entryconfig(2, state = DISABLED)
    341          self.analysisMenu.entryconfig(3, state = DISABLED)
    342
    343          # #####################
    344          # The 'View' Menu.
    345          # #####################
    346          self.viewMenu = Menu(mainMenu, tearoff = 0)
    347
    348          # The 'Plot' entry. Launches the plot dialog for NetCDF file plotting.
    349          self.viewMenu.add_command(label = 'Plot', command = self.plotNetCDF, accelerator = accel + 'L')
    350          master.bind('<%s>' % (bindKey+'l',), self.plotNetCDF)
    351
    352          # The 'Animation' entry. Launches the Animation dialog.
    353          self.viewMenu.add_command(label = 'Animation', command = self.animateTrajectory, accelerator = accel + 'M')
    354          master.bind('<%s>' % (bindKey+'m',), self.animateTrajectory)
    355
    356          # The 'View Effective mode'. Launches the effective mode viewer dialog.
    357          self.viewMenu.add_command(label = 'Effective mode', command = self.viewEffectiveMode, accelerator = accel + 'K')
    358          master.bind('<%s>' % (bindKey+'k',), self.viewEffectiveMode)
    359
    360          # This submenu is popped up when clicking on |View| item of the main menu.
    361          mainMenu.add_cascade(label = "View", menu = self.viewMenu)
    362
    363          # #####################
    364          # The 'Help' Menu.
    365          # #####################
    366          self.helpMenu = Menu(mainMenu, tearoff = 0)
    367
    368          # This is the item 'nMOLDYN users guide'. Clicking on itopen the nMOLDYN users guide pdf file.
    369          self.helpMenu.add_command(label = "Documentation", command = self.displayDocumentation, accelerator = accel + 'H')
    370          master.bind('<%s>' % (bindKey+'h',), self.displayDocumentation)
    371
    372          # This is the item 'nMOLDYN mailig list'.
    373          self.helpMenu.add_command(label = "Mailing List", command = self.displayMailingList)
    374
    375          # This is the item 'Technical notes'. Clicking on it opens the nMOLDYN API html files.
    376          self.helpMenu.add_command(label = 'API', command = self.displayAPI, accelerator = accel + 'T')
    377          master.bind('<%s>' % (bindKey+'t',), self.displayAPI)
    378
    379          self.helpMenu.add_separator()
    380
    381          # This is the item 'Check job status'. Launches a toplevel window where the march
    382          # of the running jobs is displayed.
    383  #        self.helpMenu.add_command(label = 'Trace analysis', command = self.traceAnalysis, accelerator = accel + 'J')
    384  #        master.bind('<%s>' % (bindKey+'j',), self.traceAnalysis)
    385
    386          # This is the item 'nMOLDYN benchmark'. Launches a toplevel window where the user can perform some analysis
    387          # benchmarks.
    388          self.helpMenu.add_command(label = 'Analysis benchmark', command = self.analysisBenchmark)
    389
    390          self.helpMenu.add_separator()
    391
    392          # This is the item 'About nMOLDYN'.
    393          # Clicking on it pops up a toplevel window containing some general informations about nMOLDYN.
    394          self.helpMenu.add_command(label = 'About nMOLDYN', command = self.aboutNMOLDYN, accelerator = accel + 'B')
    395          master.bind('<%s>' % (bindKey+'b',), self.aboutNMOLDYN)
    396
    397          # This submenu is popped up when clicking on |Help| item of the main menu.
    398          mainMenu.add_cascade(label = "Help", menu = self.helpMenu)
    399
    400          # Displays the menu
    401          master.config(menu = mainMenu)
    402
    403          mainPanel = Frame(master)
    404          mainPanel.grid(row = 0, column = 0)
    405
    406          # Display of an image of a neutron on the left side of the main window.
    407          self.neutronGIF = PhotoImage(file = os.path.join(nmoldyn_package_path, 'GUI', 'Images', 'Neutron.gif'))
    408
    409          leftFrame = Frame(mainPanel)
    410          leftFrame.grid(row = 0, column = 0)
    411          Label(leftFrame, image = self.neutronGIF).grid(row = 0)
    412          Label(leftFrame, image = self.neutronGIF).grid(row = 1)
    413          Label(leftFrame, image = self.neutronGIF).grid(row = 2)
    414          Label(leftFrame, image = self.neutronGIF).grid(row = 3)
    415          Label(leftFrame, image = self.neutronGIF).grid(row = 4)
    416
    417          self.info = ComboText(mainPanel)
    418          self.info.config({'width' : 500, 'relief' : SUNKEN})
    419          self.info.text.config({'wrap' : WORD, 'width' : 80})
    420          self.info.grid(row = 0, column = 1, sticky = "NSEW")
    421
    422          self.info.text.tag_config('12', font = ('Courier', 12))
    423          self.info.text.tag_config('12B', font = ('Courier', 12, 'bold'))
    424          self.info.text.tag_config('r12B', foreground = 'red', font = ('Courier', 12, 'bold'))
    425          self.info.text.tag_config('12I', font = ('Courier', 12, 'normal', 'italic'))
    426
    427          # Display of an image of a protein on the left side of the main window.
    428          self.proteinGIF = PhotoImage(file = os.path.join(nmoldyn_package_path, 'GUI', 'Images', 'Protein.gif'))
    429          rightFrame = Frame(mainPanel)
    430          rightFrame.grid(row = 0, column = 2)
    431          Label(rightFrame, image = self.proteinGIF).grid(row = 0)
    432          Label(rightFrame, image = self.proteinGIF).grid(row = 1)
    433          Label(rightFrame, image = self.proteinGIF).grid(row = 2)
    434          Label(rightFrame, image = self.proteinGIF).grid(row = 3)
    435          Label(rightFrame, image = self.proteinGIF).grid(row = 4)
    436
    437          mainPanel.grid_rowconfigure(0, weight = 1)
    438          self.info.grid_rowconfigure(0, weight = 1)
    439          self.info.text.grid_rowconfigure(0, weight = 1)
    440
    441          if self.netcdf_filename is not None:
    442              self.loadNetCDF(filename = self.netcdf_filename)
    443
    444          return None
    445
    446 -    def cancel(self, event = None):
    447          self.destroy()
    448          sys.exit(0)
    449
    450 -    def loadNetCDF(self, event = None, filename = None):
    451          """
    452          This method is launched when the user clicks on the |Load NetCDF| of the |File| menu.
    453          It loads the NetCDF file and displays its main informations in the information window.
    454          """
    455
    456          if filename is None:
    457              # A file dialog box is opened for browsing the 'nc' or 'ncs' files.
    458              # A 'ncs' file is a text file with the names of several 'nc' files.
    459              filename = askopenfilename(filetypes = [("NetCDF trajectory file", "*.nc"),], title = "Open NetCDF",
    460                                         initialdir = PREFERENCES.trajfile_path)
    461
    462          # Do nothing if no trajectory has been selected.
    463          if not filename:
    464              return
    465
    466          # The information window is cleaned up.
    467          self.info.cleanup()
    468
    469          # The information window is cleaned up.
    470          self.info.cleanup()
    471
    472          # The extension of the loaded file.
    473          ext = os.path.splitext(filename)[1]
    474
    475          try:
    476              if ext == '.ncs':
    477                  # Open the ncs file as a trajectory set.
    478                  try:
    479                      # The trajectory set is opened for reading and its contents sent to |trajSet| list.
    480                      trajSetFile = open(filename, 'r')
    481                      trajSet = [t.strip() for t in trajSetFile.readlines()]
    482                      trajSetFile.close()
    483
    484                      # Load the whole trajectory set.
    485                      self.netcdf = TrajectorySet(None, trajSet)
    486
    487                      # A new MMTK TrajectorySet class attribute is created in the case of a MMTK trajectory object coming from a trajectory set.
    488                      setattr(self.netcdf, 'filename', trajSet[0])
    489                      self.netcdf_filename = os.path.abspath(trajSet[0])
    490
    491                  except IOError:
    492                      raise
    493
    494              else:
    495                  # First try to load the file as a trajectory.
    496                  try:
    497                      # Load the input trajectory.
    498                      self.netcdf_filename = os.path.abspath(filename)
    499                      self.netcdf = Trajectory(None, filename, 'r')
    500
    501                  except:
    502                      try:
    503                          self.netcdf_filename = os.path.abspath(filename)
    504                          self.netcdf = NetCDFFile(filename, 'r')
    505                      except:
    506                          raise
    507
    508          except:
    509              self.netcdf_filename = None
    510              self.netcdf = None
    511              # The information window is cleaned up.
    512              self.info.cleanup()
    513              raise Error('The file %s is not a valid NetCDF file.' % filename)
    514
    515          if isinstance(self.netcdf,Trajectory):
    516
    517              # Detects the occurence of chemical fragments or atoms in the whole universe and indicates whether they are deuterable or not.
    518              setUniverseContents(self.netcdf.universe)
    519
    520              if hasattr(self.netcdf,'time'):
    521                  if self.netcdf.time[1] == self.netcdf.time[0]:
    522                      firstStep = 0.0
    523                      lastStep = float(len(self.netcdf))
    524                      timeStep = 1.0
    525                      LogMessage('warning', 'No or uncorrect times found in the trajectory. Will create an arbitrary \
    526  one from 0 to %d ps.' % lastStep)
    527
    528                  else:
    529                      timeStep = self.netcdf.time[1] - self.netcdf.time[0]
    530                      firstStep = self.netcdf.time[0]
    531                      lastStep = self.netcdf.time[-1]
    532
    533              else:
    534                  firstStep = 0.0
    535                  lastStep = float(len(self.netcdf))
    536                  timeStep = 1.0
    537
    538              self.info.insert(contents = 'FILE TYPE: MMTK NETCDF TRAJECTORY FILE\n\n', tag = '12B')
    539
    540              # Some trajectory informations are displayed on the information window.
    541              self.info.insert(contents = trajectoryInfo(self.netcdf_filename), tag = '12B')
    542              self.info.insert(contents = '\n\nMMTK objects found in the universe: ', tag = '12')
    543
    544              # Write into the info panel the details about the nmoldyn names found in the universe of the loaded trajectory.
    545              for k in sorted(self.netcdf.universe.nmoldyncontents.keys()):
    546                  # Do not consider the '*' nmoldyn name. Not interesting.
    547                  if k == '*':
    548                      continue
    549                  self.info.insert(contents = '\n\t- %d %s (%s)' % (self.netcdf.universe.nmoldyncontents[k]['number'],k, self.netcdf.universe.nmoldyncontents[k]['objectclass']), tag = '12')
    550
    551              self.info.insert(contents = '\n\nNumber of frames: ', tag = '12')
    552              self.info.insert(contents = str(len(self.netcdf)), tag = '12B')
    553              self.info.insert(contents = '\nStarting at: ', tag = '12')
    554              self.info.insert(contents = str(round(firstStep,3)) +' ps', tag = '12B')
    555              self.info.insert(contents = '\nEnding at: ', tag = '12')
    556              self.info.insert(contents = str(round(lastStep,3)) +' ps', tag = '12B')
    557              self.info.insert(contents = '\nTime step: ', tag = '12')
    558              self.info.insert(contents = str(round(timeStep,3)) +' ps', tag = '12B')
    559              self.info.insert(contents = '\n\nUniverse size: ', tag = '12')
    560              self.info.insert(contents = str(len(self.netcdf.universe.atomList())), tag = '12B')
    561              self.info.insert(contents = '\n\nUniverse topology: ', tag = '12')
    562              self.info.insert(contents = self.netcdf.universe.__class__.__name__, tag = '12B')
    563
    564              directBasis = self.netcdf.universe.basisVectors()
    565              if directBasis is not None:
    566                  self.info.insert(contents = '\n\nDirect basis:', tag = '12')
    567                  self.info.insert(contents = '\n%8s\t%8s\t%8s' % tuple(['i','j','k']), tag = '12I')
    568                  for directVector in directBasis:
    569                      self.info.insert(contents = '\n%8.3f\t%8.3f\t%8.3f' % tuple(directVector), tag = '12I')
    570
    571              reciprocalBasis = self.netcdf.universe.reciprocalBasisVectors()
    572              if reciprocalBasis is not None:
    573                  self.info.insert(contents = '\n\nReciprocal basis:', tag = '12')
    574                  self.info.insert(contents = '\n%8s\t%8s\t%8s' % tuple(['i','j','k']), tag = '12I')
    575                  for reciprocalVector in reciprocalBasis:
    576                      self.info.insert(contents = '\n%8.3f\t%8.3f\t%8.3f' % tuple(reciprocalVector), tag = '12I')
    577
    578              # These onglets are avaialble only if the NetCDF is a trajectory.
    579              self.analysisMenu.entryconfig(0, state = NORMAL)
    580              self.analysisMenu.entryconfig(1, state = NORMAL)
    581              self.analysisMenu.entryconfig(2, state = NORMAL)
    582              self.analysisMenu.entryconfig(3, state = NORMAL)
    583
    584          elif isinstance(self.netcdf, _NetCDFFile):
    585
    586              self.analysisMenu.entryconfig(0, state = DISABLED)
    587              self.analysisMenu.entryconfig(1, state = DISABLED)
    588              self.analysisMenu.entryconfig(2, state = DISABLED)
    589              self.analysisMenu.entryconfig(3, state = DISABLED)
    590
    591              self.info.insert(contents = 'FILE TYPE: NETCDF FILE\n\n', tag = '12B')
    592
    593              # The list of the NetCDF string attributes that can be visualized to get informations about the loaded NetCDF file.
    594              # The list is initialized with the first one that should appear if present in the NetCDF file.
    595              netCDFStringAttributes = ['title', 'history', 'comment', 'jobinfo', 'jobinput']
    596              for attr in inspect.getmembers(self.netcdf):
    597                  if isinstance(attr[1], str) & (attr[0] not in netCDFStringAttributes):
    598                      netCDFStringAttributes.append(attr[0])
    599
    600              self.info.insert(contents = 'Information about NetCDF file:\n', tag = '12B')
    601              self.info.insert(contents = '%s.\n\n' % filename, tag = '12')
    602
    603              # Loop over the netcdf file string attributes that will be displayed for information purpose.
    604              for h in netCDFStringAttributes:
    605                  # Checks that the NetCDF has this attributes.
    606                  if hasattr(self.netcdf,h):
    607                      self.info.insert(contents = '%s:\n' % h.capitalize(), tag = '12B')
    608                      self.info.insert(contents = getattr(self.netcdf,h)+ '\n\n', tag = '12')
    609
    610              if hasattr(self.netcdf, 'dimensions'):
    611                  self.info.insert(contents = '\n\nNetCDF dimensions found:', tag = '12B')
    612                  for d in self.netcdf.dimensions.items():
    613                      self.info.insert(contents = '\n\n\t-%s ----> ' % d[0], tag = '12')
    614                      self.info.insert(contents = '%s' % d[1], tag = 'r12B')
    615
    616              if hasattr(self.netcdf, 'variables'):
    617                  self.info.insert(contents = '\n\nNetCDF variables found:', tag = '12B')
    618                  for v in self.netcdf.variables.items():
    619                      self.info.insert(contents = '\n\n\t-%s:' % v[0], tag = '12B')
    620                      self.info.insert(contents = '\n\t\t+ Dimension(s) ----> ', tag = '12')
    621                      self.info.insert(contents = str(v[1].shape), tag = 'r12B')
    622                      if hasattr(v[1], 'typecode'):
    623                          self.info.insert(contents = '\n\t\t+ Typecode ----> ', tag = '12')
    624                          self.info.insert(contents = v[1].typecode(), tag = 'r12B')
    625                      else:
    626                          self.info.insert(contents = '\n\t\t+ Typecode ----> ', tag = '12')
    627                          self.info.insert(contents = 'Undefined', tag = 'r12B')
    628
    629                      if hasattr(v[1], 'units'):
    630                          self.info.insert(contents = '\n\t\t+ Units ----> ', tag = '12')
    631                          self.info.insert(contents = v[1].units, tag = 'r12B')
    632                      else:
    633                          self.info.insert(contents = '\n\t\t+ Units ----> ', tag = '12')
    634                          self.info.insert(contents = 'Undefined', tag = 'r12B')
    635
    636                      try:
    637                          if v[1].typecode() == N.Character:
    638                              continue
    639
    640                          if len(v[1].getValue().shape) > 2:
    641                              raise
    642
    643                          mini = min(v[1].getValue().ravel())
    644                          maxi = max(v[1].getValue().ravel())
    645
    646                      except:
    647                          continue
    648
    649                      else:
    650                          self.info.insert(contents = '\n\t\t+ Range ----> ', tag = '12')
    651                          self.info.insert(contents = '%.5f to %.5f' % (mini, maxi), tag = 'r12B')
    652
    653          else:
    654              self.netcdf = None
    655
    656 -    def extractTrajectoryFrame(self, event = None):
    657          """
    658          This method pops up a dialog from where the user can extract a PDB file from a NetCDF
    659          trajectory frame.
    660          """
    661
    662          PDBSnapshotGeneratorDialog(self, 'PDB frame extraction', self.netcdf)
    663
    664 -    def convertNetCDFToASCII(self, event = None):
    665          """
    666          This method pops up a dialog where the user can proceed to a conversion from a file in NetCDF format
    667          to ASCII format.
    668          """
    669
    670          NetCDFToASCIIConversionDialog(self, 'NetCDF to ASCII conversion', self.netcdf)
    671
    672 -    def convertASCIIToNetCDF(self, event = None):
    673          """
    674          This method pops up a dialog where the user can proceed to a conversion from a file in ASCII format
    675          to NetCDF format.
    676          """
    677
    678          ASCIIToNetCDFConversionDialog(self, 'ASCII/CDL to NetCDF conversion')
    679
    680 -    def checkConfiguration(self):
    681          """
    682          This method checks for missing external programs and display some warning if it found some.
    683          """
    684
    685          if os.path.exists(PREFERENCES.ncdump_path):
    686              self.fileMenu.entryconfig(5, state = NORMAL)
    687
    688          else:
    689              self.fileMenu.entryconfig(5, state = DISABLED)
    690              if PREFERENCES.warning_ncdump.lower() == 'yes':
    691                  q = askyesno(title = 'Warning', message = 'The path to "ncdump" program has not been set.\n\n\
    692  It will not be possible to convert files in NetCDF format to ASCII/CDL format.\n\
    693  You should set that path in the "Preferences"-->"ncdump_path" section.\n\n\
    694  Show this warning next time nMOLDYN starts ?')
    695                  if q:
    696                      setattr(PREFERENCES, 'warning_ncdump', 'yes')
    697                  else:
    698                      setattr(PREFERENCES, 'warning_ncdump', 'no')
    699
    700          if os.path.exists(PREFERENCES.ncgen_path):
    701              self.fileMenu.entryconfig(6, state = NORMAL)
    702
    703          else:
    704              self.fileMenu.entryconfig(6, state = DISABLED)
    705              if PREFERENCES.warning_ncgen.lower() == 'yes':
    706                  q = askyesno(title = 'Warning', message = 'The path to "ncgen" program has not been set.\n\n\
    707  It will not be possible to convert files in ASCII/CDL fomat to NetCDF format.\n\
    708  You should set that path in the "Preferences"-->"ncgen_path" section.\n\n\
    709  Show this warning next time nMOLDYN starts ?')
    710                  if q:
    711                      setattr(PREFERENCES, 'warning_ncgen', 'yes')
    712                  else:
    713                      setattr(PREFERENCES, 'warning_ncgen', 'no')
    714
    715          if (sys.platform[:5] == 'linux') & (PREFERENCES.documentation_style.lower() == 'pdf'):
    716              if os.path.exists(PREFERENCES.acroread_path):
    717                  self.helpMenu.entryconfig(0, state = NORMAL)
    718
    719              else:
    720                  self.helpMenu.entryconfig(0, state = DISABLED)
    721                  if PREFERENCES.warning_acroread.lower() == 'yes':
    722                      q = askyesno(title = 'Warning', message = 'The path to acrobat reader has not been set.\n\n\
    723  It will not be possible to read the documentation in PDF format.\n\
    724  You should set that path in the "Preferences"-->"acroread_path" section.\n\n\
    725  Show this warning next time nMOLDYN starts ?')
    726                      if q:
    727                          setattr(PREFERENCES, 'warning_acroread', 'yes')
    728                      else:
    729                          setattr(PREFERENCES, 'warning_acroread', 'no')
    730
    731          if os.path.exists(PREFERENCES.vmd_path):
    732              self.viewMenu.entryconfig(1, state = NORMAL)
    733              self.viewMenu.entryconfig(2, state = NORMAL)
    734
    735          else:
    736              self.viewMenu.entryconfig(1, state = DISABLED)
    737              self.viewMenu.entryconfig(2, state = DISABLED)
    738              if PREFERENCES.warning_vmd.lower() == 'yes':
    739                  q = askyesno(title = 'Warning', message = 'The path to "vmd" program has not been set.\n\n\
    740  It will not be possible to visualize any trajectory in nMOLDYN.\n\
    741  You should set that path in the "Preferences"-->"vmd_path" section.\n\n\
    742  Show this warning next time nMOLDYN starts ?')
    743                  if q:
    744                      setattr(PREFERENCES, 'warning_vmd', 'yes')
    745                  else:
    746                      setattr(PREFERENCES, 'warning_vmd', 'no')
    747
    748 -    def setPreferences(self, event = None):
    749          """
    750          This method pops up a dialog from where the user can edit the nMOLDYN configuration file.
    751          """
    752
    753          PreferencesDialog(self, 'Preferences')
    754
    755          self.checkConfiguration()
    756
    757 -    def analysisDialog(self, analysis):
    758          """
    759          """
    760
    761          AnalysisDialog(self, analysis, self.netcdf)
    762
    763 -    def plotNetCDF(self, event = None):
    764          """
    765          This method pops up a dialog from where the user can display any numeric 2D or 3D NetCDF variables.
    766          """
    767
    768          PlotNetCDFVariableDialog(self, 'Plot NetCDF', self.netcdf)
    769
    770 -    def animateTrajectory(self, event = None):
    771          """
    772          This method pops up a dialog from where the user can animate a trajectory. If a trajectory has been loaded
    773          for analysis this will be the default one. Otherwise the user can still browse one from the dialog.
    774          The animation requires VMD.
    775          """
    776
    777          AnimationDialog(self, 'Trajectory animation', self.netcdf)
    778
    779 -    def viewEffectiveMode(self, event = None):
    780          """
    781          This method pops up a dialog from where the user can animate an effective mode coming from a QHA
    782          analysis.
    783          The animation require VMD.
    784          """
    785
    786          ViewEffectiveModeDialog(self, 'Effective mode viewer')
    787
    788 -    def traceAnalysis(self, event = None):
    789          """
    790          This method pops up a dialog from where the user can check the march of the running jobs.
    791          The dialog can be updated dynamically by pressing its button 'refresh'.
    792          """
    793
    794          CheckJobsStatusDialog(self, 'Job progress')
    795
    796 -    def analysisBenchmark(self):
    797          """
    798          This method pops up a dialog from where the user can perform some analysis benchmark. The benchmark is
    799          done between the current version and a reference version that is the version 2.2.5 the last official release
    800          of nMOLDYN.
    801          """
    802
    803          AnalysisBenchmarkDialog(self, 'Analysis benchmark')
    804
    805 -    def displayDocumentation(self, event = None):
    806          """
    807          This methode opens the nMOLDYN pdf users guide.
    808          The users guide was written by E. Pellegrini, V. Calandrini, P. Calligari, K. Hinsen and G.R. Kneller.
    809          """
    810
    811          if PREFERENCES.documentation_style.lower() == 'html':
    812              webbrowser.open(os.path.join(nmoldyn_package_path, 'Doc', 'UsersGuide', 'HTML', 'index.html'))
    813
    814          elif PREFERENCES.documentation_style.lower() == 'pdf':
    815              # The name of the users guide pdf file.
    816              pdfFile = os.path.join(nmoldyn_package_path, 'Doc', 'UsersGuide', 'PDF', 'nMOLDYN_ug.pdf')
    817
    818              # Case of Win32
    819              if sys.platform == 'win32':
    820                  os.startfile(pdfFile)
    821
    822              # Case of MacOS
    823              elif sys.platform == 'darwin':
    824                  subprocess.call(['open', pdfFile])
    825
    826              else:
    827
    828                  try:
    829                      subprocess.call([PREFERENCES.acroread_path, pdfFile])
    830                  except:
    831                      raise Error('Unable to read the file %s\nwith the acrobat reader located in %s.' % (pdfFile, PREFERENCES.acroread_path))
    832          else:
    833              raise Error('Unknow documentation format: %s.' % PREFERENCES.documentations_style)
    834
    835 -    def displayMailingList(self):
    836          """
    837          This methode opens the nMOLDYN mailing list.
    838          """
    839
    840          webbrowser.open('http://sourcesup.cru.fr/forum/?group_id=194')
    841
    842 -    def displayAPI(self, event = None):
    843
    844          if PREFERENCES.documentation_style.lower() == 'html':
    845              webbrowser.open(os.path.join(nmoldyn_package_path, 'Doc', 'API', 'HTML', 'index.html'))
    846
    847          elif PREFERENCES.documentation_style.lower() == 'pdf':
    848              # The name of the users guide pdf file.
    849              pdfFile = os.path.join(nmoldyn_package_path, 'Doc', 'API', 'PDF', 'api.pdf')
    850
    851              # Case of Win32
    852              if sys.platform == 'win32':
    853                  os.startfile(pdfFile)
    854
    855              # Case of MacOS
    856              elif sys.platform == 'darwin':
    857                  subprocess.call(['open', pdfFile])
    858
    859              else:
    860
    861                  try:
    862                      subprocess.call([PREFERENCES.acroread_path, pdfFile])
    863                  except:
    864                      raise Error('Unable to read the file %s\nwith the acrobat reader located in %s.' % (pdfFile, PREFERENCES.acroread_path))
    865
    866          else:
    867              raise Error('Unknow documentation format: %s.' % PREFERENCES.documentations_style)
    868
    869 -    def aboutNMOLDYN(self, event = None):
    870          """
    871          This method displays general informations about the program such as the developper, the main versions ...
    872          """
    873
    874          GeneralInformationsDialog(self, 'About nMOLDYN')
    875