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

# Source Code for Module nMOLDYN.GUI.PlotNetCDFVariableDialog

       1  """This modules implements I{View-->Plot} dialog.
       2
       3  Classes:
       4      * SettingsDialog: sets up the settings dialog.
       5      * ASCIIToNetCDFConversionDialog: creates I{View-->Plot} dialog used to plot NetCDF variables.
       6  """
       7
       8  # The python distribution modules
       9  import os
      10  import sys
      11
      12  # The matplotlib modules
      13  import pylab
      14  import matplotlib
      15  from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
      16  from matplotlib.colors import ColorConverter, LogNorm, Normalize, rgb2hex
      17  from matplotlib.figure import Figure
      18  from matplotlib.widgets import Cursor
      19
      20  # The Tcl/Tk modules
      21  import _tkinter
      22  from tkFileDialog import askopenfilename, asksaveasfilename
      23  from Tkinter import *
      24  import tkColorChooser
      25
      26  # The ScientificPython modules
      27  from Scientific.IO.NetCDF import _NetCDFFile, NetCDFFile
      28  from Scientific import N as Num
      29
      30  # The MMTK modules.
      31  from MMTK.Trajectory import Trajectory
      32
      33  # The nMOLDYN modules
      34  from nMOLDYN.Core.Error import Error
      35  from nMOLDYN.Core.Logger import LogMessage
      36  from nMOLDYN.Core.Preferences import PREFERENCES
      37  from nMOLDYN.GUI.Widgets import *
      38
      39  # Some modules variables
      40  # The tuple of the possible interpolations.
      41  interpolations = ['bessel', 'bilinear', 'bicubic', 'blackman', 'catrom', 'hamming', 'hermite', 'hanning',\
      42                    'gaussian', 'kaiser', 'lanczos', 'mitchell', 'nearest', 'spline16', 'spline36', 'quadric', 'sinc']
      43
      44  # The tuple of the possible color maps.
      45  colorMaps = ['autumn', 'bone', 'cool', 'copper', 'flag', 'gray', 'hot', 'hsv', 'jet', 'pink', 'prism',\
      46               'spring', 'summer', 'winter', 'spectral']
      47
      48  # The tuple of the possible line styles.
      49  lineStyles = ['-', '--', '-.', ':', 'None']
      50
      51  # The tuple of the possible marker styles.
      52  markerStyles = ['+', '.', '<', '>', 'o', 'p', 's', 'v', 'x', '|', 'None']
      53
      54  axisScales = ['linear', 'log']
      55
      56 -class SettingsDialog(Toplevel):
      57      """Sets up a dialog tp perform some settings on the plots.
      58      """
      59
      60 -    def __init__(self, parent):
      61          """The constructor.
      62
      63          @param parent: the parent widget.
      64          """
      65
      66          Toplevel.__init__(self, parent)
      67          self.transient(parent)
      68
      69          self.parent = parent
      70
      71          self.widgets = {}
      72          self.settings = {}
      73
      74          self.selectedPlot = None
      75
      76          body = Frame(self)
      77          self.initial_focus = self.body(body)
      78          body.grid(row = 0, column = 0, sticky = EW)
      79
      80          self.buttonbox()
      81
      82          self.grab_set()
      83
      84          if not self.initial_focus:
      85              self.initial_focus = self
      86
      87          self.protocol("WM_DELETE_WINDOW", self.cancel)
      88
      89          self.resizable(width = NO, height = NO)
      90
      91          self.title = 'Settings'
      92
      93          self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))
      94
      95          self.initial_focus.focus_set()
      96
      97          self.wait_window(self)
      98
      99 -    def body(self, master):
     100          """
     101          Create dialog body. Return widget that should have initial focus.
     102          """
     103
     104          settingsFrame = LabelFrame(master, text = 'Settings', bd = 2, relief = GROOVE)
     105          settingsFrame.grid(row = 0, column = 0, sticky = EW, padx = 3, pady = 3)
     106          settingsFrame.grid_columnconfigure(0, weight = 1)
     107          settingsFrame.grid_rowconfigure(0, weight = 1)
     108
     109          self.gFrame = LabelFrame(settingsFrame, text = 'Global', bd = 2, relief = GROOVE)
     110          self.gFrame.grid(row = 0, column = 0, sticky = "NEW", padx = 3, pady = 3)
     111          self.gFrame.grid_columnconfigure(0, weight = 1)
     112          self.gFrame.grid_rowconfigure(0, weight = 1)
     113
     114          # All the attributes concerning the global and plot settings are set.
     115          self.initGlobalSettings()
     116
     117          # The widget to change plot title.
     118          self.widgets['plotTitle'] = ComboStringEntry(self.gFrame,frameLabel = 'Plot title', contents = self.settings['plotTitle'])
     119          self.widgets['plotTitle'].grid(row = 1, column = 0, sticky = "NEW")
     120          self.widgets['plotTitle'].grid_columnconfigure(0, weight = 1)
     121          self.widgets['plotTitle'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'plotTitle'))
     122
     123          # The widget to change X-axis label.
     124          self.widgets['xLabel'] = ComboStringEntry(self.gFrame,frameLabel = 'X label', contents = self.settings['xLabel'])
     125          self.widgets['xLabel'].grid(row = 2, column = 0, sticky = "NEW")
     126          self.widgets['xLabel'].grid_columnconfigure(0, weight = 1)
     127          self.widgets['xLabel'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'xLabel'))
     128
     129          # The widget to change Y-axis label.
     130          self.widgets['yLabel'] = ComboStringEntry(self.gFrame,frameLabel = 'Y label', contents = self.settings['yLabel'])
     131          self.widgets['yLabel'].grid(row = 3, column = 0, sticky = "NEW")
     132          self.widgets['yLabel'].grid_columnconfigure(0, weight = 1)
     133          self.widgets['yLabel'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'yLabel'))
     134
     135          # The widget to change X-axis ticks contents.
     136          self.widgets['xTicks'] = ComboStringEntry(self.gFrame,frameLabel = 'X ticks', contents = self.settings['xTicks'])
     137          self.widgets['xTicks'].grid(row = 4, column = 0, sticky = "NEW")
     138          self.widgets['xTicks'].grid_columnconfigure(0, weight = 1)
     139          self.widgets['xTicks'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'xTicks'))
     140
     141          # The widget to change X-axis ticks size.
     142          self.widgets['xTicksSize'] = ComboIntegerEntry(self.gFrame, frameLabel = 'X ticks size', contents = self.settings['xTicksSize'])
     143          self.widgets['xTicksSize'].grid(row = 5, column = 0, sticky = "NEW")
     144          self.widgets['xTicksSize'].grid_columnconfigure(0, weight = 1)
     145          self.widgets['xTicksSize'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'xTicksSize'))
     146
     147          # The widget to change X-axis ticks angle.
     148          self.widgets['xTicksAngle'] = ComboIntegerEntry(self.gFrame, frameLabel = 'X ticks angle', contents = self.settings['xTicksAngle'])
     149          self.widgets['xTicksAngle'].grid(row = 6, column = 0, sticky = "NEW")
     150          self.widgets['xTicksAngle'].grid_columnconfigure(0, weight = 1)
     151          self.widgets['xTicksAngle'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'xTicksAngle'))
     152
     153          # The widget to change Y-axis ticks contents.
     154          self.widgets['yTicks'] = ComboStringEntry(self.gFrame,frameLabel = 'Y ticks', contents = self.settings['yTicks'])
     155          self.widgets['yTicks'].grid(row = 7, column = 0, sticky = "NEW")
     156          self.widgets['yTicks'].grid_columnconfigure(0, weight = 1)
     157          self.widgets['yTicks'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'yTicks'))
     158
     159          # The widget to change Y-axis ticks size.
     160          self.widgets['yTicksSize'] = ComboIntegerEntry(self.gFrame, frameLabel = 'Y ticks size', contents = self.settings['yTicksSize'])
     161          self.widgets['yTicksSize'].grid(row = 8, column = 0, sticky = "NEW")
     162          self.widgets['yTicksSize'].grid_columnconfigure(0, weight = 1)
     163          self.widgets['yTicksSize'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'yTicksSize'))
     164
     165          # The widget to change Y-axis ticks angle.
     166          self.widgets['yTicksAngle'] = ComboIntegerEntry(self.gFrame, frameLabel = 'Y ticks angle', contents = self.settings['yTicksAngle'])
     167          self.widgets['yTicksAngle'].grid(row = 9, column = 0, sticky = "NEW")
     168          self.widgets['yTicksAngle'].grid_columnconfigure(0, weight = 1)
     169          self.widgets['yTicksAngle'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'yTicksAngle'))
     170
     171          # The widget to change X-axis range.
     172          self.widgets['xRange'] = ComboStringEntry(self.gFrame,frameLabel = 'X range', contents = self.settings['xRange'])
     173          self.widgets['xRange'].grid(row = 10, column = 0, sticky = "NEW")
     174          self.widgets['xRange'].grid_columnconfigure(0, weight = 1)
     175          self.widgets['xRange'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'xRange'))
     176
     177          # The widget to change Y-axis range.
     178          self.widgets['yRange'] = ComboStringEntry(self.gFrame,frameLabel = 'Y range', contents = self.settings['yRange'])
     179          self.widgets['yRange'].grid(row = 11, column = 0, sticky = "NEW")
     180          self.widgets['yRange'].grid_columnconfigure(0, weight = 1)
     181          self.widgets['yRange'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'yRange'))
     182
     183          if len(self.parent.figure.gca().get_images()) == 0:
     184              v = axisScales.index(self.settings['xScale'])
     185              # The widget to change X-axis scale.
     186              self.widgets['xScale'] = ComboRadiobutton(self.gFrame,\
     187                                                        frameLabel = 'X scale',\
     188                                                        contents = axisScales,\
     189                                                        default = v,\
     190                                                        layout = (1,2))
     191              self.widgets['xScale'].grid(row = 12, column = 0, sticky = "NEW")
     192              self.widgets['xScale'].grid_columnconfigure(0, weight = 1)
     193              for r in self.widgets['xScale'].radio:
     194                  r.bind('<ButtonRelease-1>', lambda event : self.changeSettings(event, 'xScale'))
     195
     196              v = axisScales.index(self.settings['yScale'])
     197              # The widget to change Y-axis scale.
     198              self.widgets['yScale'] = ComboRadiobutton(self.gFrame,\
     199                                                        frameLabel = 'Y scale',\
     200                                                        contents = axisScales,\
     201                                                        default = v,\
     202                                                        layout = (1,2))
     203              self.widgets['yScale'].grid(row = 13, column = 0, sticky = "NEW")
     204              self.widgets['yScale'].grid_columnconfigure(0, weight = 1)
     205              for r in self.widgets['yScale'].radio:
     206                  r.bind('<ButtonRelease-1>', lambda event : self.changeSettings(event, 'yScale'))
     207
     208          else:
     209              v = axisScales.index(self.settings['zScale'])
     210              # The widget to change Z-axis scale.
     211              self.widgets['zScale'] = ComboRadiobutton(self.gFrame,\
     212                                                        frameLabel = 'Z scale',\
     213                                                        contents = axisScales,\
     214                                                        default = v,\
     215                                                        layout = (1,2))
     216              self.widgets['zScale'].grid(row = 12, column = 0, sticky = "NEW")
     217              self.widgets['zScale'].grid_columnconfigure(0, weight = 1)
     218              for r in self.widgets['zScale'].radio:
     219                  r.bind('<ButtonRelease-1>', lambda event : self.changeSettings(event, 'zScale'))
     220
     221          # The widget to change grid line width.
     222          self.widgets['gridWidth'] = ComboFloatEntry(self.gFrame, frameLabel = 'Grid width', contents = self.settings['gridWidth'])
     223          self.widgets['gridWidth'].grid(row = 15, column = 0, sticky = "NEW")
     224          self.widgets['gridWidth'].grid_columnconfigure(0, weight = 1)
     225          self.widgets['gridWidth'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'gridWidth'))
     226
     227          # The widget to change grid style.
     228          self.widgets['gridStyle'] = ComboSpinbox(self.gFrame, frameLabel = 'Grid style', default = self.settings['gridStyle'], contents = tuple(lineStyles))
     229          self.widgets['gridStyle'].grid(row = 16, column = 0, sticky = "NEW")
     230          self.widgets['gridStyle'].grid_columnconfigure(0, weight = 1)
     231          self.widgets['gridStyle'].spinbox.config({'width' : 10, 'command' : lambda : self.changeSettings(None, 'gridStyle')})
     232
     233          # The button to change grid color.
     234          self.widgets['gridColor'] = ComboButton(self.gFrame,\
     235                                                  frameLabel = 'Grid color')
     236          self.widgets['gridColor'].grid(row = 17, column = 0, sticky = "NEW")
     237          self.widgets['gridColor'].grid_columnconfigure(0, weight = 1)
     238          self.widgets['gridColor'].button.config({'bg' : self.settings['gridColor'],\
     239                                                   'activebackground' : self.settings['gridColor'],\
     240                                                   'width' : 1,\
     241                                                   'command' : lambda : self.changeSettings(None, 'gridColor')})
     242          self.widgets['gridColor'].results = self.settings['gridColor']
     243
     244          self.pFrame = LabelFrame(settingsFrame, text = 'Plot', bd = 2, relief = GROOVE)
     245          self.pFrame.grid(row = 0, column = 1, sticky = "NEW", padx = 3, pady = 3)
     246          self.pFrame.grid_columnconfigure(1, weight = 1)
     247          self.pFrame.grid_rowconfigure(0, weight = 1)
     248
     249          # The listbox that will contain the plot list.
     250          self.plots = ComboListbox(self.pFrame, frameLabel = 'Available plots', contents = [])
     251          self.plots.grid(row = 0, column = 0, sticky = "NEW")
     252          self.plots.grid_columnconfigure(0, weight = 1)
     253          self.plots.grid_rowconfigure(0, weight = 1)
     254          self.plots.lb.config({'exportselection' : 0, 'width' : 28, 'height' : 4, 'selectmode' : SINGLE})
     255          self.plots.lb.bind('<ButtonRelease-1>', self.addPlotSettingsWidgets)
     256
     257          if self.parent.figure.gca().get_images():
     258              self.plots.lb.insert(END, self.parent.figure.gca().get_images()[0].get_label())
     259
     260          else:
     261              for f in self.parent.figure.gca().get_lines():
     262                  self.plots.lb.insert(END, f.get_label())
     263
     264          self.plots.lb.selection_set(0)
     265
     266          self.addPlotSettingsWidgets()
     267
     268          return None
     269
     270 -    def buttonbox(self):
     271          """
     272          Add standard button box.
     273          """
     274
     275          # The frame that contains the 'Cancel' and 'OK' buttons.
     276          box = LabelFrame(self, text = 'Actions', bd = 2, relief = GROOVE)
     277          box.grid(row = 1, column = 0, sticky = EW, padx = 3, pady = 3)
     278          box.grid_columnconfigure(0, weight = 1)
     279
     280          w = Button(box, text = "Cancel", width = 10, command = self.cancel)
     281          w.grid(row = 0, column = 0, sticky = E)
     282
     283          w = Button(box, text = "OK", width = 10, command = self.ok, default = ACTIVE)
     284          w.grid(row = 0, column = 4, sticky = E)
     285
     286          self.bind("<Return>", self.ok)
     287          self.bind("<Escape>", self.cancel)
     288
     289      # Standard button semantics.
     290 -    def ok(self, event = None):
     291
     292          if not self.validate():
     293              self.initial_focus.focus_set()
     294              return
     295
     296          self.withdraw()
     297          self.update_idletasks()
     298
     299          self.apply()
     300
     301          self.cancel()
     302
     303 -    def cancel(self, event=None):
     304
     305          # Put focus back to the parent window
     306          self.parent.grab_set()
     307          self.parent.focus_set()
     308          self.destroy()
     309
     310      # Command hooks
     311 -    def validate(self):
     312
     313          try:
     314              self.storeSettings()
     315
     316          except:
     317              LogMessage('warning','Bad input. Please try again.',['gui'])
     318              return False
     319
     320          return True
     321
     322 -    def apply(self):
     323          try:
     324              for k, v in self.settings.items():
     325                  if k == 'plotTitle':
     326                      self.parent.figure.gca().set_title(v)
     327                  elif k == 'xRange':
     328                      xMin, xMax = [float(vv) for vv in v.split('-')]
     329                      self.parent.figure.gca().set_xlim((xMin, xMax))
     330                  elif k == 'yRange':
     331                      yMin, yMax = [float(vv) for vv in v.split('-')]
     332                      self.parent.figure.gca().set_ylim((yMin,yMax))
     333                  elif k == 'xLabel':
     334                      self.parent.figure.gca().set_xlabel(v)
     335                  elif k == 'yLabel':
     336                      self.parent.figure.gca().set_ylabel(v)
     337                  elif k == 'xScale':
     338                      self.parent.figure.gca().set_xscale(v)
     339                  elif k == 'yScale':
     340                      self.parent.figure.gca().set_yscale(v)
     341                  elif k == 'zScale':
     342                      if v == 'linear':
     343                          self.parent.figure.gca().get_images()[self.selectedPlot].set_norm(Normalize())
     344                      else:
     345                          self.parent.figure.gca().get_images()[self.selectedPlot].set_norm(LogNorm())
     346                  elif k == 'xTicks':
     347                      ticks, ticksLabels = [eval(t) for t in v.split(';')]
     348                      ticks = [float(t) for t in ticks]
     349                      ticksLabels = [str(t) for t in ticksLabels]
     350
     351                      self.parent.figure.gca().set_xticks(ticks)
     352                      self.parent.figure.gca().set_xticklabels(ticksLabels)
     353                  elif k == 'xTicksSize':
     354                      [l.set_size(int(v)) for l in self.parent.figure.gca().get_xticklabels()]
     355                  elif k == 'xTicksAngle':
     356                      [l.set_rotation(int(v)) for l in self.parent.figure.gca().get_xticklabels()]
     357                  elif k == 'yTicks':
     358                      ticks, ticksLabels = [eval(t) for t in v.split(';')]
     359                      ticks = [float(t) for t in ticks]
     360                      ticksLabels = [str(t) for t in ticksLabels]
     361
     362                      self.parent.figure.gca().set_yticks(ticks)
     363                      self.parent.figure.gca().set_yticklabels(ticksLabels)
     364                  elif k == 'yTicksSize':
     365                      [l.set_size(int(v)) for l in self.parent.figure.gca().get_yticklabels()]
     366                  elif k == 'yTicksAngle':
     367                      [l.set_rotation(int(v)) for l in self.parent.figure.gca().get_yticklabels()]
     368                  elif k == 'gridWidth':
     369                      self.parent.figure.gca().grid(True, linewidth = v)
     370                  elif k == 'gridStyle':
     371                      self.parent.figure.gca().grid(True, linestyle = v)
     372                  elif k == 'gridColor':
     373                      self.parent.figure.gca().grid(True, color = v)
     374                  elif isinstance(k, int):
     375                      for kk, vv in v.items():
     376                          if kk == 'lineLabel':
     377                              self.parent.figure.gca().get_lines()[k].set_label(vv)
     378                              self.plots.lb.delete(k)
     379                              self.plots.lb.insert(k, vv)
     380                          elif kk == 'lineWidth':
     381                              self.parent.figure.gca().get_lines()[k].set_linewidth(vv)
     382                          elif kk == 'lineStyle':
     383                              self.parent.figure.gca().get_lines()[k].set_linestyle(vv)
     384                          elif kk == 'lineColor':
     385                              self.parent.figure.gca().get_lines()[k].set_color(vv)
     386                          elif kk == 'markerSize':
     387                              self.parent.figure.gca().get_lines()[k].set_markersize(vv)
     388                          elif kk == 'markerStyle':
     389                              self.parent.figure.gca().get_lines()[k].set_marker(vv)
     390                          elif kk == 'markerColor':
     391                              self.parent.figure.gca().get_lines()[k].set_markeredgecolor(vv)
     392                          elif kk == 'colorMap':
     393                              colorMap = eval('pylab.cm.' + vv)
     394                              self.parent.figure.gca().get_images()[k].set_cmap(colorMap)
     395                          elif kk == 'interpolation':
     396                              self.parent.figure.gca().get_images()[k].set_interpolation(vv)
     397                          elif kk == 'alpha':
     398                              self.parent.figure.gca().get_images()[k].set_alpha(vv)
     399
     400              self.parent.figure.gca().legend(loc = 'best')
     401              self.parent.canvas.show()
     402
     403          except:
     404              raise Error('Error when updating plots.')
     405
     406 -    def initGlobalSettings(self):
     407
     408          self.settings['plotTitle'] = self.parent.figure.gca().title.get_text()
     409          self.settings['xRange'] = '-'.join([str(v) for v in self.parent.figure.gca().get_xlim()])
     410          self.settings['yRange'] = '-'.join([str(v) for v in self.parent.figure.gca().get_ylim()])
     411          self.settings['xLabel'] = self.parent.figure.gca().xaxis.get_label().get_text()
     412          self.settings['yLabel'] = self.parent.figure.gca().yaxis.get_label().get_text()
     413
     414          # The selected plot is a 2D plot.
     415          if len(self.parent.figure.gca().get_images()) == 0:
     416              self.settings['xScale'] = self.parent.figure.gca().get_xscale()
     417              self.settings['yScale'] = self.parent.figure.gca().get_yscale()
     418          else:
     419              self.settings['xScale'] = 'linear'
     420              self.settings['yScale'] = 'linear'
     421              self.settings['zScale'] = 'linear'
     422
     423          self.settings['xTicks'] = str(list(self.parent.figure.gca().get_xticks()))
     424          self.settings['xTicks'] += ';' + str([v.get_text() for v in self.parent.figure.gca().get_xticklabels()])
     425          self.settings['xTicksSize'] = int(self.parent.figure.gca().get_xticklabels()[0].get_size())
     426          self.settings['xTicksAngle'] = int(self.parent.figure.gca().get_xticklabels()[0].get_rotation())
     427          self.settings['yTicks'] = str(list(self.parent.figure.gca().get_yticks()))
     428          self.settings['yTicks'] += ';' + str([v.get_text() for v in self.parent.figure.gca().get_yticklabels()])
     429          self.settings['yTicksSize'] = int(self.parent.figure.gca().get_yticklabels()[0].get_size())
     430          self.settings['yTicksAngle'] = int(self.parent.figure.gca().get_yticklabels()[0].get_rotation())
     431          self.settings['gridWidth'] = float(self.parent.figure.gca().get_xgridlines()[0].get_linewidth())
     432          self.settings['gridStyle'] = lineStyles.index(self.parent.figure.gca().get_xgridlines()[0].get_linestyle())
     433          self.settings['gridColor'] = rgb2hex(ColorConverter().to_rgb(self.parent.figure.gca().get_xgridlines()[0].get_color()))
     434
     435 -    def initPlotSettings(self):
     436
     437          if self.selectedPlot is None:
     438              return
     439
     440          else:
     441              self.settings[self.selectedPlot] = {}
     442
     443          # The selected plot is a 2D plot.
     444          if len(self.parent.figure.gca().get_images()) == 0:
     445              # The instance of the plot to set up.
     446              plot = self.parent.figure.get_axes()[0].get_lines()[self.selectedPlot]
     447
     448              self.settings[self.selectedPlot]['lineLabel'] = plot.get_label()
     449              self.settings[self.selectedPlot]['lineWidth'] = int(float(plot.get_linewidth()))
     450
     451              self.settings[self.selectedPlot]['lineStyle'] = plot.get_linestyle()
     452              self.settings[self.selectedPlot]['lineColor'] = rgb2hex(ColorConverter().to_rgb(plot.get_color()))
     453
     454              self.settings[self.selectedPlot]['markerStyle'] = plot.get_marker()
     455              self.settings[self.selectedPlot]['markerSize'] = int(plot.get_markersize())
     456              self.settings[self.selectedPlot]['markerColor'] = rgb2hex(ColorConverter().to_rgb(plot.get_markeredgecolor()))
     457
     458          else:
     459              # The instance of the plot to set up.
     460              plot = self.parent.figure.gca().get_images()[self.selectedPlot]
     461
     462              self.settings[self.selectedPlot]['colorMap'] = plot.cmap.name
     463              self.settings[self.selectedPlot]['interpolation'] = plot._interpolation
     464              self.settings[self.selectedPlot]['alpha'] = plot.get_alpha()
     465
     466 -    def storeSettings(self):
     467
     468          self.settings['plotTitle'] = self.widgets['plotTitle'].getValue()
     469          self.settings['xRange'] = self.widgets['xRange'].getValue()
     470          self.settings['yRange'] = self.widgets['yRange'].getValue()
     471          self.settings['xLabel'] = self.widgets['xLabel'].getValue()
     472          self.settings['yLabel'] = self.widgets['yLabel'].getValue()
     473
     474          self.settings['xTicks'] = self.widgets['xTicks'].getValue()
     475          self.settings['xTicksSize'] = self.widgets['xTicksSize'].getValue()
     476          self.settings['xTicksAngle'] = self.widgets['xTicksAngle'].getValue()
     477          self.settings['yTicks'] = self.widgets['yTicks'].getValue()
     478          self.settings['yTicksSize'] = self.widgets['yTicksSize'].getValue()
     479          self.settings['yTicksAngle'] = self.widgets['yTicksAngle'].getValue()
     480
     481          self.settings['gridWidth'] = self.widgets['gridWidth'].getValue()
     482          self.settings['gridStyle'] = self.widgets['gridStyle'].getValue()
     483          self.settings['gridColor'] = self.widgets['gridColor'].getValue()
     484
     485          # The selected plot is a 2D plot.
     486          if len(self.parent.figure.gca().get_images()) == 0:
     487              self.settings['xScale'] = self.widgets['xScale'].getValue()
     488              self.settings['yScale'] = self.widgets['yScale'].getValue()
     489
     490              self.settings[self.selectedPlot]['lineLabel'] = self.widgets['lineLabel'].getValue()
     491              self.settings[self.selectedPlot]['lineWidth'] = self.widgets['lineWidth'].getValue()
     492              self.settings[self.selectedPlot]['lineStyle'] = self.widgets['lineStyle'].getValue()
     493              self.settings[self.selectedPlot]['lineColor'] = self.widgets['lineColor'].getValue()
     494
     495              self.settings[self.selectedPlot]['markerStyle'] = self.widgets['markerStyle'].getValue()
     496              self.settings[self.selectedPlot]['markerSize'] = self.widgets['markerSize'].getValue()
     497              self.settings[self.selectedPlot]['markerColor'] = self.widgets['markerColor'].getValue()
     498
     499          else:
     500              self.settings['xScale'] = 'linear'
     501              self.settings['yScale'] = 'linear'
     502              self.settings['zScale'] = self.widgets['zScale'].getValue()
     503
     504              self.settings[self.selectedPlot]['colorMap'] = self.widgets['colorMap'].getValue()
     505              self.settings[self.selectedPlot]['interpolation'] = self.widgets['interpolation'].getValue()
     506              self.settings[self.selectedPlot]['alpha'] = self.widgets['alpha'].getValue()
     507
     508 -    def selectColor(self, widget):
     509
     510          defaultColor = widget.button.cget('bg')
     511
     512          junk, selectedColor = tkColorChooser.askcolor(color = defaultColor, master = self)
     513          if isinstance(selectedColor, _tkinter.Tcl_Obj):
     514              selectedColor = selectedColor.string
     515
     516          elif isinstance(selectedColor, str):
     517              pass
     518
     519          else:
     520              selectedColor = defaultColor
     521
     522          widget.button.configure(bg = selectedColor, activebackground = selectedColor)
     523          widget.results = selectedColor
     524
     525          return selectedColor
     526
     527 -    def changeSettings(self, event, widgetName):
     528          """
     529          Argument:
     530              - event: either a Tkinter event, either a Tkinter control variable value that has been traced for changes.
     531          """
     532
     533          if not self.widgets.has_key(widgetName):
     534              return
     535
     536          w = self.widgets[widgetName]
     537          if widgetName == 'plotTitle':
     538              self.parent.figure.gca().set_title(w.getValue())
     539          elif widgetName == 'xRange':
     540              xMin, xMax = [float(v) for v in w.getValue().split('-')]
     541              self.parent.figure.gca().set_xlim((xMin,xMax))
     542          elif widgetName == 'yRange':
     543              yMin, yMax = [float(v) for v in w.getValue().split('-')]
     544              self.parent.figure.gca().set_ylim((yMin,yMax))
     545          elif widgetName == 'xLabel':
     546              self.parent.figure.gca().set_xlabel(w.getValue())
     547          elif widgetName == 'yLabel':
     548              self.parent.figure.gca().set_ylabel(w.getValue())
     549          elif widgetName == 'xScale':
     550              self.parent.figure.gca().set_xscale(w.getValue())
     551          elif widgetName == 'yScale':
     552              self.parent.figure.gca().set_yscale(w.getValue())
     553          elif widgetName == 'zScale':
     554              if w.getValue() == 'linear':
     555                  self.parent.figure.gca().get_images()[self.selectedPlot].set_norm(Normalize())
     556              else:
     557                  self.parent.figure.gca().get_images()[self.selectedPlot].set_norm(LogNorm())
     558          elif widgetName == 'gridWidth':
     559              self.parent.figure.gca().grid(True, linewidth = w.getValue())
     560          elif widgetName == 'gridStyle':
     561              self.parent.figure.gca().grid(True, linestyle = w.getValue())
     562          elif widgetName == 'gridColor':
     563              color = self.selectColor(w)
     564              self.parent.figure.gca().grid(True, color = color)
     565          elif widgetName == 'xTicks':
     566              ticks, ticksLabels = [eval(t) for t in w.getValue().split(';')]
     567              ticks = [float(t) for t in ticks]
     568              ticksLabels = [str(t) for t in ticksLabels]
     569              self.parent.figure.gca().set_xticks(ticks)
     570              self.parent.figure.gca().set_xticklabels(ticksLabels)
     571          elif widgetName == 'xTicksSize':
     572              [l.set_size(int(w.getValue())) for l in self.parent.figure.gca().get_xticklabels()]
     573          elif widgetName == 'xTicksAngle':
     574              [l.set_rotation(int(w.getValue())) for l in self.parent.figure.gca().get_xticklabels()]
     575          elif widgetName == 'yTicks':
     576              ticks, ticksLabels = [eval(t) for t in w.getValue().split(';')]
     577              ticks = [float(t) for t in ticks]
     578              ticksLabels = [str(t) for t in ticksLabels]
     579              self.parent.figure.gca().set_yticks(ticks)
     580              self.parent.figure.gca().set_yticklabels(ticksLabels)
     581          elif widgetName == 'yTicksSize':
     582              [l.set_size(int(w.getValue())) for l in self.parent.figure.gca().get_yticklabels()]
     583          elif widgetName == 'yTicksAngle':
     584              [l.set_rotation(int(w.getValue())) for l in self.parent.figure.gca().get_yticklabels()]
     585          elif widgetName == 'lineLabel':
     586              self.parent.figure.gca().get_lines()[self.selectedPlot].set_label(w.getValue())
     587              self.plots.lb.delete(self.selectedPlot)
     588              self.plots.lb.insert(self.selectedPlot, w.getValue())
     589          elif widgetName == 'lineWidth':
     590              self.parent.figure.gca().get_lines()[self.selectedPlot].set_linewidth(w.getValue())
     591          elif widgetName == 'lineStyle':
     592              self.parent.figure.gca().get_lines()[self.selectedPlot].set_linestyle(w.getValue())
     593          elif widgetName == 'lineColor':
     594              color = self.selectColor(w)
     595              self.parent.figure.gca().get_lines()[self.selectedPlot].set_color(color)
     596          elif widgetName == 'markerSize':
     597              self.parent.figure.gca().get_lines()[self.selectedPlot].set_markersize(w.getValue())
     598          elif widgetName == 'markerStyle':
     599              self.parent.figure.gca().get_lines()[self.selectedPlot].set_marker(w.getValue())
     600          elif widgetName == 'markerColor':
     601              color = self.selectColor(w)
     602              self.parent.figure.gca().get_lines()[self.selectedPlot].set_markeredgecolor(color)
     603          elif widgetName == 'alpha':
     604              self.parent.figure.gca().get_images()[self.selectedPlot].set_alpha(w.getValue())
     605          elif widgetName == 'colorMap':
     606              colorMap = eval('pylab.cm.' + w.getValue())
     607              self.parent.figure.gca().get_images()[self.selectedPlot].set_cmap(colorMap)
     608          elif widgetName == 'interpolation':
     609              self.parent.figure.gca().get_images()[self.selectedPlot].set_interpolation(w.getValue())
     610
     611          self.parent.figure.gca().legend(loc = 'best')
     612          self.parent.canvas.show()
     613
     614 -    def removePreviousPlotSettings(self):
     615          """
     616          This method removes the previous plot settings widgets.
     617          """
     618
     619          # Loop over all the plot settings widgets.
     620          for widget in self.pFrame.winfo_children():
     621              # Keep the plot settings list box
     622              if widget == self.plots:
     623                  continue
     624
     625              # Destroys all the other ones.
     626              else:
     627                  widget.destroy()
     628
     629 -    def addPlotSettingsWidgets(self, event = None):
     630
     631          if event is not None:
     632              if not event.widget.curselection():
     633                  return
     634
     635          self.selectedPlot = int(self.plots.lb.curselection()[0])
     636
     637          self.removePreviousPlotSettings()
     638
     639          self.initPlotSettings()
     640
     641          # The selected plot is a 2D plot.
     642          if len(self.parent.figure.gca().get_images()) == 0:
     643
     644              self.widgets['lineLabel'] = ComboStringEntry(self.pFrame, frameLabel = 'Plot label', contents = self.settings[self.selectedPlot]['lineLabel'])
     645              self.widgets['lineLabel'].grid(row = 1, column = 0, sticky = "NEW")
     646              self.widgets['lineLabel'].grid_columnconfigure(0, weight = 1)
     647              self.widgets['lineLabel'].grid_rowconfigure(1, weight = 1)
     648              self.widgets['lineLabel'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'lineLabel'))
     649
     650              self.widgets['lineWidth'] = ComboIntegerEntry(self.pFrame, frameLabel = 'Line width', contents = self.settings[self.selectedPlot]['lineWidth'])
     651              self.widgets['lineWidth'].grid(row = 2, column = 0, sticky = "NEW")
     652              self.widgets['lineWidth'].grid_columnconfigure(0, weight = 1)
     653              self.widgets['lineWidth'].grid_rowconfigure(2, weight = 1)
     654              self.widgets['lineWidth'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'lineWidth'))
     655
     656              v = lineStyles.index(self.settings[self.selectedPlot]['lineStyle'])
     657              self.widgets['lineStyle'] = ComboSpinbox(self.pFrame,\
     658                                                       frameLabel = 'Line style',\
     659                                                       default = v,\
     660                                                       contents = tuple(lineStyles))
     661              self.widgets['lineStyle'].grid(row = 3, column = 0, sticky = "NEW")
     662              self.widgets['lineStyle'].grid_columnconfigure(0, weight = 1)
     663              self.widgets['lineStyle'].grid_rowconfigure(3, weight = 1)
     664              self.widgets['lineStyle'].spinbox.config({'width' : 10, 'command' : lambda : self.changeSettings(None, 'lineStyle')})
     665
     666              self.widgets['lineColor'] = ComboButton(self.pFrame, frameLabel = 'Line color')
     667              self.widgets['lineColor'].grid(row = 4, column = 0, sticky = "NEW")
     668              self.widgets['lineColor'].grid_columnconfigure(0, weight = 1)
     669              self.widgets['lineColor'].grid_rowconfigure(4, weight = 1)
     670              self.widgets['lineColor'].button.config({'bg' : self.settings[self.selectedPlot]['lineColor'],\
     671                                                       'activebackground' : self.settings[self.selectedPlot]['lineColor'],\
     672                                                       'width' : 1,\
     673                                                       'command' : lambda : self.changeSettings(None, 'lineColor')})
     674              self.widgets['lineColor'].results = self.settings[self.selectedPlot]['lineColor']
     675
     676              v = markerStyles.index(self.settings[self.selectedPlot]['markerStyle'])
     677              self.widgets['markerStyle'] = ComboSpinbox(self.pFrame, frameLabel = 'Marker style',\
     678                                                         default = v,\
     679                                                         contents = tuple(markerStyles))
     680              self.widgets['markerStyle'].grid(row = 5, column = 0, sticky = "NEW")
     681              self.widgets['markerStyle'].grid_columnconfigure(0, weight = 1)
     682              self.widgets['markerStyle'].grid_rowconfigure(5, weight = 1)
     683              self.widgets['markerStyle'].spinbox.config({'width' : 10, 'command' : lambda : self.changeSettings(None, 'markerStyle')})
     684
     685              self.widgets['markerSize'] = ComboIntegerEntry(self.pFrame, frameLabel = 'Marker size', contents = self.settings[self.selectedPlot]['markerSize'])
     686              self.widgets['markerSize'].grid(row = 6, column = 0, sticky = "NEW")
     687              self.widgets['markerSize'].grid_columnconfigure(0, weight = 1)
     688              self.widgets['markerSize'].grid_rowconfigure(6, weight = 1)
     689              self.widgets['markerSize'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'markerSize'))
     690
     691              self.widgets['markerColor'] = ComboButton(self.pFrame, frameLabel = 'Marker color')
     692              self.widgets['markerColor'].grid(row = 7, column = 0, sticky = "NEW")
     693              self.widgets['markerColor'].grid_columnconfigure(0, weight = 1)
     694              self.widgets['markerColor'].grid_rowconfigure(7, weight = 1)
     695              self.widgets['markerColor'].button.config({'bg' : self.settings[self.selectedPlot]['markerColor'],\
     696                                                         'activebackground' : self.settings[self.selectedPlot]['markerColor'],\
     697                                                         'width' : 1,\
     698                                                         'command' : lambda event : self.changeSettings(event, 'markerColor')})
     699              self.widgets['markerColor'].results = self.settings[self.selectedPlot]['markerColor']
     700
     701          # The selected plot is a 3D plot.
     702          else:
     703
     704              v = colorMaps.index(self.settings[self.selectedPlot]['colorMap'])
     705              self.widgets['colorMap'] = ComboSpinbox(self.pFrame,\
     706                                                      frameLabel = 'Color map',\
     707                                                      default = v,\
     708                                                      contents = tuple(colorMaps))
     709              self.widgets['colorMap'].grid(row = 1, column = 0, sticky = "NEW")
     710              self.widgets['colorMap'].grid_columnconfigure(0, weight = 1)
     711              self.widgets['colorMap'].grid_rowconfigure(5, weight = 1)
     712              self.widgets['colorMap'].spinbox.config({'width' : 10,\
     713                                                       'command' : lambda : self.changeSettings(None, 'colorMap')})
     714
     715              v = interpolations.index(self.settings[self.selectedPlot]['interpolation'])
     716              self.widgets['interpolation'] = ComboSpinbox(self.pFrame,\
     717                                                           frameLabel = 'Interpolation',\
     718                                                           default = v,\
     719                                                           contents = tuple(interpolations))
     720              self.widgets['interpolation'].grid(row = 2, column = 0, sticky = "NEW")
     721              self.widgets['interpolation'].grid_columnconfigure(0, weight = 1)
     722              self.widgets['interpolation'].grid_rowconfigure(5, weight = 1)
     723              self.widgets['interpolation'].spinbox.config({'width' : 10,\
     724                                                            'command' : lambda : self.changeSettings(None, 'interpolation')})
     725
     726              self.widgets['alpha'] = ComboFloatEntry(self.pFrame, frameLabel = 'Alpha', contents = self.settings[self.selectedPlot]['alpha'])
     727              self.widgets['alpha'].grid(row = 3, column = 0, sticky = "NEW")
     728              self.widgets['alpha'].grid_columnconfigure(0, weight = 1)
     729              self.widgets['alpha'].grid_rowconfigure(0, weight = 1)
     730              self.widgets['alpha'].entry.bind('<FocusOut>', lambda event : self.changeSettings(event, 'alpha'))
     731
     732 -class PlotNetCDFVariableDialog(Toplevel):
     733      """Sets up a dialog used to plot variables present in a NetCDF file.
     734      """
     735
     736 -    def __init__(self, parent, title = None, netcdf = None, xVar = None, yVar = None, zVar = None):
     737          """The constructor.
     738
     739          @param parent: the parent widget.
     740
     741          @param title: a string specifying the title of the dialog.
     742          @type title: string
     743
     744          @param netcdf: the name of a NetCDF file to plot (string) or an opened NetCDF trajectory file.
     745          @type netcdf: a string or a Scientific.IO.NetCDF._NetCDFFile object
     746
     747          @param xVar: the NetCDF variable name of the X variable to plot.
     748          @type xVar: string
     749
     750          @param yVar: the NetCDF variable name of the Y variable to plot.
     751          @type yVar:
     752
     753          @param zVar: the NetCDF variable name of the Z variable to plot.
     754          @type zVar:
     755          """
     756
     757          Toplevel.__init__(self, parent)
     758
     759          self.transient(parent)
     760
     761          if title:
     762              self.title(title)
     763
     764          self.parent = parent
     765
     766          try:
     767              if isinstance(netcdf, _NetCDFFile):
     768                  self.data = netcdf
     769
     770              elif isinstance(netcdf, Trajectory):
     771                  self.data = NetCDFFile(netcdf.filename, 'r')
     772
     773              elif isinstance(netcdf, str):
     774                  self.data = NetCDFFile(netcdf, 'r')
     775
     776              else:
     777                  raise
     778
     779          except:
     780              self.data = None
     781
     782          self.xVar = xVar
     783          self.yVar = yVar
     784          self.zVar = zVar
     785
     786          body = Frame(self)
     787          self.initial_focus = self.body(body)
     788          body.grid(row = 0, column = 0, sticky = EW)
     789
     790          self.buttonbox()
     791
     792          # If a NetCDF file has been loaded in nMOLDYN then it is proposed by default for plotting.
     793          if self.data is not None:
     794              # The control variables are updated with the informations coming from the loaded trajectory.
     795              self.fileBrowser.setValue(str(self.data))
     796
     797              # The listbox storing the numerical 1D and 2D NetCDF data is filled up.
     798              self.displayVariables()
     799
     800              if (self.xVar is not None) & (self.yVar is not None):
     801                  if self.zVar is not None:
     802                      self.plotXYZ()
     803                  else:
     804                      self.plotXY()
     805
     806          self.grab_set()
     807
     808          if not self.initial_focus:
     809              self.initial_focus = self
     810
     811          self.protocol("WM_DELETE_WINDOW", lambda : self.cancel(self))
     812
     813          self.resizable(width = NO, height = NO)
     814
     815          self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))
     816
     817          self.initial_focus.focus_set()
     818
     819          self.wait_window(self)
     820
     821 -    def body(self, master):
     822          """
     823          Create dialog body. Return widget that should have initial focus.
     824          """
     825
     826          settingsFrame = LabelFrame(master, text = 'Settings', bd = 2, relief = GROOVE)
     827          settingsFrame.grid(row = 0, column = 0, sticky = EW, padx = 3, pady = 3)
     828          settingsFrame.grid_columnconfigure(0, weight = 1)
     829
     830          # The combo widget for the file browser.
     831          self.fileBrowser = ComboFileBrowser(settingsFrame,\
     832                                              frameLabel = "NetCDF input file",\
     833                                              contents = '',\
     834                                              save = False,\
     835                                              command = self.openNetCDF)
     836          self.fileBrowser.grid(row = 0, column = 0, padx = 2, pady = 2, sticky = EW)
     837          self.fileBrowser.grid_columnconfigure(0, weight = 1)
     838          self.fileBrowser.entry.bind('<Return>', self.openNetCDF)
     839
     840          # The frame that will host the different listboxes of the dialog.
     841          listboxesFrame =  LabelFrame(settingsFrame, text = 'Variables', bd = 2, relief = GROOVE)
     842          listboxesFrame.grid(row = 1, column = 0, padx = 2, pady = 2, sticky = W)
     843
     844          # The combo listbox that will contain the X variables.
     845          self.xVarLb = ComboListbox(listboxesFrame, 'X', [])
     846          self.xVarLb.lb.config({'exportselection' : 0, 'width' : 22, 'height' : 4, 'selectmode' : SINGLE})
     847          self.xVarLb.config({'relief' : FLAT})
     848          self.xVarLb.grid(row = 0, column = 0)
     849          self.xVarLb.lb.bind('<ButtonRelease-1>', self.selectXVariable)
     850
     851          # The combo listbox that will contain the Y variables.
     852          self.yVarLb = ComboListbox(listboxesFrame, 'Y', [])
     853          self.yVarLb.config({'relief' : FLAT})
     854          self.yVarLb.grid(row = 0, column = 1)
     855          self.yVarLb.lb.config({'exportselection' : 0, 'width' : 22, 'height' : 4, 'selectmode' : SINGLE})
     856
     857          # The combo listbox that will contain the Z variables.
     858          self.zVarLb = ComboListbox(listboxesFrame, 'Z', [])
     859          self.zVarLb.config({'relief' : FLAT})
     860          self.zVarLb.grid(row = 0, column = 2)
     861          self.zVarLb.lb.config({'exportselection' : 0, 'width' : 22, 'height' : 4, 'selectmode' : SINGLE})
     862
     863          # The frame that will host the pylab figure displaying the plot and the global settings widgets.
     864          plotFrame = LabelFrame(master, text = 'Plot', bd = 2, relief = GROOVE)
     865          plotFrame.grid(row = 3, column = 0, padx = 2, pady = 2, sticky = EW)
     866          plotFrame.grid_columnconfigure(0, weight = 1)
     867
     868          # The subframe that will host the figure.
     869          figureFrame = Frame(plotFrame, relief = FLAT)
     870          figureFrame.grid(row = 0, column = 0, sticky = 'EW')
     871          figureFrame.grid_columnconfigure(0, weight = 1)
     872
     873          # The figure.
     874          self.figure = Figure(dpi = 72)
     875          self.canvas = FigureCanvasTkAgg(self.figure, master = figureFrame)
     876
     877          self.canvas.get_tk_widget().pack()
     878          self.canvas._tkcanvas.pack()
     879          self.toolbar = NavigationToolbar2TkAgg(self.canvas, figureFrame)
     880          self.toolbar.update()
     881
     882          return None
     883
     884 -    def buttonbox(self):
     885          """
     886          Add standard button box.
     887          """
     888
     889          # The frame that contains the action buttons.
     890          box = LabelFrame(self, text = 'Actions', bd = 2, relief = GROOVE)
     891          box.grid(row = 1, column = 0, sticky = EW, padx = 3, pady = 3)
     892          box.grid_columnconfigure(0, weight = 1)
     893
     894          w = Button(box, text = "Cancel", width = 10, command = lambda : self.cancel(self))
     895          w.grid(row = 0, column = 0, sticky = E)
     896
     897          w = Button(box, text = "Settings", width = 10, command = self.openSettingsDialog)
     898          w.grid(row = 0, column = 1, sticky = E)
     899
     900          w = Button(box, text = "Reset", width = 10, command = self.resetPlots)
     901          w.grid(row = 0, column = 3, sticky = E)
     902
     903          w = Button(box, text = "Export plot", width = 10, command = self.exportPlotDialog)
     904          w.grid(row = 0, column = 4, sticky = E)
     905
     906          w = Button(box, text = "OK", width = 10, command = self.ok, default = ACTIVE)
     907          w.grid(row = 0, column = 5, sticky = E)
     908
     909          self.bind("<Return>", self.ok)
     910          self.bind("<Escape>", lambda event : self.cancel(self, event))
     911
     912      # Standard button semantics.
     913 -    def ok(self, event = None):
     914
     915          if not self.validate():
     916              self.initial_focus.focus_set()
     917              return
     918
     919          self.update_idletasks()
     920
     921          self.apply()
     922
     923 -    def cancel(self, dialog, event = None):
     924
     925          dialog.parent.grab_set()
     926          dialog.parent.focus_set()
     927          dialog.destroy()
     928
     929      # Command hooks
     930 -    def validate(self):
     931
     932          if not self.xVarLb.lb.curselection():
     933              raise Error('Please select a X variable.')
     934          self.xVar = self.xVarLb.lb.get(int(self.xVarLb.lb.curselection()[0]))
     935          dimX = self.data.variables[self.xVar].dimensions[0]
     936
     937          if not self.yVarLb.lb.curselection():
     938              raise Error('Please select a Y variable.')
     939          self.yVar = self.yVarLb.lb.get(int(self.yVarLb.lb.curselection()[0]))
     940          dimY = self.data.variables[self.yVar].dimensions[0]
     941
     942          if not self.zVarLb.lb.curselection():
     943              self.zVar = None
     944
     945              # The variable must have the same dimension than the selected Y variable to be retained.
     946              if dimX != dimY:
     947                  raise Error('X (dim = %d) and Y (dim = %d) do not have the same dimension.' % (dimX,dimY))
     948
     949          else:
     950              self.zVar = self.zVarLb.lb.get(int(self.zVarLb.lb.curselection()[0]))
     951              dimZ = self.data.variables[self.zVar].dimensions
     952              if (dimX != dimZ[0]) or (dimY != dimZ[1]):
     953                  raise Error('X (dim = %d) and Y (dim = %d) dimensions do not match Z (dim = %d) dimensions.' % (dimX,dimY,dimZ))
     954
     955          return True
     956
     957 -    def apply(self):
     958
     959          try:
     960              if self.zVar is None:
     961                  self.plotXY()
     962
     963              else:
     964                  self.plotXYZ()
     965          except:
     966              raise Error('Error when plotting datas.')
     967
     968      # The class specific methods.
     969 -    def openSettingsDialog(self):
     970          """This method will open the dialog to set up the global settings.
     971          """
     972
     973          if not self.figure.gca().get_lines():
     974              LogMessage('warning', 'No plot defined.', ['gui'])
     975              return
     976
     977          g = SettingsDialog(self)
     978
     979 -    def resetPlots(self):
     980          """This method will clear up all the displayed plots.
     981          """
     982
     983          # The figure is cleared up.
     984          self.figure.clear()
     985
     986          if hasattr(self.figure,'xValues'):
     987              delattr(self.figure, 'xValues')
     988
     989          if hasattr(self.figure,'yValues'):
     990              delattr(self.figure, 'yValues')
     991
     992          if hasattr(self.figure,'zValues'):
     993              delattr(self.figure, 'zValues')
     994
     995          # The canvas is updated.
     996          self.canvas.show()
     997
     998 -    def exportPlotDialog(self):
     999          """This method pops up a dialog from which the plotted datas can be exported to an ASCII file.
    1000          """
    1001
    1002          if not self.figure.gca().get_lines():
    1003              LogMessage('warning', 'No plot defined.', ['gui'])
    1004              return
    1005
    1006          self.exportDialog = Toplevel(self)
    1007
    1008          self.exportDialog.parent = self
    1009
    1010          self.exportDialog.transient(self)
    1011
    1012          self.exportDialog.title('Export plot to ASCII')
    1013
    1014          self.exportDialog.grab_set()
    1015
    1016          # The frame that contains the action buttons.
    1017          settingsFrame = LabelFrame(self.exportDialog, text = 'Settings', bd = 2, relief = GROOVE)
    1018          settingsFrame.grid(row = 0, column = 0, sticky = EW, padx = 3, pady = 3)
    1019          settingsFrame.grid_columnconfigure(0, weight = 1)
    1020
    1021          # Windows not resizable
    1022          self.exportDialog.resizable(width = NO, height = NO)
    1023
    1024          self.exportDialog.geometry("+%d+%d" % (self.winfo_rootx()+50, self.winfo_rooty()+50))
    1025
    1026          if self.figure.gca().get_images():
    1027              self.xIndexMinEntry = ComboIntegerEntry(settingsFrame, frameLabel = 'X Index min', contents = 1)
    1028              self.xIndexMinEntry.grid(row = 0, column = 0, sticky = "EW")
    1029              self.xIndexMinEntry.grid_columnconfigure(0, weight = 1)
    1030
    1031              self.xIndexMaxEntry = ComboIntegerEntry(settingsFrame, frameLabel = 'X Index max',\
    1032                                                      contents = self.figure.gca().get_images()[0].get_array().shape[1])
    1033              self.xIndexMaxEntry.grid(row = 1, column = 0, sticky = "EW")
    1034              self.xIndexMaxEntry.grid_columnconfigure(0, weight = 1)
    1035
    1036              self.yIndexMinEntry = ComboIntegerEntry(settingsFrame, frameLabel = 'Y Index min', contents = 1)
    1037              self.yIndexMinEntry.grid(row = 2, column = 0, sticky = "EW")
    1038              self.yIndexMinEntry.grid_columnconfigure(0, weight = 1)
    1039
    1040              self.yIndexMaxEntry = ComboIntegerEntry(settingsFrame, frameLabel = 'Y Index max',\
    1041                                                      contents = self.figure.gca().get_images()[0].get_array().shape[0])
    1042              self.yIndexMaxEntry.grid(row = 3, column = 0, sticky = "EW")
    1043              self.yIndexMaxEntry.grid_columnconfigure(0, weight = 1)
    1044
    1045          else:
    1046              self.indexMinEntry = ComboIntegerEntry(settingsFrame, frameLabel = 'Index min', contents = 1)
    1047              self.indexMinEntry.grid(row = 0, column = 0, sticky = "EW")
    1048              self.indexMinEntry.grid_columnconfigure(0, weight = 1)
    1049
    1050              self.indexMaxEntry = ComboIntegerEntry(settingsFrame, frameLabel = 'Index max',\
    1051                                                     contents = len(self.figure.gca().get_lines()[0].get_xdata()))
    1052              self.indexMaxEntry.grid(row = 1, column = 0, sticky = "EW")
    1053              self.indexMaxEntry.grid_columnconfigure(0, weight = 1)
    1054
    1055          # The filebrowser for the output ASCII file.
    1056          self.exportedFileBrowser = ComboFileBrowser(settingsFrame,\
    1057                                                      frameLabel = "ASCII output file",\
    1058                                                      contents = '',\
    1059                                                      save = True)
    1060          self.exportedFileBrowser.grid(row = 4, column = 0, padx = 2, pady = 2, sticky = EW)
    1061          self.exportedFileBrowser.grid_columnconfigure(0, weight = 1)
    1062
    1063          # The frame that contains the action buttons.
    1064          box = LabelFrame(self.exportDialog, text = 'Actions', bd = 2, relief = GROOVE)
    1065          box.grid(row = 1, column = 0, sticky = EW, padx = 3, pady = 3)
    1066          box.grid_columnconfigure(0, weight = 1)
    1067
    1068          w = Button(box, text = "Cancel", width = 10, command = lambda : self.cancel(self.exportDialog))
    1069          w.grid(row = 0, column = 0, sticky = E)
    1070
    1071          w = Button(box, text = "OK", width = 10, command = self.exportPlot, default = ACTIVE)
    1072          w.grid(row = 0, column = 1, sticky = E)
    1073
    1074          self.exportDialog.protocol("WM_DELETE_WINDOW", lambda : self.cancel(self.exportDialog))
    1075          self.exportDialog.bind("<Return>", self.exportPlot)
    1076          self.exportDialog.bind("<Escape>", lambda event : self.cancel(self.exportDialog, event))
    1077
    1078 -    def exportPlot(self, event = None):
    1079          """This method exports plotted datas to an ASCII file.
    1080          """
    1081
    1082          filename = self.exportedFileBrowser.getValue()
    1083          if not filename:
    1084              LogMessage('warning', 'No output file defined.', ['gui'])
    1085              return
    1086
    1087          if filename[-4:] != '.dat':
    1088              filename += '.dat'
    1089
    1090          try:
    1091
    1092              fFmt = '%20.8e '
    1093              sFmt = '%20s '
    1094              f = open(filename, 'w')
    1095
    1096              if self.figure.gca().get_images():
    1097                  xIndexMin = self.xIndexMinEntry.getValue() - 1
    1098                  xIndexMax = self.xIndexMaxEntry.getValue()
    1099
    1100                  if xIndexMin < 0:
    1101                      xIndexMin = 0
    1102                  if xIndexMax > self.figure.gca().get_images()[0].get_array().shape[1]:
    1103                      xIndexMax = self.figure.gca().get_images()[0].get_array().shape[1]
    1104
    1105                  yIndexMin = self.yIndexMinEntry.getValue() - 1
    1106                  yIndexMax = self.yIndexMaxEntry.getValue()
    1107                  if yIndexMin < 0:
    1108                      yIndexMin = 0
    1109                  yIndexMax = self.yIndexMaxEntry.getValue()
    1110                  if yIndexMax > self.figure.gca().get_images()[0].get_array().shape[0]:
    1111                      yIndexMax = self.figure.gca().get_images()[0].get_array().shape[0]
    1112
    1113                  f.write('datas = %s\n' % self.figure.gca().get_images()[0].get_label())
    1114                  f.write('1st line = %s\n' % self.figure.gca().get_images()[0].get_axes().get_ylabel())
    1115                  f.write('1st column = %s\n\n' % self.figure.gca().get_images()[0].get_axes().get_xlabel())
    1116
    1117                  data = self.figure.gca().get_images()[0].get_array()
    1118
    1119                  # The first line is written with the Y values.
    1120                  f.write(sFmt % 'NaN')
    1121                  for v in range(yIndexMin,yIndexMax):
    1122                      f.write(fFmt % self.figure.yValues[v])
    1123                  f.write('\n')
    1124
    1125                  # The 2D datas are written.
    1126                  for ix in range(xIndexMin,xIndexMax):
    1127                      f.write(fFmt % self.figure.xValues[ix])
    1128                      for iy in range(yIndexMin,yIndexMax):
    1129                          v = self.figure.zValues[iy,ix]
    1130                          try:
    1131                              f.write(fFmt % v)
    1132                          except:
    1133                              f.write(sFmt % v)
    1134
    1135                      f.write('\n')
    1136
    1137              else:
    1138                  indexMin = self.indexMinEntry.getValue() - 1
    1139                  indexMax = self.indexMaxEntry.getValue()
    1140                  if indexMin < 0:
    1141                      indexMin = 0
    1142                  if indexMax > len(self.figure.gca().get_lines()[0].get_xdata()):
    1143                      indexMax = len(self.figure.gca().get_lines()[0].get_xdata())
    1144
    1145                  data = []
    1146                  comp = 1
    1147                  for p in self.figure.gca().get_lines():
    1148                      f.write('columns-%d = %s\n' % (comp,p.get_axes().get_xlabel()))
    1149                      f.write('columns-%d = %s\n' % (comp+1,p.get_axes().get_ylabel()))
    1150                      data.append(list(p.get_xdata())[indexMin:indexMax])
    1151                      data.append(list(p.get_ydata())[indexMin:indexMax])
    1152                      comp += 2
    1153                  data = [[r[col] for r in data] for col in range(len(data[0]))]
    1154
    1155                  f.write('\n')
    1156
    1157                  for d in data:
    1158                      for dd in d:
    1159                          try:
    1160                              f.write(fFmt % dd)
    1161                          except:
    1162                              f.write(sFmt % dd)
    1163                      f.write('\n')
    1164              f.close()
    1165          except:
    1166              raise Error('Error when exporting plotted data.')
    1167          finally:
    1168              self.cancel(self.exportDialog)
    1169
    1170 -    def selectXVariable(self, event):
    1171
    1172          self.yVarLb.lb.selection_clear(0, END)
    1173          self.zVarLb.lb.selection_clear(0, END)
    1174
    1175 -    def displayVariables(self):
    1176          """
    1177          This method display the numeric variables found in the NetCDF file into their appropriate listbox.
    1178          """
    1179
    1180          # The X, Y and Z variables listboxes are cleared up first.
    1181          self.xVarLb.lb.delete(0, END)
    1182          self.yVarLb.lb.delete(0, END)
    1183          self.zVarLb.lb.delete(0, END)
    1184
    1185          # Loop over the variables NetCDF dictionnary items.
    1186          for varName in sorted(self.data.variables.keys()):
    1187              variable = self.data.variables[varName]
    1188
    1189              if len(variable.shape) > 2:
    1190                  continue
    1191
    1192              # If the NetCDF variable is not numeric then loop to the next one.
    1193              if variable.typecode() not in [Num.Float, Num.Float0, Num.Int, Num.Int32, Num.Character]:
    1194                  continue
    1195
    1196              # If the variable is 1D then it is appended to the X and Y listboxes.
    1197              if len(variable.shape) == 1:
    1198                  self.xVarLb.lb.insert(END, varName)
    1199                  self.yVarLb.lb.insert(END, varName)
    1200
    1201              # If the variable is 2D then it is appended to the Z listbox.
    1202              elif len(variable.shape) == 2:
    1203                  if variable.typecode() == Num.Character:
    1204                      self.xVarLb.lb.insert(END, varName)
    1205                      self.yVarLb.lb.insert(END, varName)
    1206
    1207                  else:
    1208                      self.zVarLb.lb.insert(END, varName)
    1209
    1210 -    def openNetCDF(self, event = None):
    1211          """
    1212          This method opens a NetCDF file and updates the dialog with the data read from that file.
    1213          Arguments:
    1214              -event: Tkinter event.
    1215          """
    1216
    1217          if event is not None:
    1218              filename = self.fileBrowser.getValue()
    1219
    1220          else:
    1221              # The name of the NetCDF file to load.
    1222              filename = askopenfilename(parent = self,\
    1223                                         filetypes = [('NetCDF file','*.nc')],\
    1224                                         initialdir = PREFERENCES.outputfile_path)
    1225
    1226          # The file must exist.
    1227          if filename:
    1228              try:
    1229                  self.data = NetCDFFile(filename, 'r')
    1230
    1231                  self.fileBrowser.setValue(filename)
    1232
    1233                  # The X, Y and Z variables listboxes are filled up.
    1234                  self.displayVariables()
    1235
    1236              except:
    1237                  LogMessage('warning','The file %s could not be read.' % filename,['gui'])
    1238
    1239          return 'break'
    1240
    1241 -    def plotXY(self):
    1242          """
    1243          This method display a 2D plot.
    1244          """
    1245
    1246          if (self.xVar is None) or (self.yVar is None):
    1247              return
    1248
    1249          if self.yVar is None:
    1250              return
    1251
    1252          if self.figure.gca().get_images():
    1253              self.resetPlots()
    1254
    1255          if self.data.variables[self.xVar].typecode() == Num.Character:
    1256              temp = self.data.variables[self.xVar].getValue()
    1257              xValues = Num.arange(temp.shape[0])
    1258          else:
    1259              xValues = self.data.variables[self.xVar].getValue()
    1260
    1261          if self.data.variables[self.yVar].typecode() == Num.Character:
    1262              temp = self.data.variables[self.yVar].getValue()
    1263              yValues = Num.arange(temp.shape[0])
    1264          else:
    1265              yValues = self.data.variables[self.yVar].getValue()
    1266
    1267          self.figure.gca().plot(xValues, yValues, label = self.yVar + ' vs ' + self.xVar)
    1268
    1269          self.figure.gca().grid(True)
    1270
    1271          if self.data.variables[self.xVar].typecode() == Num.Character:
    1272              xTicks = xValues[:]
    1273              xTicksLabels = ['%s' % temp[v,:].tostring().rstrip('\x00') for v in range(len(temp))]
    1274              self.figure.gca().set_xticks(xTicks)
    1275              self.figure.gca().set_xticklabels(xTicksLabels)
    1276
    1277          if self.data.variables[self.yVar].typecode() == Num.Character:
    1278              yTicks = yValues[:]
    1279              yTicksLabels = ['%s' % temp[v,:].tostring().rstrip('\x00') for v in range(len(temp))]
    1280              self.figure.gca().set_yticks(yTicks)
    1281              self.figure.gca().set_yticklabels(yTicksLabels)
    1282
    1283          # Set the title.
    1284          try:
    1285              self.figure.get_axes()[0].set_title(self.data.title)
    1286          except AttributeError:
    1287              self.figure.get_axes()[0].set_title('')
    1288
    1289          # Sets the X-axis label.
    1290          try:
    1291              self.figure.get_axes()[0].set_xlabel('%s (%s)' % (self.xVar, self.data.variables[self.xVar].units))
    1292          except AttributeError:
    1293              self.figure.get_axes()[0].set_xlabel(self.xVar)
    1294
    1295          # Sets the Y-axis label.
    1296          try:
    1297              self.figure.get_axes()[0].set_ylabel('%s (%s)' % (self.yVar, self.data.variables[self.yVar].units))
    1298          except AttributeError:
    1299              self.figure.get_axes()[0].set_ylabel(self.yVar)
    1300
    1301          # Set the legend.
    1302          self.figure.get_axes()[0].legend(loc = 'best')
    1303
    1304          self.canvas.show()
    1305
    1306 -    def displayPlotSlices(self, event):
    1307          """
    1308          This call back plot the orthogonal slices defined by the moving cursor of a 3D plot.
    1309          """
    1310
    1311          # The event triggering the call back must be the right mouse button.
    1312          if event.button != 3:
    1313              return
    1314
    1315          # If the event does not occur into the plot then do nothing.
    1316          if event.inaxes is None:
    1317              return
    1318
    1319          # The plot that will contain the horizontal slice.
    1320          horizontalSlice = pylab.subplot(211)
    1321          if self.data.variables[self.xVar].typecode() == Num.Character:
    1322              temp = self.data.variables[self.xVar].getValue()
    1323              xValue = Num.arange(len(temp))
    1324              xTicks = ['%s' % temp[v,:].tostring().rstrip('\x00') for v in range(len(temp))]
    1325              horizontalSlice.set_xticks(range(len(xTicks)))
    1326              horizontalSlice.set_xticklabels(xTicks)
    1327          else:
    1328              xValue = self.data.variables[self.xVar].getValue()
    1329
    1330          # The horizontal slice plot.
    1331          horizontalSlice.plot(xValue, self.data.variables[self.zVar][:,int(event.ydata)])
    1332
    1333          # Sets the X-axis label.
    1334          if hasattr(self.data.variables[self.xVar], 'units'):
    1335              horizontalSlice.set_xlabel('%s (%s)' % (self.xVar, self.data.variables[self.xVar].units))
    1336          else:
    1337              horizontalSlice.set_xlabel('%s (Unknown unit)' % self.xVar)
    1338
    1339          # Sets the Y-axis label.
    1340          if self.data.variables[self.yVar].typecode() == Num.Character:
    1341              value = self.data.variables[self.yVar].getValue()[int(event.ydata),:].tostring()
    1342              horizontalSlice.set_ylabel('%s %s = %s' % (self.zVar,self.yVar, value), fontsize = 7)
    1343          else:
    1344              value = self.data.variables[self.yVar].getValue()[int(event.ydata)]
    1345              horizontalSlice.set_ylabel('%s %s = %.3f' % (self.zVar,self.yVar, value), fontsize = 7)
    1346
    1347          # The plot that will contain the vertical slice.
    1348          verticalSlice = pylab.subplot(212)
    1349
    1350          if self.data.variables[self.yVar].typecode() == Num.Character:
    1351              temp = self.data.variables[self.yVar].getValue()
    1352              xValue = Num.arange(len(temp))
    1353              xTicks = ['%s' % temp[v,:].tostring().rstrip('\x00') for v in range(len(temp))]
    1354              verticalSlice.set_xticks(range(len(xTicks)))
    1355              verticalSlice.set_xticklabels(xTicks)
    1356          else:
    1357              xValue = self.data.variables[self.yVar].getValue()
    1358
    1359          # The vertical slice plot.
    1360          verticalSlice.plot(xValue, self.data.variables[self.zVar][int(event.xdata),:])
    1361          # The x label of the horizontal slice plot.
    1362          verticalSlice.set_xlabel(self.yVar)
    1363          if self.data.variables[self.xVar].typecode() == Num.Character:
    1364              value = self.data.variables[self.xVar].getValue()[int(event.xdata),:].tostring()
    1365              verticalSlice.set_ylabel('%s %s = %s' % (self.zVar,self.xVar, value), fontsize = 7)
    1366          else:
    1367              value = self.data.variables[self.xVar].getValue()[int(event.xdata)]
    1368              verticalSlice.set_ylabel('%s %s = %.3f' % (self.zVar,self.xVar, value), fontsize = 7)
    1369
    1370          pylab.show()
    1371
    1372 -    def plotXYZ(self):
    1373          """
    1374          This method display a 2D plot.
    1375          """
    1376
    1377          if (self.xVar is None) or (self.yVar is None) or (self.zVar is None):
    1378              return
    1379
    1380          self.resetPlots()
    1381
    1382          xValues = self.data.variables[self.xVar].getValue()
    1383          yValues = self.data.variables[self.yVar].getValue()
    1384          zValues = pylab.transpose(self.data.variables[self.zVar].getValue())
    1385
    1386          self.figure.gca().imshow(zValues, aspect = 'auto', label = self.zVar + ' vs ' + self.xVar + ' and ' + self.yVar)
    1387          self.figure.xValues = xValues
    1388          self.figure.yValues = yValues
    1389          self.figure.zValues = zValues
    1390
    1391          # The image colorbar.
    1392          self.figure.colorbar(self.figure.gca().get_images()[0])
    1393
    1394          # Creation of a cursor to plot orthogonal slices of the 3D plot.
    1395          cursor = Cursor(self.figure.gca(), useblit = True, color ='red', linewidth = 1)
    1396          cursor.ax.set_xlim((0, zValues.shape[1]-1))
    1397          cursor.ax.set_ylim((0, zValues.shape[0]-1))
    1398
    1399          if self.data.variables[self.xVar].typecode() == Num.Character:
    1400              self.figure.gca().set_xticks(range(len(xValues)))
    1401              xTickLabels = ['%s' % xValues[v,:].tostring().rstrip('\x00') for v in range(len(xValues))]
    1402              self.figure.gca().set_xticklabels(xTickLabels)
    1403          else:
    1404              xStep = len(xValues)/10
    1405              if xStep < 1:
    1406                  xStep = 1
    1407              self.figure.gca().set_xticks(range(0, len(xValues), xStep))
    1408              self.figure.gca().set_xticklabels(['%.3f' % xValues[v] for v in range(0, len(xValues), xStep)])
    1409
    1410          if self.data.variables[self.yVar].typecode() == Num.Character:
    1411              self.figure.gca().set_yticks(range(len(yValues)))
    1412              yTickLabels = ['%s' % yValues[v,:].tostring().rstrip('\x00') for v in range(len(yValues))]
    1413              self.figure.gca().set_yticklabels(yTickLabels)
    1414          else:
    1415              yStep = len(yValues)/10
    1416              if yStep < 1:
    1417                  yStep = 1
    1418              self.figure.gca().set_yticks(range(0, len(yValues), yStep))
    1419              self.figure.gca().set_yticklabels(['%.3f' % yValues[v] for v in range(0, len(yValues), yStep)])
    1420
    1421          # Set the title.
    1422          try:
    1423              self.figure.gca().set_title(self.data.title)
    1424
    1425          except AttributeError:
    1426              self.figure.gca().set_title('')
    1427
    1428          # Sets the X-axis label.
    1429          try:
    1430              self.figure.gca().set_xlabel('%s (%s)' % (self.xVar, self.data.variables[self.xVar].units))
    1431
    1432          except AttributeError:
    1433              self.figure.gca().set_xlabel(self.xVar)
    1434
    1435          # Sets the Y-axis label.
    1436          try:
    1437              self.figure.gca().set_ylabel('%s (%s)' % (self.yVar, self.data.variables[self.yVar].units))
    1438          except AttributeError:
    1439              self.figure.gca().set_ylabel(self.yVar)
    1440
    1441          # Bind the plot to the callback |self.displayPlotSlices|.
    1442          self.canvas.mpl_connect('button_press_event', self.displayPlotSlices)
    1443
    1444          self.canvas.show()
    1445