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