#!/usr/bin/env python2.7

#Imports section
import pygtk
import gtk
import cf
import numpy as np
import sys
import cfplot as cfp
import matplotlib.pyplot as plt
import gtk, gobject, pango
import unittest
from collections import OrderedDict
from distutils.version import StrictVersion
from subprocess import call

#Check numpy version is okay
numpy_version_min='1.8.1'
numpy_errstr='\n numpy >= ' + numpy_version_min + ' needs to be installed to use cfplot,\n found numpy '+np.__version__ +'\n'
if np.__version__ < StrictVersion(numpy_version_min): raise  Warning(numpy_errstr) 


__version__='0.9.0'

cfgPadding=5

#Define a persistent pvars instance to store data across plotting
class pvars(object):
   ''' 
      plotvars is a set of persistent variables to store various parameters
      across plotting actions such as multiple plots.
      
      pos=None - current plot position in multiple plot mode 
      nplots=1 - number of plots in multiple plot mode
      code='' - Python and cfplot commands used to make the present plot
      plot_finish=0 - number of plots to make for this type of plot.  
                      For graph and contour this is set to 1, vector to 2 and contour and vector to 3.
      plot_counter=0 - present plot number
      title='' - title for plot
      field_selected='' - field selected from currently loaded data
      file_selected='' - file currently selected
      levs_set=False - user has set levels - min, max, step
      levs_manual_set=False - user has set manual levels
      levs_min='0' - user selected minimim level
      levs_max='0' - user selected maximim level
      levs_step='0' - user selected level step
      resolution='c' - map resolution
      continent_thickness='1.5' - Thickness of the drawn continents
      continent_color='black' - colour of the drawn continents
      proj='cyl' - map projection, default=cylindrical projection
      vect_auto_set=True - auto vectors set
      vect_stride_set=False - vect stride set
      vect_pts_set=False - vector intepolation set
      vect_ufield=''
      vect_length='10' - vector length
      vect_scale='100' - vector scale
      vect_stride='1' - vector stride between points
      vect_pts='30' - interpolate tho this number of vectors
      cscale_auto_set=True - automatic colour scale
      cscale_reverse_set=False - reverse colour scale
      cscale_ncols='' - number of colours to interpolate to
      cscale_white='' - set these colour indicies to white
      cscale_above='' - number of colours above the midpoint of the scale
      cscale_below='' - number of colours above the midpoint of the scale
      cscale='viridis' - default colour scale
   '''
   def __init__(self, **kwargs):
      '''Initialize a new Pvars instance'''
      for attr, value in kwargs.iteritems():
         setattr(self, attr, value)

   def __str__(self):
      '''x.__str__() <==> str(x)'''
      out = ['%s = %s' % (a, repr(v))]
      for a, v in self.__dict__.iteritems():
         return '\n'.join(out)


plotvars=pvars(pos=0, nplots=1, code='', plot_counter=0, title='', field_selected='',\
               file_selected='', data=None, levs_set=False, levs_manual_set=False,\
               levs_min='0', levs_max='0', levs_step='0',levs_manual='', \
               levs_extend_lower=True, levs_extend_upper=True,
               proj='cyl', lonmin='-180', lonmax='180', latmin='-90', latmax='90',\
               boundinglat='0', lon_0='0',
               resolution='c', continent_thickness='1.5', continent_color='black',\
               vect_ufield='', vect_auto_set=True, vect_length='10', vect_scale='100',\
               vect_stride='1', vect_pts='30', vect_stride_set=False,vect_pts_set=False,\
               cscale_auto_set=True, cscale_reverse_set=False, cscale_white='',\
               cscale_ncols='', cscale_above='', cscale_below='',cscale='viridis'\
               )

cscales=['viridis', 'magma', 'inferno', 'plasma', 'parula', 'gray', 'hotcold_18lev', 'hotcolr_19lev',
'mch_default', 'perc2_9lev', 'percent_11lev', 'precip2_15lev', 'precip2_17lev', 'precip3_16lev',
'precip4_11lev', 'precip4_diff_19lev', 'precip_11lev', 'precip_diff_12lev', 'precip_diff_1lev', 'rh_19lev',
'spread_15lev', 'amwg', 'amwg_blueyellowred', 'BlueDarkRed18', 'BlueDarkOrange18', 'BlueGreen14', 'BrownBlue12',
'Cat12', 'cmp_flux', 'cosam12', 'cosam', 'GHRSST_anomaly', 'GreenMagenta16',
'nrl_sirkes', 'nrl_sirkes_nowhite','prcp_1', 'prcp_2',
'prcp_3', 'radar', 'radar_1', 'seaice_1', 'seaice_2', 'so4_21',
'StepSeq25', 'sunshine_9lev', 'sunshine_diff_12lev', 'temp_19lev', 'temp_diff_18lev', 'temp_diff_1lev',
'topo_15lev', 'wgne15', 'wind_17lev', 'amwg256', 'BkBlAqGrYeOrReViWh200', 'BlAqGrYeOrRe', 'BlAqGrYeOrReVi200',
'BlGrYeOrReVi200', 'BlRe', 'BlueRed', 'BlueRedGray', 'BlueWhiteOrangeRed', 'BlueYellowRed', 'BlWhRe', 'cmp_b2r',
'cmp_haxby', 'detail', 'extrema', 'GrayWhiteGray', 'GreenYellow', 'helix', 'helix1', 'hotres', 'matlab_hot',
'matlab_hsv', 'matlab_jet', 'matlab_lines', 'ncl_default', 'ncview_default', 'OceanLakeLandSnow', 'rainbow',
'rainbow_white_gray', 'rainbow_white', 'rainbow_gray', 'tbr_240_300', 'tbr_stdev_0_30', 'tbr_var_0_500',
'tbrAvg1', 'tbrStd1', 'tbrVar1', 'thelix', 'ViBlGrWhYeOrRe', 'wh_bl_gr_ye_re', 'WhBlGrYeRe', 'WhBlReWh',
'WhiteBlue', 'WhiteBlueGreenYellowRed', 'WhiteGreen', 'WhiteYellowOrangeRed', 'WhViBlGrYeOrRe', 'WhViBlGrYeOrReWh',
'wxpEnIR', '3gauss', '3saw', 'posneg_2', 'posneg_1',
'os250kmetres', 'wiki_1_0_2', 'wiki_1_0_3', 'wiki_2_0',
'wiki_2_0_reduced', 'arctic', 'scale1', 'scale2', 'scale3', 'scale4', 'scale5', 'scale6', 'scale7', 'scale8',
'scale9', 'scale10', 'scale11', 'scale12', 'scale13', 'scale14', 'scale15', 'scale16', 'scale17' , 'scale18',
'scale19', 'scale20', 'scale21', 'scale22', 'scale23', 'scale24', 'scale25', 'scale26', 'scale27', 'scale28',
'scale29', 'scale30', 'scale31', 'scale32', 'scale33', 'scale34', 'scale35', 'scale36', 'scale37', 'scale38',
'scale39', 'scale40', 'scale41', 'scale42', 'scale43', 'scale44']

cscales=pvars(cscales=cscales)

class guiFrame(gtk.Frame):
    ''' Mixin to simplify framesetup with size and label '''
    xsize,ysize=800,600
    def __init__(self,title=None,xsize=None,ysize=None):
        super(guiFrame,self).__init__()
        
        if title is not None: self.set_label(title)
        
        if xsize is not None: self.xsize=xsize
        if ysize is not None: self.ysize=ysize
        self.set_size_request(self.xsize,self.ysize)
            
class scrolledFrame(guiFrame):
    ''' Provides a scrolled window inside a frame '''
    def __init__(self,title=None,xsize=None,ysize=None):
        ''' Initialise with optional title and size '''
        super(scrolledFrame,self).__init__(title=title,xsize=xsize,ysize=ysize)
        # now set up scrolled window    
        self.sw=gtk.ScrolledWindow()
        self.sw.set_border_width(5)
        self.sw.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
        # the following doesn't seem to get honoured, even
        # though if we get it back, it's definitely set shadow_none
        self.sw.set_shadow_type(gtk.SHADOW_NONE)
        # print 'shadow',self.sw.get_shadow_type()
        # if we have to have a frame drawn around the sw, 
        # and it seems we do, we might as well have some space around it.
       
        self.add(self.sw)
        # put a vbox in the scrolled window for content
        self.vbox=gtk.VBox()
        self.sw.add_with_viewport(self.vbox)
    def show(self):
        super(scrolledFrame,self).show_all()    
        
def smallLabel(text,size='small',wrap=True):
    ''' Convenience method for providing a small multi-line gtk.Label '''
    #Change any & characters in the text as these cause an error in 
    string="<span size='%s'>%s</span>"%(size,text.replace('&', 'and'))
    label=gtk.Label(string)
    label.set_use_markup(True)
    label.set_line_wrap(wrap)
    if wrap:
        label.set_justify(gtk.JUSTIFY_LEFT)
    return label
    
def smallButton(text,size='small'):
    ''' Makes a small button '''
    text="<span size='small'>%s</span>"%text
    b=gtk.Button(text)
    label=b.get_child()
    label.set_use_markup(True)
    return b
    
class myCombo(gtk.HBox):
    ''' Mixin convenience class for small combo boxes with labels '''
    def __init__(self,content,label=None,initial=None, callback=None,fontSize=8,
                 padding=2):
        ''' Initialise with content, label, callback and fontsize '''
        super(myCombo,self).__init__()
        self._model=gtk.ListStore(gobject.TYPE_STRING)
        self._box=gtk.ComboBox(self._model)
        txt=gtk.CellRendererText()
        self._box.pack_start(txt,True)
        self._box.add_attribute(txt,'text',0)
        txt.set_property('font','sans %s'%fontSize)
        if label is not None:
            mylabel=smallLabel(label)
            self.pack_start(mylabel,padding=padding,expand=False)
        if callback is not None:
            self._callback=callback
            self._box.connect('changed',self.__myComboCallback)
        self.pack_start(self._box,padding=padding,expand=True) 
        for item in content:
            self._model.append([item])

        #print 'ajh - myCombo - setting self.content to ', content, type(content)
        #if type(content) is float: print 'ajh - myCombo - float is ', float(content)
        self.content=content
        if initial is not None: self.set_value(initial)
    def set_value(self,v):
        ''' Set active value of combobox '''
        if v not in self.content:  

            print 'ajh -self.content is ', self.content, type(self.content)
            print 'ajh - v is ', v, type(v)        

            raise ValueError('Attempt to set combobox to non valid value')
        else:
            #print 'ajh - myCombo - setting val to ', v
            self._box.set_active(self.content.index(v))
     


    def get_value(self):
        ''' Returns value in combobox '''
        return self._model[self._box.get_active()][0]
    def __myComboCallback(self,w):
        ''' Return current value of widget as argument to the callback '''
        self._callback(w,self.get_value())
    def show(self):
        super(myCombo,self).show_all()    
        

class collapseCombo(myCombo):
    ''' Choose from the available collapse operators '''
    options=OrderedDict([('range',None),('min','min'),('max','max'),
                         ('mid','mid_range'),('sum','sum'),('mean','mean'),
                         ('s.d.','standard_deviation'),('var','variance')])
    def __init__(self,initial='None',callback=None,fontSize='8'):
        ''' Initialise with the current value, a callback to be called if
        the value changes, and if necessary, a font size '''
        super(collapseCombo,self).__init__(
            self.options.keys(),initial=initial,callback=callback,fontSize=fontSize)
        # FIXME. I think I want the size request to be 20, but the
        # problem is that the text on the button disappears. I need
        # to shrink the container button, but I don't know how to do
        # that, yet. (Which would hopefully fix the off-centred text
        # as well.
        self._box.set_size_request(40,22)
    def get_value(self):
        ''' Override mixin method so we get the long name, after combobox
        only shows the short name. '''
        r=super(collapseCombo,self).get_value()
        return self.options[r]
    
class arrayCombo(myCombo):
    ''' Returns a labeled array combobox widget which has set_value, get_value 
    and show methods. '''
    def __init__(self,vector,label=None,callback=None,initial=None,padding=3):
        ''' Initialise with a vector to put in the combobox.
        Optional arguments:
            label - the combobox label
            callback - a tuple with a callback to pass the current value to 
                       after it has been changed and something else that the
                       callback knows how to interpret, e.g. (callback, target)
                       will result in a functional call to callback(target,value).
            initial - is an initial value
            padding - the typical hbox padding.
            '''
        self.callback=callback
        # I think we can afford a copy
        # convert to strings retaining the full precision of the data
        options=[repr(i) for i in vector]


        super(arrayCombo,self).__init__(options,initial=initial,label=label,
                                        callback=self.__aCcallback)
        # following magic sets the dropdown to use scrollbars (and is much faster)
        style = gtk.rc_parse_string('''
        style "my-style" { GtkComboBox::appears-as-list = 1 }
        widget "*.mycombo" style "my-style" ''')
        self._box.set_name('mycombo')
        self._box.set_style(style)
        
        # the natural size request seems to be (106,25), but
        # it could be much smaller given our smaller text
        self._box.set_size_request(60,20)
      
    def set_value(self,v):
        ''' Set active value of combobox '''
        # need to override the mixin class to ensure type conversion from float
        # take account of three different types of float that are input
        # 

        #print 'ajh - set_value passed is ', v, type(v), repr(v)

        if type(v) is float or type(v) is np.float32 \
           or type(v) is np.float64:
            super(arrayCombo,self).set_value(repr(v))
        else:
            super(arrayCombo,self).set_value(str(v))


    def get_value(self):
        ''' Returns value in combobox '''
        # Need to override the mixin class to ensure type conversion to float.
        # Convert numbers with a lot of decimal places to a np.float64 so 
        # that full precision is retained else return a float.

        
        ndecs=super(arrayCombo,self).get_value()[::-1].find('.')
        if ndecs < 8:
            return np.float(super(arrayCombo,self).get_value())
        else:
            return np.float64(super(arrayCombo,self).get_value())


    def __aCcallback(self,entry,value):
        ''' This is the internal component of the optional callback '''
        if self.callback is not None:
            self.callback[0](self.callback[1],value)  

################################################################################
# EVERYTHING ABOVE IS gtk aware, but not cf aware
# EVERYTHING BELOW is *both* gtk aware and cf aware
################################################################################    
    
def cfkeyvalue(f,p):
    ''' Utility method for making a pango string from a cf object <f> and 
    a specific property <p>. '''
    s='<b>%s</b>'%p
    if hasattr(f,p):
        v=getattr(f,p)
    else: return ''
    if hasattr(v,'__call__'): v=str(v())
    return '%s: %s'%(s,v)    
    
def cfdimprops(f):
    ''' Utility method for obtaining dimension properties from a CF field
    and converting them into a list of pango strings. '''
    r=[]
    
    for p in ['X','Y','Z','T']:
        s='<b>%s</b>:\n'%p
        coord=f.coord(p)
        if coord is None: continue
        for k in coord.properties:
            kv=cfkeyvalue(coord,k)
            if kv<>'':s+='     %s\n'%kv
        s+='     <b>Units: </b>%s'%str(f.coord(p).data.Units)
        r.append(s)
    return r
            
class fieldMetadata(scrolledFrame):
    ''' Provides a frame widget for field metadata, and packs it with content
    via the set_data method which takes one or more fields in a list. If 
    multiple fields are provided, show the metadata common to the fields. '''
    
    size='small'   # font size for the content
    
    def __init__(self,title='Field Metadata',xsize=None,ysize=None):
        ''' Initialise '''
        super(fieldMetadata,self).__init__(title=title,xsize=xsize,ysize=ysize)
        # Tell the set_data method when it's the first time through.
        self.shown=False
        
    def set_data(self,fields):
        ''' Show field metadata information for a specific field.
        If more than one field in fields, show common metadata. '''
        common=[]

        if len(fields)>1:
            string='<i>Common Field Metadata</i>\n'
            # find intersection, don't you love python?
            sets=[set([cfkeyvalue(f,p) for p in f.properties]) for f in fields]
            u=set.intersection(*sets)
        elif len(fields) == 1:
            string=''
            # just show the field properties
            u=[cfkeyvalue(fields[0],p) for p in fields[0].properties]

        if len(fields) >0:
            for i in u: 
                 if i<>'':string+='%s\n'%i
            if self.shown:  
                self.label.destroy()
                self.hbox.destroy()  # we don't want it to be the old size
            # now build the label
            self.label=smallLabel(string)
            # shove it in a box and make sure it doesn't expand.
            self.hbox=gtk.HBox()
            self.hbox.pack_start(self.label,expand=False,padding=5)
            self.vbox.pack_start(self.hbox,expand=False,padding=5)
            self.show()
            self.shown=True
        

    
class gridMetadata(scrolledFrame):
    ''' Shows grid metadata for a field or set of fields '''
    def __init__(self,title='Grid Metadata',xsize=None,ysize=None):
        ''' Initialise as an empty vessel which gets populated
        via the set data method.'''
        super(gridMetadata,self).__init__(title=title,xsize=xsize,ysize=ysize)
        self.shown=False
    def set_data(self,fields):
        ''' Takes a set of cf fields and extracts their grid information.
        If their is common information is common, it says so. '''
        common=[]
        if len(fields)>1:
            string='<i>Common Grid Metadata</i>\n'
            # find intersection, don't you love python?
            sets=[set(cfdimprops(f))for f in fields]
            u=set.intersection(*sets)
        elif len(fields) ==1:
            string=''
            # just show the field properties
            u=cfdimprops(fields[0])

        if len(fields) > 0:
            for i in u: 
                if i<>'':string+='%s\n'%i
            if self.shown:  
                self.label.destroy()
                self.hbox.destroy()  # we don't want it to be the old size
            # now build the label
            self.label=smallLabel(string)
            # shove it in a box and make sure it doesn't expand.
            self.hbox=gtk.HBox()
            self.hbox.pack_start(self.label,expand=False,padding=5)
            self.vbox.pack_start(self.hbox,expand=False,padding=5)
            self.show()
            self.shown=True
        
class fieldSelector(guiFrame):
    ''' Provides a widget for data discovery, depends on the CF api
    to load data through the set_data method. '''
    
    def __init__(self, selection_callback,xsize=None,ysize=None):
        ''' Initialise as an empty vessel which gets populated when
        via the set_data method. Needs a selection callback for when
        the selection is changed. '''
        
        super(fieldSelector,self).__init__(title='Field Selector',xsize=xsize,ysize=ysize)   
        
        self.selection_callback=selection_callback
      
        # use a scrolled window to hold a list store for examining variables
        self.sw=gtk.ScrolledWindow()
        self.sw.set_policy(gtk.POLICY_NEVER,gtk.POLICY_AUTOMATIC)
        
        # create a tree view list store
        self.view=gtk.TreeView(None)
        self.view.set_search_column(1)          # search on field names
        self.view.set_rules_hint(True)          # nice alternating lines
        self.view.set_headers_clickable(True)   # can reorder on column headers

        # now set a liststore for the treeview.
        # [Index, Field Name, Length of X Array, Length of Y Array, Length of Z Array and Length of T Array]
        self.fieldStore = gtk.ListStore(int, str, int, int, int, int)
        
        # bind the store to the tree view
        self.view.set_model(self.fieldStore)      
        
        #The cell renderer is used to display the text in list store.
        self.fieldRenderer = gtk.CellRendererText() 
        for k,v in (('xpad',10),('size-points',8)):
            self.fieldRenderer.set_property(k,v)
            
        self.columns_are_setup=False
        
        # Allow multiple selections
        self.treeselector=self.view.get_selection()
        self.treeselector.set_mode(gtk.SELECTION_MULTIPLE)
        
        #Add the tree view to the scrolled window and the sw to self (frame)
        self.sw.add(self.view)
        self.add(self.sw)
            
    def _setColumns(self):
        ''' Set's the columns. We do this as late as possible, so
        the widget knows how big it is and get can the sizing right.'''
        
        #column headings
        headings=['Index', 'Field Name', 'X', 'Y', 'Z', 'T'] 
        i=0

        # work out how big we are so we can get the right column sizes
        allocation=self.get_allocation()
        xsize=allocation[2]-allocation[0]
        
        for h in headings:
            
            col=gtk.TreeViewColumn(h,self.fieldRenderer,text=i)
            col.set_sort_column_id(i)    # is sortable
            col.set_alignment(0.5)  
            i+=1
                
            #Each column is fixed width, dependant on screen size
            col.set_property('sizing',gtk.TREE_VIEW_COLUMN_FIXED)
            
            if h=='Field Name': 
                col.set_fixed_width(int(xsize * 0.5))
            else:
                col.set_fixed_width(int(xsize * 0.1))
            
            #Add the column created to the tree view
            self.view.append_column(col)
        
        #When the selection is changed the function selectionChanged is called.
        self.fieldChoice = self.view.get_selection()
        self.fieldChoice.connect("changed", self.changed)
        self.columns_are_setup=True
    
    def cf_field_to_columns(self,index,field):
        ''' Given a CF field, convert to list store data structure ''' 

        name='Unknown' #Catchall 
        if hasattr(field, 'id'): name=field.id
        if hasattr(field, 'ncvar'): name=field.ncvar
        if hasattr(field, 'short_name'): name=field.short_name 
        if hasattr(field, 'long_name'): name=field.long_name 
        if hasattr(field, 'standard_name'): name=field.standard_name

        nx=0
        ny=0
        nz=0
        nt=0
        #if field.coord('X') is not None: nx=len(field.item('X').array)
        #if field.coord('Y') is not None: ny=len(field.item('Y').array)
        #if field.coord('T') is not None: nt=len(field.item('T').array)

        #There can be multiple matches for a dimension.  In this case 
        #if len(field.items('Z')) == 1: 
        #    nz=len(field.item('Z').array)
        #elif len(field.items('Z')) > 1: 

        #There can be multiple matches for a dimension as in a Z that has a match and
        #also a Z that has a dimension.  
        for k, v  in field.items('X').iteritems():
            if v.isdimension is True: nx=len(field.item(k).array)
        for k, v  in field.items('Y').iteritems():
            if v.isdimension is True: ny=len(field.item(k).array)
        for k, v  in field.items('Z').iteritems():
            if v.isdimension is True: nz=len(field.item(k).array)
        for k, v  in field.items('T').iteritems():
            if v.isdimension is True: nt=len(field.item(k).array)

        return (index,name,nx,ny,nz,nt)
        
    def set_data(self,data):
        ''' Loop over fields in data file and display'''
        
        if not self.columns_are_setup: self._setColumns()
        
        # clear existing content, if any
        if len(self.fieldStore)<>0:
            self.fieldStore.clear()
            
        # loop over fields
        i=0
        for field in data:
            self.fieldStore.append(self.cf_field_to_columns(i,field))
            i+=1
       
        # Set the first row as selected straight away
        firstRow=self.fieldStore.get_iter_first()
        self.treeselector.select_iter(firstRow)
        
    def changed(self,treeSelection):
        ''' Called when the liststore changes '''
        (treestore, pathlist) = treeSelection.get_selected_rows()
        # at this point pathlist is a list of tuples that looks like
        # [((6,),(7,), ...]
        # These indices are the field indexes in the file!
        if len(pathlist) > 0:
            indices=[i[0] for i in pathlist]
            self.selection_callback(indices)
    
    def show(self):
        ''' Show widgets '''
        super(fieldSelector,self).show_all()
        
class cfGrid(object):
    ''' This is the grid part of a CF field, basically
    a convenience API into it. '''
    def __init__(self,field):
        ''' Initialise with a field. Provides two dictionaries
        which are keyed by the grid dimension names:
            .axes is the axes dictionary, and
            .drange is a set of min,max tuples for each axis.
        There must be a cleaner way to do this.
        # FIXME DAVID
        '''

        self.domain=field.domain
        self.axes={}
        self.drange={}
        self.names={}
        # FIXME, use CF grid information, not this ...
        #order=['X','Y','Z','T']
        #for k in order ...field.dim[k]
        self.shortNames={'dim0':'T','dim1':'Z','dim2':'Y','dim3':'X'}

        for axis in ['X','Y','Z','T']:
            if field.domain.axis(axis) is not None:  
                k=field.domain.axis(axis)
                cfdata=field.dim(k)
                self.axes[k]=cfdata
                self.drange[k]=min(cfdata.array),max(cfdata.array)
                self.names[k]=self.domain.axis_name(k)

        #Original code
        #for k in self.domain.data_axes():
        #    cfdata=field.dim(k)
        #    self.axes[k]=cfdata
        #    self.drange[k]=min(cfdata.array),max(cfdata.array)
        #    self.names[k]=self.domain.axis_name(k)




        
class gridSelector(guiFrame):
    ''' Provides a selector for choosing a sub-space in a multi-dimensional field'''
    def __init__(self,xsize=None,ysize=None):
        ''' Constuctor just sets some stuff up '''
        super(gridSelector,self).__init__(title='Grid Selector',xsize=xsize,ysize=ysize)
        self.vbox=gtk.VBox()
        self.add(self.vbox)
        self.shown=False
        
    def set_data(self,field):
        ''' Set data with just one field '''
        self.grid=cfGrid(field)
        self._makeGui()
        self.show()
        
    def _makeGui(self):
        ''' Make the GUI for a specific domain '''
        if self.shown:
            for d in self.sliders:
                self.sliders[d].destroy()
        self.sliders={}
        self.combos={}

        #counter to display two ranges of values in the sliders.  Futher ranges are set
        #to be the first value of the range
        #Display the axes in the order X, Y, Z, T
        dcount=0
        for axis in 'X', 'Y', 'Z', 'T':
           for dim in self.grid.axes:
              com='res=self.grid.axes[dim].'+axis
              exec(com)
              if res is True:
                 if len(self.grid.axes[dim].array) > 1: dcount = dcount + 1
                 box=self._makeSlider(dim, dcount)
                 self.sliders[dim]=box
                 self.vbox.pack_start(box,expand=False)

        self.shown=True
            


    def _makeSlider(self,dim, dcount):
        ''' Makes an entry for choosing array max and minima and
        for selecting a collapse operator. Note that the
        max grid selector slaves from the min grid selector,
        so users should always use that one first.'''
        maxcombo=arrayCombo(self.grid.axes[dim].array,'Max:')
        mincombo=arrayCombo(self.grid.axes[dim].array,'Min:',
                    callback=(self._linkCallback,maxcombo),
                    initial=self.grid.drange[dim][0])

        colcombo=collapseCombo(initial='range')
        self.combos[dim]=(mincombo,maxcombo,colcombo)        
        # can't do this one at initial value coz it gets reset by the link
        maxcombo.set_value(self.grid.drange[dim][1])

        if dcount > 2:
            #After the first two ranges set additional ranges to be the first value of the field.
            #If the axis is a pressure of height coordinate set it to be the maximim value of
            #the field i.e. that nearest the ground. 
         
            mincombo.set_value(self.grid.drange[dim][0])
            maxcombo.set_value(self.grid.drange[dim][0])

            height_units=['millibar', 'mbar', 'decibar', 'atmosphere', 'atm', 'pascal','Pa', 'hPa',\
                 'meter', 'metre', 'm', 'kilometer', 'kilometre', 'km']
            if hasattr(self.grid.axes[dim], 'Units'):
                if str(self.grid.axes[dim].Units) in height_units:
                    mincombo.set_value(max(self.grid.drange[dim]))
                    maxcombo.set_value(max(self.grid.drange[dim]))




        # all the box and border malarkey to make it look nice
        vbox=gtk.VBox()
        bbox=gtk.HBox()

        if len(self.grid.axes[dim].array)>1:
            bbox.pack_start(colcombo,padding=2)
            bbox.pack_start(mincombo,padding=2)
            bbox.pack_start(maxcombo,padding=2)
        else:
            bbox.pack_start(smallLabel('Value %s'%repr(self.grid.axes[dim].array[0])))

        vbox.pack_start(bbox,padding=5)
        frame=gtk.Frame(self.grid.names[dim])
        frame.set_border_width(5)
        frame.add(vbox)
        return frame
    
    def _linkCallback(self,target,value):
        ''' Takes a callback from a mincombo, which has been changed to value
        and updates the maxcombo to this value as an initial condition. '''
        target.set_value(value)
        
    def get_selected(self):
        ''' Return the combobox selections as they currently stand '''
        selections={}
        if not self.shown: return None
        for dim in self.combos:
            selections[dim]=(self.combos[dim][0].get_value(),
                             self.combos[dim][1].get_value(),
                             self.combos[dim][2].get_value())
        return selections
    
    def show(self):
        ''' Show all widgets '''
        super(gridSelector,self).show_all()
            
class guiInspect(guiFrame): 
    ''' Provides a file inspection widget '''
    def __init__(self,selector):
        super(guiInspect,self).__init__()
    def reset(self):
        ''' Handle changing the data file '''
        pass

class guiGallery(guiFrame):
    ''' Provides a gallery of plots '''
    def __init__(self,selector):
        super(guiGallery,self).__init__()
    def reset(self):
        ''' Handle changing the data file'''
        pass
        
class QuarterFrame(guiFrame):
    ''' Provides a frame with four sub-frames in the four quarters
    topLeft, topRight, bottomLeft, bottomRight. Each of these
    is exposed for use, e.g. self.topLeft ...
    Optional arguments include the xsize and ysize of the overall frame,
    otherwise defaults are used. '''
    # these are the default window ratio splits
    xsplit=[0.6,0.4]
    ysplit=[0.7,0.3]
    def __init__(self,xsize=None,ysize=None):
        ''' Initialise with optional quarter frame size '''
        super(QuarterFrame,self).__init__(xsize=xsize,ysize=ysize)
        self.set_shadow_type(gtk.SHADOW_NONE)  # no border
        # two boxes inside a vbox for the frames to sit in
        vbox=gtk.VBox()
        hboxTop=gtk.HBox()
        hboxBottom=gtk.HBox()
        vbox.pack_start(hboxTop)
        vbox.pack_start(hboxBottom)
        self.add(vbox)
        # find out the frame sizes
        ls,rs=int(self.xsplit[0]*self.xsize),int(self.xsplit[1]*self.xsize)
        ts,bs=int(self.ysplit[0]*self.ysize),int(self.ysplit[1]*self.ysize)
        # and then create them
        self.topLeft=guiFrame(xsize=ls,ysize=ts)
        self.topRight=guiFrame(xsize=rs,ysize=ts)
        self.bottomLeft=guiFrame(xsize=ls,ysize=bs)
        self.bottomRight=guiFrame(xsize=rs,ysize=bs)
        # don't want borders around the subframes:
        for f in [self.topLeft,self.topRight,self.bottomLeft,self.bottomRight]:
            f.set_shadow_type(gtk.SHADOW_NONE)
        # and place them in the boxes
        hboxTop.pack_start(self.topLeft)
        hboxTop.pack_start(self.topRight)
        hboxBottom.pack_start(self.bottomLeft)
        hboxBottom.pack_start(self.bottomRight)
    def show(self):
        ''' Show internal widgets '''
        self.show_all()
        
def makeDummy(standardname,longname):
    ''' Returns a dummy cf field object for testing, with
    standardname <standardname> and variablename <longname> from
    http://cfpython.bitbucket.org/docs/0.9.8.3/field_creation.html '''
    #---------------------------------------------------------------------
    # 1. Create the field's domain items
    #---------------------------------------------------------------------
    # Create a grid_latitude dimension coordinate
    dim0 = cf.DimensionCoordinate(properties={'standard_name': 'grid_latitude'},
                          data=cf.Data(np.arange(10.), 'degrees'))

    # Create a grid_longitude dimension coordinate
    dim1 = cf.DimensionCoordinate(data=cf.Data(np.arange(9.), 'degrees'))
    dim1.standard_name = 'grid_longitude'
    
    # Create a time dimension coordinate (with bounds)
    bounds = cf.CoordinateBounds(
    data=cf.Data([0.5, 1.5], cf.Units('days since 2000-1-1', calendar='noleap')))
    dim2 = cf.DimensionCoordinate(properties=dict(standard_name='time'),
                                  data=cf.Data(1, cf.Units('days since 2000-1-1',
                                                           calendar='noleap')),
                                  bounds=bounds)
    
    # Create a longitude auxiliary coordinate
    aux0 = cf.AuxiliaryCoordinate(data=cf.Data(np.arange(90).reshape(10, 9),
                                               'degrees_north'))
    aux0.standard_name = 'latitude'
    
    # Create a latitude auxiliary coordinate
    aux1 = cf.AuxiliaryCoordinate(properties=dict(standard_name='longitude'),
                                  data=cf.Data(np.arange(1, 91).reshape(9, 10),
                                               'degrees_east'))
    
    # Create a rotated_latitude_longitude grid mapping transform
    trans0 = cf.Transform(grid_mapping_name='rotated_latitude_longitude',
                          grid_north_pole_latitude=38.0,
                          grid_north_pole_longitude=190.0)
    
    # --------------------------------------------------------------------
    # 2. Create the field's domain from the previously created items
    # --------------------------------------------------------------------
    domain = cf.Domain(dim=[dim0, dim1, dim2],
                       aux=[aux0, aux1],
                       trans=trans0,
                       assign_axes={'aux1': ['dim1', 'dim0']})
    
    #---------------------------------------------------------------------
    # 3. Create the field
    #---------------------------------------------------------------------
    # Create CF properties
    properties = {'standard_name': standardname,
                  'long_name'    : longname,
                  'cell_methods' : cf.CellMethods('latitude: point')}
    
    # Create the field's data array
    data = cf.Data(np.arange(90.).reshape(9, 10), 'm s-1')
    
    # Finally, create the field
    f = cf.Field(properties=properties,
                 domain=domain,
                 data=data,
                 axes=domain.axes(['grid_long', 'grid_lat'], ordered=True))
                 
    return f
    
class TestCFutilities(unittest.TestCase):
    ''' Test methods for the CF utilities '''
    def setUp(self):
        ''' make some dummy CF data'''
        self.sname,self.lname='eastward_wind','East Wind'
        self.f=makeDummy(self.sname,self.lname)
        
    def test_cfkeyval(self):
        ''' test the cfkeyval method '''
        expecting={'long_name':'<b>long_name</b>: %s'%self.lname,
                   'standard_name':'<b>standard_name</b>: %s'%self.sname,
                   'cell_methods':'?'}
        # currently cf python doesn't return cell_methods in properties
        # even though I think it should. This test will break when it does,
        # coz expected will need to be filled out correctly and I haven't
        # gotten around to that.
        for p in self.f.properties:
            self.assertEqual(cfkeyvalue(self.f,p),expecting[p])
            print p,cfkeyvalue(self.f,p)
    def test_cfdimprops(self):
        ''' test the cfdimprops method doesn't crash '''
        p=cfdimprops(self.f)
    def test_arrayCombo(self):
        ''' Actually creates and runs a widget, so we turn this off
        after testing it properly '''
        win=gtk.Window()
        vector=np.arange(100.)
        x=arrayCombo(vector)
        x.set_value(10.)
        self.assertEqual(x.get_value(),10.)
        #win.add(x.box)
        #win.show_all()
        #gtk.main()




class cfview:
    ''' Provides the main frame for cfview '''
    def __init__(self, filename):
        ''' Create main window as a notebook with three panes:
                Discover
                Inspect
                Plot
            Provide a status window underneath and a toolbar above.
        '''
        
        window=gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.connect('delete_event',self.delete)
        window.set_border_width(cfgPadding)
        
        # box for all main window elements
        vbox=gtk.VBox()
        window.add(vbox)
        
        # get menubar
        menubar=self.get_mainMenu(window)
        vbox.pack_start(menubar,expand=False)
        
        # script record of what is going on 
        #self.script=script(debug=True)
        self.script=script()
        
        # notebook
        #nb=gtk.Notebook()
        #nb.show_tabs=True
        #vbox.pack_start(nb,padding=cfgPadding)
        #self.nb=nb
        #for a,p,m in [ ('Select',xconvLike,self.script)]:
        #            ('Inspect',guiInspect,self.selector),
        #            ('Gallery',guiGallery,self.selector),
        #           ]:
        #    label=gtk.Label(a)
        #    w=p(m)
        #    self.nb.append_page(w,label)
        #    setattr(self,a,w)
        #    w.show_all()
        

        w=xconvLike(self.script)
        vbox.pack_start(w)
        w.show_all()
        setattr(self,'Select',w)


        # status window
        #statusbar=gtk.Statusbar()
        #statusbar.set_has_resize_grip(False)
        #vbox.pack_start(statusbar,padding=cfgPadding,expand=False)
        
        self.w=window
        
        self.default_title=' cfview %s'%__version__
        self.set_title(self.default_title)
        
        window.show_all()
        
        if filename is not None:
            self.reset_with(filename)
        
    def selector(self):
        ''' Callback to mediate the various panes. May need to be
        a class with methods ... '''
        #FIXME
        pass
        
    def get_mainMenu(self,w):
        
        ''' Build a menuBar toolbar using the gtk uimanager '''
        
        ui = '''<ui>
            <menubar name="MenuBar">
                <menu action="File">
                    <menuitem action="Load"/>
                    <separator/>
                    <menuitem action="Save"/>
                    <separator/>
                    <menuitem action="Quit"/>
                </menu>
                <menu action="View">
                    <menuitem action="Code"/>
                    <separator/>
                    <menuitem action="Data"/>
                </menu>
                <menu action="Configure">
                    <menuitem action="Contour levels"/>
                    <separator/>
                    <menuitem action="Map settings"/>
                    <separator/>
                    <menuitem action="Vectors"/>
                    <separator/>
                    <menuitem action="Color scales"/>
                    <separator/>
                    <menuitem action="Reset all"/>
                </menu>

                <menu action="Help">
                    <menuitem action="Interface help"/>
                    <menuitem action="About"/>
                </menu>
            </menubar>
            </ui>
            '''
        uimanager = gtk.UIManager()
        
        accelgroup = uimanager.get_accel_group()
        w.add_accel_group(accelgroup)
        
        actiongroup=gtk.ActionGroup('cfview')

        actiongroup.add_actions ([
                ('File',None,'_File'),
                ('Load',gtk.STOCK_OPEN,'Load File',None,
                 'Load File',self.file_load),
                ('Save',gtk.STOCK_OPEN,'Save code',None,
                 'Save code',self.code_save),
                 ('Quit',gtk.STOCK_QUIT,'Quit',None,
                 'Quit',self.delete),
                ('View',None,'_View'),
                ('Code',gtk.STOCK_OPEN,'Code', None,
                'View plot code', code_show),
                ('Data',gtk.STOCK_OPEN,'Data', None,
                'View data',data_show),
                ('Configure',None,'_Configure'),
                ('Contour levels',gtk.STOCK_OPEN,'Contour levels', None,
                'Set contour levels', Config_contour_levels),
                ('Map settings',gtk.STOCK_OPEN,'Map settings', None,
                'Map settings', Config_map_settings),
                ('Vectors',gtk.STOCK_OPEN,'Vectors', None,
                'Vectors', Config_vectors),
                ('Color scales',gtk.STOCK_OPEN,'Color scales', None,
                'Color scales', Config_color_scales),
                ('Reset all',gtk.STOCK_OPEN,'Reset all', None,
                'Reset all', Config_reset_all),
                ('Help',None,'_Help'),
                ('Interface help',gtk.STOCK_HELP,'Interface help', None,
                'Interface help',self.help_interface),
                ('About',gtk.STOCK_HELP,'About', None,
                'About cfview',self.help_about),
                ])

        uimanager.insert_action_group(actiongroup, 0)
        uimanager.add_ui_from_string(ui)
        
        widget=uimanager.get_widget('/MenuBar')
        
        # make sure the help menu is on the right
        helpmenu = uimanager.get_widget('/MenuBar/Help')
        helpmenu.set_right_justified(True)         
        
        return widget
        
    def file_load(self,b):
        ''' Open a file for cfview. '''
        chooser=gtk.FileChooserDialog(title='Open data file',
                    action=gtk.FILE_CHOOSER_ACTION_OPEN,
                    buttons=(   gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
                                gtk.STOCK_OPEN,gtk.RESPONSE_OK),
                    )
        response=chooser.run()
        if response==gtk.RESPONSE_OK:
            newfile=chooser.get_filename()
            self.reset_with(newfile)
        chooser.destroy()

    def code_save(self,b):
        ''' Save code to make current plot'''
        chooser=gtk.FileChooserDialog(title='Save code to make current plot',
                    action=gtk.FILE_CHOOSER_ACTION_SAVE,
                    buttons=(   gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,
                                gtk.STOCK_SAVE,gtk.RESPONSE_OK),
                    )
        chooser.set_current_name('code.py')
        response=chooser.run()
        if response==gtk.RESPONSE_OK:
            savefile=chooser.get_filename()
            f=open(savefile, 'w')
            f.write(plotvars.code)
            f.close()
        chooser.destroy()



       

           


        # Apply any operators
        opstring=' '
        op_counter=0
        for d in grid:
            if grid[d][2]<>None:
                op_counter=op_counter+1
                axis_name=getattr(sfield.item(d), 'standard_name', d)
                code="sfield=sfield.collapse('%s',axes='%s')"%(grid[d][2],axis_name)
                exec(code)
                if op_counter == 1: plotvars.code=plotvars.code+'#Apply operators\n'
                plotvars.code=plotvars.code+code+'\n'
                opstring+='%s:%s '%(grid[d][2],sfield.domain.axis_name(d))


        print sfield

        
    def reset_with(self,filename):
        ''' Open dataset filename '''
        data=cf.read(filename)
        self.Select.set_data(data)
        #self.Inspect.reset()
        #self.Gallery.reset()
        #Reset any existing data
        self.domain={}
        self.axes={}
        self.drange={}
        self.names={}

        #self.script.open(filename)
        #self.script.start()
        plotvars.plot_code=''
        
    def help_about(self,b):
        ''' Provide an about dialog '''

        helpstr='<T>cfview '+(__version__)+'\
        \n\
        This is a pre-release version of NCAS cfview\n\
        \
        Credits to:\r\
        Bryan Lawrence - for the initial version of cfview\r\
        Mudit Gupta - for the prototype pygtk interface to cf-python/cfplot\r\
        David Hassell, Charles Roberts - for cf-python\r\
        Andy Heaps - for cf-plot\r\
        \n'
            
        CFview_help(helpstr, helpstr)

        
    def help_interface(self,b):
        ''' Provide a help dialog '''

        helpstr='<T>cfview '+(__version__)+'\
        \n\
        To load data type cfview filename on the command prompt or File -> Load File once cfview is running.\
        Data types that cfview can use are netCDF, Met Office PP and Fields files.\n\
        \
        <H>Making multiple plots\r\
        To make muliple plots select the n-up drop down menu in the Configure and Generate Plots section.\
        1x2 would give one plot in the x direction by two plots in the y direction.  If two contour plots\
        are made then the plot button will need to be pressed twice to make the separate plots before the\
        picture appears.\n\
        \
        <H>Making a vector plot\r\
        Select a vector plot from the Configure and Generate Plots panel. Then select the field as a\
        2-dimensional slice in the grid selector\
        This is usually a longitude-latitude slice but can also be a latitude-pressure or height\
        slice or a longitude-pressure/height slice.  Press the plot button to select this as the x component.\
        Select another slice for the y-component of the vector plot and then press the plot button to make a\
        vector plot.  Vector options such as string, interpolation and size are available from the Configure\
        drop down menu from the top bar of cfview.\
        \n'
            
        CFview_help(helpstr, helpstr)


    def delete(self,w=None,b=None):
        ''' Delete menu '''
        gtk.main_quit()
        return False
        
    def set_title(self,title):
        ''' Set window title '''
        self.w.set_title(title)

class xconvLike(QuarterFrame):
    ''' Set up an xconv like set of panels with 
            field selection on the top left
            field metadata on the bottom left
            grid metadata on the bottom right
            and a combination of grid selection and actions on the top right
        which of course isn't like xconv, but is more cf-like ...
        
        '''
    def __init__(self,script):
        ''' Initialise with the script recorder '''
        super(xconvLike,self).__init__()
        self.script=script
        self.fieldSelector=fieldSelector(self.selection)
        self.fieldMetadata=fieldMetadata()
        self.gridMetadata=gridMetadata()
        self.gridSelector=gridSelector(ysize=200)
        self.topLeft.add(self.fieldSelector)
        self.bottomLeft.add(self.fieldMetadata)
        self.bottomRight.add(self.gridMetadata)
        self._topRight()
        
    def _topRight(self):
        ''' Combination frame for the top right '''
        topRv=gtk.VBox()
        topRv.pack_start(self._actionBox(),padding=2)
        topRv.pack_start(self.gridSelector,expand=True,fill=True)
        self.topRight.add(topRv)
        
    def _actionBox(self):
        ''' Provides the buttons and callbacks to the actual actions which 
        the routine supports. '''
        #actionBox=pcw.plotChoices(callback=self._plot,ysize=90)
        actionBox=plotChoices(callback=self._plot,ysize=90)
        actionBox.show()
        return actionBox
        
    def _plot(self,w,plotOptions):
        ''' Executes a plot given the information returned from the various
        selectors and configuration widgets. In practice we have
            - the grid selector telling us about the data slicing,
            - the field selector telling us about what data to plot, and
            - the plot choices widget giving us the cf plot arguments.
        All we have to do here is configure the plot (possibly including
        dealing with multiple plots on one page). 
        '''

        #Define the start of the plotting script 
        script_start="#\n# script generated by cfview version "+__version__+"\n#\
                      \nimport cf, cfplot as cfp\n\n"
        

        #Extract data
        code='sfield=self.fields[0]'
        exec(code)






        #Single plot
        if plotOptions['nup'] == '1': 
           plotvars.nplots=1
           if plotvars.plot_counter == 0: plotvars.code=script_start


        #Multiple plots
        if plotOptions['nup'] != '1' and plotvars.pos == 0:
           #Set number of plots in gplot from interface nup parameter
           if plotOptions['nup'] == '2x1': 
              code="cfp.gopen(columns=2, rows=1)"
              plotvars.nplots=2
           if plotOptions['nup'] == '1x2': 
              code="cfp.gopen(columns=1, rows=2, orientation='portrait')"
              plotvars.nplots=2
           if plotOptions['nup'] == '2x2': 
              code="cfp.gopen(columns=2, rows=2)"
              plotvars.nplots=4
           if plotOptions['nup'] == '3x2':
              code="cfp.gopen(columns=3, rows=2)"
              plotvars.nplots=6
           if plotOptions['nup'] == '2x3':
              code="cfp.gopen(columns=2, rows=3, orientation='portrait')"
              plotvars.nplots=6
           if plotOptions['nup'] == '3x3':
              code="cfp.gopen(columns=3, rows=3)"
              plotvars.nplots=9
           exec("\n"+code)
           plotvars.code=script_start
           plotvars.code=plotvars.code+code+'\n'

           #Position at first plot
           plotvars.pos=1
           code="cfp.gpos(pos=1)"
           exec(code)
           plotvars.code=plotvars.code+code+'\n\n'
           plotvars.title=''


        

        grid=self.gridSelector.get_selected()
        # check we have some data
        if grid is None:
            dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,
                    'Please select some data before trying to plot!')
            dialog.run()
            dialog.destroy()
            return
       
        # Operate on the first field
        plotvars.code=plotvars.code+'#Read data\n'
        plotvars.code=plotvars.code+"sfield=cf.read('"+sfield.file+"')"+\
                      plotvars.field_selected+'\n\n'




        #Bryan's original code
        # first let's do the subspace selection (if any):
        #kwargs={}
        #self.script.add('kwargs={}',hash=True)
        #for d in grid:
        #    kwargs[d]=cf.wi(grid[d][0],grid[d][1])
        #    self.script.add("kwargs['%s']=cf.wi(%s,%s)"%(d,grid[d][0],grid[d][1]))
        #sfield=sfield.subspace(**kwargs)
        #self.script.add('sfield=sfield.subspace(**kwargs)')


        #Check if field is subsetted by the user
        subset_counter=0
        subset_string=''
        for d in grid:
            dmin=np.min(sfield.item(d).array)
            dmax=np.max(sfield.item(d).array)
            tol=abs((dmin-int(dmin))/1e3)

            axis_name=getattr(sfield.item(d), 'standard_name', d)

            if len(sfield.item(d).array) == 1: subset_string=subset_string+\
               ' '+axis_name+':'+str(grid[d][0])+' '

            #Take account of multiple axes with almost the same name
            #by adding the 'exact' keyword to the subset
            naxes=len(sfield.items(axis_name))
            exact_str=''
            if naxes > 1: exact_str="'exact',"


            if (abs(dmin-grid[d][0]) > tol) or (abs(dmax-grid[d][1]) > tol):
               subset_counter=subset_counter+1
               if (abs(grid[d][1]-grid[d][0]) <= tol):
                  #Single value
                  code="sfield=sfield.subspace(%s%s=%s)"%(exact_str,axis_name,repr(grid[d][0]))
                  subset_string=subset_string+' '+axis_name+':'+repr(grid[d][0])+' '
               else:
                  #Range of values
                  code="sfield=sfield.subspace(%s%s=cf.wi(%s,%s))"%(exact_str,axis_name,repr(grid[d][0]),repr(grid[d][1]))
               
               exec(code)
               if subset_counter == 1: plotvars.code=plotvars.code+'#Subspace the data\n'
               plotvars.code=plotvars.code+code+'\n'

            

        if subset_counter > 0: plotvars.code=plotvars.code+'\n'


        #Do we have to apply any operators?
        #Apply these all together to sfield as if longitude and latitude are meaned at the same
        #time this is a global area weighted mean.  If applied individually it is a linear mean in
        #longitude followed by a linear mean in latitude.
        opstring=' '
        op_counter=0
        for d in grid:
            if grid[d][2]<>None:
                op_counter=op_counter+1
                if op_counter > 1: opstring=opstring+' '
                axis_name=getattr(sfield.item(d), 'standard_name', d)
                opstring+='%s: %s'%(axis_name,grid[d][2])

        if op_counter >0 :
           code="sfield=sfield.collapse('"+opstring+"')"
           plotvars.code=plotvars.code+'#Apply operators\n'
           plotvars.code=plotvars.code+code+'\n\n'
           exec(code)
 

        plotvars.data=sfield

        #Error message if data not of right dimensionality
        ndim=len(sfield.axes(size=cf.gt(1)))
        plot_type=plotOptions['con']['ptype']
        if ndim == 1: plot_type=0
        if ndim < 1 or ndim >2:
           dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                  gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,
                  'Please select  1 or 2 dimensional data to plot')
           dialog.run()
           dialog.destroy()
           return

        #Work out the type of plot requested
        if plot_type == 0 or plot_type == 1:  plotvars.plot_finish=1
        if plot_type == 2:  plotvars.plot_finish=2
        if plot_type == 3:  plotvars.plot_finish=3


        # now we know the shape we can check that the plotting options
        # and data shape are consistent.
        #message=pcw.checkConsistency(sfield,plotOptions)
        message=checkConsistency(sfield,plotOptions)

        if message <>'':
            # We currently don't know how to plot it
            dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
            dialog.run()
            dialog.destroy()
            return
        
        # ok we really can plot this thing!
        
        # get more titles, and slicing information for multiple plots
        #tsList=pcw.getSlicesAndTitles(sfield,plotOptions)
        tsList=getSlicesAndTitles(sfield,plotOptions)

        #reset cfplot before any plot to clear previous settings
        if plotvars.pos == 0: cfp.reset()


        #Check colour scale inputs are valid
        if plotvars.cscale_white != '':
            message=''
            for val in plotvars.cscale_white.split():
                try:
                    float(val)
                except ValueError:
                    message=message+'colour scale white setting = '+val+ ' is not a valid number\n'

            if message != '':
                dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                dialog.run()
                dialog.destroy()
                return

        if plotvars.cscale_ncols != '':
               message=''
               try:
                   float(plotvars.cscale_ncols)
               except ValueError:
                   message=message+'colour scale number of colours setting = '+val+ ' is not a valid number\n'

               if message != '':
                   dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                   gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                   dialog.run()
                   dialog.destroy()
                   return


        if plotvars.cscale_above != '':
               message=''
               try:
                   float(plotvars.cscale_above)
               except ValueError:
                   message=message+'colour scale above setting = '+val+ ' is not a valid number\n'

               if message != '':
                   dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                   gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                   dialog.run()
                   dialog.destroy()
                   return

        if plotvars.cscale_below != '':
               message=''
               try:
                   float(plotvars.cscale_below)
               except ValueError:
                   message=message+'colour scale below setting = '+val+ ' is not a valid number\n'

               if message != '':
                   dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                   gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                   dialog.run()
                   dialog.destroy()
                   return



        #Make up the cscale command if any changes required
        code=''
          
        if plotvars.cscale_auto_set is False: 
            code="'"+plotvars.cscale+"'"
            if plotvars.cscale_reverse_set is True: code=code+', reverse=True'
 
            if plotvars.cscale_white != '': 
                if code != '': code=code+', '
                code=code+'white='
                nvals=np.size(plotvars.cscale_white.split())
                if nvals == 1:
                    code=code+plotvars.cscale_white
                else:
                    code=code+'['
                    count=0
                    for val in plotvars.cscale_white.split():
                        code=code+val
                        if count < nvals-1: code=code+','
                        count=count+1
                    code=code+']'

            if plotvars.cscale_ncols != '': 
                if code != '': code=code+', '
                code=code+'ncols='+plotvars.cscale_ncols

            if plotvars.cscale_above != '': 
                if code != '': code=code+', '
                code=code+'above='+plotvars.cscale_above

            if plotvars.cscale_below != '': 
                if code != '': code=code+', '
                code=code+'below='+plotvars.cscale_below

            if code != '':
                code='cfp.cscale('+code+')'
                exec(code)
                plotvars.code=plotvars.code+'\n#Set colour scale\n'
                plotvars.code=plotvars.code+code+'\n\n'

         
 
        if plot_type == 0:
           #Plot a graph if 1D            
           #plotvars.code=plotvars.code+'#Select data for plot\n'
           #code='import numpy as np'
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #xtitle=''                 
           #yunits='Value'
           #code="xvals=np.squeeze(sfield.array)"
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #plotvars.title=sfield.file+opstring+subset_string
           #for mydim in sfield.items():
           #   vals=np.squeeze(sfield.item(mydim).array)                 
           #   if np.size(vals) > 1: 
           #      xpts=vals
           #      #code="xpts=np.squeeze(sfield.item('%s').array)"%mydim
           #      axis_name=getattr(sfield.item(mydim), 'standard_name', mydim)
           #      code="xpts=np.squeeze(sfield.item('%s').array)"%axis_name
           #      plotvars.code=plotvars.code+code+'\n'
           #      units='Value'
           #      #xtitle
           #      xtitle=getattr(sfield.item(mydim), 'standard_name', 'NoName')
           #      if xtitle == 'NoName': xtitle=getattr(sfield.item(mydim), 'long_name', 'NoName')
           #      if xtitle == 'NoName': xtitle=getattr(sfield.item(mydim), 'short_name', 'NoName')
           #      if xtitle == 'NoName': xtitle=getattr(sfield.item(mydim), 'ncvar', 'NoName')
           #      xunits=getattr(sfield.item(mydim), 'units', 'No units')
           #      #ytitle
           #      if hasattr(sfield, 'id'): ytitle=sfield.id
           #      if hasattr(sfield, 'ncvar'): ytitle=sfield.ncvar
           #      if hasattr(sfield, 'short_name'): ytitle=sfield.short_name 
           #      if hasattr(sfield, 'long_name'): ytitle=sfield.long_name 
           #      if hasattr(sfield, 'standard_name'): ytitle=sfield.standard_name
           #      if hasattr(sfield, 'Units'): yunits=sfield.Units
             

           plotvars.code=plotvars.code+'\n#Make a graph plot\n'

           code="cfp.lineplot(sfield)"
           exec(code)
           plotvars.code=plotvars.code+code+'\n'

           #Open the plot if a single plot
           #if plotvars.nplots == 1:
           #   code="cfp.gopen()"
           #   exec("\n"+code)
           #   plotvars.code=plotvars.code+code+'\n'

           #Plot the graph
           #code="cfp.plotvars.plot.tick_params(direction='out', which='both')"
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #code="cfp.plotvars.plot.set_xlabel('%s(%s)')"%(xtitle,xunits)
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #code="cfp.plotvars.plot.set_ylabel('%s(%s)')"%(ytitle,yunits)
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #code="cfp.plotvars.plot.plot(xpts, xvals)"
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #code="cfp.plotvars.plot.set_title('%s')"%plotvars.title
           #exec(code)
           #plotvars.code=plotvars.code+code+'\n'
           #Close the plot if a single plot
           if plotvars.nplots != 1:
              plotvars.pos=plotvars.pos+1
              if plotvars.pos <= plotvars.nplots: 
                 code="cfp.gpos(pos=%s)"%plotvars.pos
                 exec(code)
                 plotvars.code=plotvars.code+'\n#Move to next plot position\n'
                 plotvars.code=plotvars.code+code+'\n\n'



           #   code="cfp.gclose()"
           #   exec(code)
           #   plotvars.code=plotvars.code+code+'\n'
           #else:
           #   #Move onto next plot
           #   plotvars.pos=plotvars.pos+1
           #   if plotvars.pos <= plotvars.nplots: 
           #      code="cfp.gpos(pos=%s)"%plotvars.pos
           #      exec(code)
           #      plotvars.code=plotvars.code+'\n#Move to next plot position\n'
           #      plotvars.code=plotvars.code+code+'\n\n'



        #vector plot
        if ndim == 2 and (plot_type == 2 or plot_type == 3):
           fname=''
           units=''
           if hasattr(sfield, 'ncvar'): fname=sfield.ncvar
           if hasattr(sfield, 'short_name'): fname=sfield.short_name 
           if hasattr(sfield, 'long_name'): fname=sfield.long_name 
           if hasattr(sfield, 'standard_name'): fname=sfield.standard_name
           if hasattr(sfield, 'Units'): units=str(sfield.Units)


           #Work out whether to store the u vector data or to make the vector plot
           make_vector=0
           store_vector=0
           if plot_type == 2 and plotvars.plot_counter == 1:  make_vector=1
           if plot_type == 2 and plotvars.plot_counter == 0:  store_vector=1
           if plot_type == 3 and plotvars.plot_counter == 2:  make_vector=1
           if plot_type == 3 and plotvars.plot_counter == 1:  store_vector=1

              
           
           if make_vector == 1:
              #Make the vector plot
              plotvars.title=plotvars.title+plotvars.vect_title+sfield.file+' '+fname+'('+units+')'+\
                             opstring+subset_string
              
              ufield=plotvars.vect_ufield
              vfield=sfield
              plotvars.code=plotvars.code+'vfield=sfield\n\n'
              
              code='cfp.vect(u=ufield, v=vfield'           

              if plotvars.vect_auto_set is True:
                  exec('key_length=str(int(np.max((np.sqrt(ufield.array**2+vfield.array**2))/2)))')
                  code=code+', key_length='+key_length+', scale=100'
              else:
                  #Check inputs are numbers
                  message=''
                  for val in ['plotvars.vect_scale', 'plotvars.vect_length']:
                      exec('myval='+val)
                      try:
                         float(myval)
                      except ValueError:
                         message=message+val+'='+str(myval)+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return


                  #Check length and scale > 0
                  if  float(plotvars.vect_length) <= 0 or float(plotvars.vect_scale) <= 0:
                      message='Vector length and scale must be > 0.'
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return

                  #Add to vect command
                  code=code+',key_length='+plotvars.vect_length+', scale='+plotvars.vect_scale
              

              #Add stride if required
              if plotvars.vect_stride_set is True:
                  stridevals=plotvars.vect_stride
                  strides=stridevals.split()
                  #Check inputs are numbers
                  message=''
                  for stride in strides:
                      try:
                          float(stride)
                      except ValueError:
                          message='Vector stride ='+stride+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return


                  #Check inputs are integers
                  message=''
                  for stride in strides:
                      try:
                          int(stride)
                      except ValueError:
                          message='Vector stride ='+stride+ ' is not an integer\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return



                  #Check stride >= 1
                  message=''
                  for stride in strides:
                      if  float(stride) <= 1:
                          message='Vector stride must be >= 1.'
                          dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                          gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                          dialog.run()
                          dialog.destroy()
                          return

                  if np.size(strides) == 1: code=code+' ,stride='+strides[0]
                  if np.size(strides) == 2: code=code+' ,stride=['+strides[0]+','+strides[1]+']'

              #Add pts if required
              if plotvars.vect_pts_set is True:
                  ptsvals=plotvars.vect_pts
                  pts=ptsvals.split()
                  #Check inputs are numbers
                  message=''
                  for pt in pts:
                      try:
                          float(pt)
                      except ValueError:
                          message='Vector stride ='+pt+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return


                  #Check inputs are integers
                  message=''
                  for pt in pts:
                      try:
                          int(pt)
                      except ValueError:
                          message='Vector interpolation ='+pt+ ' is not an integer\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return



                  #Check pts >= 1
                  message=''
                  for pt in pts:
                      if  float(pt) < 1:
                          message='Vector interpolation must be >= 1.'
                          dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                          gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                          dialog.run()
                          dialog.destroy()
                          return

                  if np.size(pts) == 1: code=code+' ,pts='+pts[0]
                  if np.size(pts) == 2: code=code+' ,pts=['+pts[0]+','+pts[1]+']'

 
             

              #Finish off the vect command
              code=code+", title='%s')"%plotvars.title
              exec(code)
              plotvars.code=plotvars.code+'\n#Make vector plot\n'
              plotvars.code=plotvars.code+code+'\n\n'

              #reset counters and move on to the next plot
              plotvars.vect_ufield=None
              plotvars.vect_title=''
              plotvars.title=''
              plotvars.pos=plotvars.pos+1
              if plotvars.pos <= plotvars.nplots and plotvars.nplots > 1: 
                 code="cfp.gpos(pos=%s)"%plotvars.pos
                 exec(code)                    
                 plotvars.code=plotvars.code+'\n#Move to next plot position\n'
                 plotvars.code=plotvars.code+code+'\n\n'
              plotvars.plot_counter=plotvars.plot_counter+1


           if store_vector == 1:
              #Store u field in plotvars for use next time around
              plotvars.vect_ufield=sfield
              if plot_type == 3: plotvars.title=plotvars.title+'\\n'
              plotvars.vect_title=sfield.file+' '+fname+'('+units+')'+opstring+subset_string+'\\n'
              plotvars.plot_counter=plotvars.plot_counter+1
              plotvars.code=plotvars.code+'ufield=sfield\n\n'



        #Contour plot
        if ndim == 2 and (plot_type == 1 or plot_type == 3):
           proceed=1
           if plot_type == 3 and plotvars.plot_counter > 1: proceed=0
           if proceed == 1:
              if plotvars.nplots == 1 and plot_type == 3: 
                 code="cfp.gopen()"
                 exec("\n"+code)
                 plotvars.code=plotvars.code+code+'\n'

                 
              #if plotOptions['mapset']['proj']<>'cyl': 
              #   code="cfp.mapset(proj='%s')"%(plotOptions['mapset']['proj'])
              #   exec(code)
              #   plotvars.code=plotvars.code+'\n#Set mapping\n'
              #   plotvars.code=plotvars.code+code+'\n\n'


              plotvars.title=sfield.file+opstring+subset_string
              
              #Set levels if set in contour_levels
              #Reset levels to automatic
              exec('cfp.levs()')

              if plotvars.levs_set is True:
                  #Check inputs are numbers
                  message=''
                  for val in ['plotvars.levs_min', 'plotvars.levs_max', 'plotvars.levs_step']:
                      exec('myval='+val)
                      try:
                         float(myval)
                      except ValueError:
                         message=message+val+'='+str(myval)+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return


                  #Check min < max and step > 0
                  if  float(plotvars.levs_min) >= float(plotvars.levs_max) or float(plotvars.levs_step) <= 0:
                      message='Levels must be set so that the \n minimum < maximum and step >0.'
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return
 
                  code="cfp.levs(min="+plotvars.levs_min+", max="+plotvars.levs_max+", step="+plotvars.levs_step


              if plotvars.levs_manual_set is True:
                  #Check inputs are numbers
                  message=''
                  for val in plotvars.levs_manual.split():
                      try:
                         float(val)
                      except ValueError:
                         message=message+val+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return

                  #Check levels are ascending
                  message=''
                  levs=plotvars.levs_manual.split()
                  nvals=np.size(plotvars.levs_manual.split())
                  count=0
                  for i in np.arange(nvals-1):
                      if float(levs[i+1]) - float(levs[i]) <= 0:
                          message=message+'Levels are not ascending '+ levs[i]+' '+levs[i+1]+'\n'
                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return


 
                  code="cfp.levs(manual=["
                  nvals=np.size(plotvars.levs_manual.split())
                  count=0
                  for val in plotvars.levs_manual.split():
                      code=code+val
                      if count < nvals-1: code=code+','
                      count=count+1
                  code=code+']' 

              #Add on colour bar extensions if specified
              if plotvars.levs_set is False and plotvars.levs_manual_set is False: 
                  if  plotvars.levs_extend_lower is False or plotvars.levs_extend_upper is False:
                      code='cfp.levs('
              if plotvars.levs_set is True or plotvars.levs_manual_set is True: 
                  if  plotvars.levs_extend_lower is False or plotvars.levs_extend_upper is False:
                      code=code+','
              if plotvars.levs_set is False or plotvars.levs_manual_set is False: 
                  if plotvars.levs_extend_lower is False and plotvars.levs_extend_upper is True:
                      code=code+"extend='max'"
                  if plotvars.levs_extend_lower is True and plotvars.levs_extend_upper is False:
                      code=code+"extend='min'"
                  if plotvars.levs_extend_lower is False and plotvars.levs_extend_upper is False:
                      code=code+"extend='neither'"
                  code=code+")"


              if code[:8] == 'cfp.levs':
                   exec(code)
                   plotvars.code=plotvars.code+'#Set levels\n'
                   plotvars.code=plotvars.code+code+'\n\n'

            
              #Map settings
              code=''
              #Set the cylindrical map limits if changed
              if plotvars.proj == 'cyl':
                  code=''
                  if plotvars.lonmin != '-180' or plotvars.lonmax != '180' or plotvars.latmin != '-90' \
                      or plotvars.latmax != '90':
                       #Check inputs are numbers
                       message=''
                       for val in ['plotvars.lonmin', 'plotvars.lonmax', 'plotvars.latmin', 'plotvars.latmax']:
                           exec('myval='+val)
                           try:
                               float(myval)
                           except ValueError:
                               message=message+val[9:]+'='+str(myval)+ ' is not a valid number\n'

                       if message != '':
                           dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                           gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                           dialog.run()
                           dialog.destroy()
                           return


                       #Check lonmin < lonmax and latmin < latmax
                       if  float(plotvars.lonmin) >= float(plotvars.lonmax) or float(plotvars.latmin) >= float(plotvars.latmax):
                           message='lonmin must be < lonmax and latmin must be < latmax.'
                           dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                           gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                           dialog.run()
                           dialog.destroy()
                           return
 
                       code="cfp.mapset(lonmin="+plotvars.lonmin+", lonmax="+plotvars.lonmax\
                            +", latmin="+plotvars.latmin+", latmax="+plotvars.latmax


              #Polar plot if selected
              if plotvars.proj == 'npstere' or plotvars.proj == 'spstere': 
                  #Check inputs are numbers
                  message=''
                  if plotvars.lon_0 != '0' or plotvars.boundinglat != '0':
                      for val in ['plotvars.lon_0', 'plotvars.boundinglat']:
                           exec('myval='+val)
                           try:
                               float(myval)
                           except ValueError:
                               message=message+val[9:]+'='+str(myval)+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return

                  code="cfp.mapset(proj='"+plotvars.proj+"'"
                  if plotvars.lon_0 != '0': code=code+', lon_0='+plotvars.lon_0
                  if plotvars.boundinglat != '0': code=code+', boundinglat='+plotvars.boundinglat

              #Change resolution if requested
              if plotvars.resolution !='c':
                  if code == '': 
                      code="cfp.mapset(resolution='"+plotvars.resolution+"'"
                  else:
                      code=code+",resolution='"+plotvars.resolution+"'"
              
              #Run code if changes have been made    
              if code != '':
                  code=code+')'
                  exec(code)
                  plotvars.code=plotvars.code+'#Set map projection and limits\n'
                  plotvars.code=plotvars.code+code+'\n\n'


              #Set continent thickness and colour if required
              if plotvars.continent_thickness != '1.5' or plotvars.continent_color != 'k':
                  #Check continent_thinckness is a valid number
                  message=''
                  if plotvars.continent_thickness != '1.5':
                      exec('myval='+plotvars.continent_thickness)
                      try:
                          float(myval)
                      except ValueError:
                          message='continent_thickness='+plotvars.continent_thickness+ ' is not a valid number\n'

                  if message != '':
                      dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                      gtk.MESSAGE_ERROR,gtk.BUTTONS_CLOSE,message)
                      dialog.run()
                      dialog.destroy()
                      return

                  #Generate cf-plot setvars command as required
                  code='cfp.setvars('
                  if plotvars.continent_thickness != '1.5': code=code+"continent_thickness='"+plotvars.continent_thickness+"',"
                  if plotvars.continent_color != 'k': code=code+"continent_color='"+plotvars.continent_color+"'"
                  code=code+')'
                  exec(code)
                  plotvars.code=plotvars.code+'#Set continent attributes\n'
                  plotvars.code=plotvars.code+code+'\n\n'



              #Assemble contour command from plotOptions
              code="cfp.con(sfield"

              contour_style=plotOptions['con']['contour_style']
              contour_line=plotOptions['con']['contour_line']
              if contour_style == 1: 
                 if contour_line == 2: code=code+",lines=0"
                 if contour_line == 3: code=code+",lines=1, negative_linestyle=1"
              if contour_style == 2: 
                 if contour_line == 1: code=code+",blockfill=1,lines=1"
                 if contour_line == 2: code=code+",blockfill=1, lines=0"
                 if contour_line == 3: code=code+",blockfill=1, lines=1, negative_linestyle=1"
              if contour_style == 3: 
                 if contour_line == 1: code=code+",fill=0,lines=1"
                 if contour_line == 2: code=code+",fill=0, lines=0"
                 if contour_line == 3: code=code+",fill=0, lines=1, negative_linestyle=1"


              if plotOptions['con']['line_labels'] is False:
                  code=code+', line_labels=False'

              #if plotOptions['con']['filled']<>1:code=code+",fill=0"
              code=code+",title='%s'"%plotvars.title
              if plotOptions['con']['colorbar_orientation']=='vertical': \
                 code=code+",colorbar_orientation='vertical'"
              if plotOptions['con']['colorbar'] is None: code=code+",colorbar=None"
              if plotOptions['con']['xlog'] is True: code=code+", xlog=1"
              if plotOptions['con']['ylog'] is True: code=code+", ylog=1"
              code=code+')'

              exec(code)
              plotvars.code=plotvars.code+'#Make contour plot\n'
              plotvars.code=plotvars.code+code+'\n\n'

              if plot_type ==1: plotvars.title=''
              plotvars.plot_counter=plotvars.plot_counter+1
              if plot_type == 1:  
                 plotvars.pos=plotvars.pos+1
                 if plotvars.pos <= plotvars.nplots and plotvars.nplots > 1: 
                    code="cfp.gpos(pos=%s)"%plotvars.pos
                    exec(code)
                    plotvars.code=plotvars.code+'\n#Move to next plot position\n'
                    plotvars.code=plotvars.code+code+'\n\n'



        #Reset counters if a single plot
        if plotvars.nplots == 1:
           plot_reset=0
           if plot_type == 0 or plot_type == 1:  plot_reset=1
           if plot_type == 2 and plotvars.plot_counter == 2: plot_reset=1
           if plot_type == 3 and plotvars.plot_counter == 3: 
              code="cfp.gclose()"
              exec(code)
              plotvars.code=plotvars.code+code+'\n'
              plot_reset=1

           if plot_reset == 1:
              plotvars.nplots=1
              plotvars.pos=0
              plotvars.plot_counter=0
              plotvars.plot_finish=0
              plotvars.title=''


        #Reset counter if the last plot of a contour and vector plot
        if plotvars.nplots > 1 and plot_type == 3 and plotvars.plot_counter == 3:
           plotvars.plot_counter=0

        #Close and view if multiple plot and it is also the last plot
        if plotvars.nplots > 1 and plotvars.pos > plotvars.nplots:
           plotvars.nplots=1
           plotvars.pos=0
           plotvars.plot_counter=0
           plotvars.plot_finish=0
           code="cfp.gclose()"
           exec(code)
           plotvars.code=plotvars.code+code+'\n'





        



          


    def set_data(self,data):
        ''' Set with an open cf dataset object '''
        self.cf_dataset=data
        self.fieldSelector.set_data(data)
        #print 'In data selection'


    def selection(self,data):
        ''' Provided to fieldSelector as a callback, so that when
        fields are selected, the metadata and grid selectors are
        updated. '''
        fields=[self.cf_dataset[i] for i in data]
        plotvars.field_selected='%s'%data
        self.fieldMetadata.set_data(fields)
        self.gridMetadata.set_data(fields)
        self.gridSelector.set_data(fields[0]) 
        self.fields=fields
        
class script:
    ''' Provides a scriptable copy of what the gui is doing '''
    #def __init__(self,debug=True):
    def __init__(self):
        ''' Construct the file header. If debug, write actions 
        as we go along. '''
        self.content=''

    def start(self):
       self.content=\
'''
import cf
import cfplot as cfp
#
# script generated by cfview version %s
#
                    '''%__version__



    def add(self,command,hash=False):
        ''' Add a command to the script, preceded by a hash if hash true '''
        if hash: self.content+='\n#\n'
        # we have to parse the command for things that need escaping
        command=command.replace('\n','\\n')
        self.content+='%s\n'%command
        #if self.debug:self.debug.write('%s\n'%command)

    def clear(self):
       self.content=''



#Show code for making plot in a text window
class code_show:
    def close_application(self, widget):
        #widget.destroy()
        self.text_window.destroy()

    def __init__(self, widget):
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_resizable(True)  
        window.connect("destroy", self.close_application)
        window.set_title("cfview - code to make plot")
        window.set_border_width(0)
        window.set_size_request(700, 900)

        box1 = gtk.VBox(False, 0)
        window.add(box1)
        box1.show()

        box2 = gtk.VBox(False, 10)
        box2.set_border_width(10)        
        box1.pack_start(box2, True, True, 0)
        box2.show()

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        textview = gtk.TextView()
        textbuffer = textview.get_buffer()
        sw.add(textview)
        sw.show()
        textview.show()

        box2.pack_start(sw)
        
        #Set the text view to be plot code.
        textbuffer.set_text(plotvars.code)

        hbox = gtk.HButtonBox()
        hbox.set_size_request(200,100)
        box2.pack_start(hbox, False, False, 0)
        box2.set_size_request(200,100)
        hbox.show()

        vbox = gtk.VBox()
        vbox.show()
        hbox.pack_start(vbox, False, False, 0)


        box2 = gtk.VBox(False, 10)
        box2.set_border_width(10)
        box1.pack_start(box2, False, True, 0)
        box2.show()

        button = gtk.Button("close")
        button.connect("clicked", self.close_application)
        box2.pack_start(button, True, True, 0)
        button.set_flags(gtk.CAN_DEFAULT)
        button.grab_default()
        button.show()

        self.text_window=window
        self.text_window.show()

class data_show:
    def close_application(self, widget):
        self.text_window.destroy()

            
    def __init__(self, widget):
        ''' View selected data including any subsets or transforms'''

        sfield=plotvars.data     


        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_resizable(True)  
        window.connect("destroy", self.close_application)
        window.set_title("cfview - data as selected")
        window.set_border_width(0)
        window.set_size_request(700, 900)

        box1 = gtk.VBox(False, 0)
        window.add(box1)
        box1.show()

        box2 = gtk.VBox(False, 10)
        box2.set_border_width(10)        
        box1.pack_start(box2, True, True, 0)
        box2.show()

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        textview = gtk.TextView()
        textbuffer = textview.get_buffer()
        sw.add(textview)
        sw.show()
        textview.show()

        box2.pack_start(sw)
        
        #Create tags using pango
        h_tag = textbuffer.create_tag( "h", size_points=12, weight=pango.WEIGHT_BOLD) 
        b_tag = textbuffer.create_tag( "b", size_points=10, weight=pango.WEIGHT_BOLD) 
        #Use courier font for data tables
        t_tag = textbuffer.create_tag( "t", size_points=10, font="Courier") 
        tb_tag = textbuffer.create_tag( "tb", size_points=10, font="Courier", weight=pango.WEIGHT_BOLD) 

        #Set size of tabs
        tab_size=14

        #Summary text
        lineno=0
        for line in str(sfield).splitlines():
           if lineno <1:
              position = textbuffer.get_end_iter()
              textbuffer.insert_with_tags(position, line+'\n', b_tag) 
           else:
              colon_pos=line.find(":")
              position = textbuffer.get_end_iter()
              textbuffer.insert_with_tags(position, line[0:colon_pos], b_tag)
              position = textbuffer.get_end_iter()
              textbuffer.insert(position,line[colon_pos:] +'\n') 
           lineno=lineno+1


        #Field properties
        position = textbuffer.get_end_iter()
        textbuffer.insert_with_tags( position,'\n\nProperties\n__________\n' , b_tag) 
        for item in sfield.properties:
           position = textbuffer.get_end_iter()
           textbuffer.insert_with_tags( position, str(item)+':   ', b_tag) 
           position = textbuffer.get_end_iter()
           textbuffer.insert( position, str(sfield.properties[item])+'\n') 

        position = textbuffer.get_end_iter()
        textbuffer.insert( position, '\n\n') 


        #Field data
        position = textbuffer.get_end_iter()
        textbuffer.insert_with_tags(position, 'Data\n____\n\n', b_tag) 

        data=np.squeeze(sfield.array)
        if hasattr(sfield, 'ncvar'): fname=sfield.ncvar
        if hasattr(sfield, 'short_name'): fname=sfield.short_name 
        if hasattr(sfield, 'long_name'): fname=sfield.long_name 
        if hasattr(sfield, 'standard_name'): fname=sfield.standard_name

        #Find max string length
        #smax=len(max(xpts.astype('str'), key=len))+len(max(data.astype('str'), key=len))+4

        #1D data
        if np.ndim(data) == 1:
           for mydim in sfield.items():
              dimdata=sfield.item(mydim).array
              if np.size(dimdata) == np.size(data):
                  xpts=dimdata
                  xname=''
                  if hasattr(sfield.item(mydim), 'ncvar'): xname=sfield.item(mydim).ncvar
                  if hasattr(sfield.item(mydim), 'short_name'): xname=sfield.item(mydim).short_name 
                  if hasattr(sfield.item(mydim), 'long_name'): xname=sfield.item(mydim).long_name 
                  if hasattr(sfield.item(mydim), 'standard_name'): xname=sfield.item(mydim).standard_name


           #Write out data
           position = textbuffer.get_end_iter()
           mystr=(xname+'\t'+fname+'\n').expandtabs(tab_size)
           textbuffer.insert_with_tags(position, mystr, tb_tag) 
        
           for count in np.arange(np.size(data)):
              mystr=(str(xpts[count])+'\t'+str(data[count])+'\n')
              mystr=mystr.expandtabs(tab_size)
              position = textbuffer.get_end_iter()
              textbuffer.insert_with_tags( position, mystr, t_tag)

        #2D data
        if np.ndim(data) == 2:
           data_shape=np.squeeze(np.shape(data))
           for mydim in sfield.items():
              dimdata=sfield.item(mydim).array
              if np.size(dimdata) == data_shape[1]:
                  xpts=dimdata
                  xname=''
                  if hasattr(sfield.item(mydim), 'ncvar'): xname=sfield.item(mydim).ncvar
                  if hasattr(sfield.item(mydim), 'short_name'): xname=sfield.item(mydim).short_name 
                  if hasattr(sfield.item(mydim), 'long_name'): xname=sfield.item(mydim).long_name 
                  if hasattr(sfield.item(mydim), 'standard_name'): xname=sfield.item(mydim).standard_name
              if np.size(dimdata) == data_shape[0]:
                  ypts=dimdata
                  yname=''
                  if hasattr(sfield.item(mydim), 'ncvar'): yname=sfield.item(mydim).ncvar
                  if hasattr(sfield.item(mydim), 'short_name'): yname=sfield.item(mydim).short_name 
                  if hasattr(sfield.item(mydim), 'long_name'): yname=sfield.item(mydim).long_name 
                  if hasattr(sfield.item(mydim), 'standard_name'): yname=sfield.item(mydim).standard_name


           #Write out data
           mystr=' \t'+str(xpts[0])
           for i in np.arange(np.size(xpts)-1): mystr=mystr+'\t'+str(xpts[i+1])
           mystr=mystr.expandtabs(tab_size)
           position = textbuffer.get_end_iter()
           textbuffer.insert_with_tags(position, mystr+'\n', tb_tag) 
           for iy in np.arange(np.size(ypts)):
              mystr=(str(ypts[iy])+'\t').expandtabs(tab_size)
              textbuffer.insert_with_tags(position, mystr, tb_tag)
              mystr=''
              for ix in np.arange(np.size(xpts)):
                 if ix == 0: mystr=mystr+str(data[iy,ix])
                 if ix > 0: mystr=mystr+'\t'+str(data[iy,ix])
              mystr=mystr.expandtabs(tab_size)
              position = textbuffer.get_end_iter()
              textbuffer.insert_with_tags(position, mystr+'\n', t_tag) 



        textbuffer.insert( position, '\n\n\n') 

        hbox = gtk.HButtonBox()
        hbox.set_size_request(200,100)
        box2.pack_start(hbox, False, False, 0)
        box2.set_size_request(200,100)
        hbox.show()

        vbox = gtk.VBox()
        vbox.show()
        hbox.pack_start(vbox, False, False, 0)
        

        #button = gtk.Button("close")        
        #button.set_size_request(200,100)
        #button.connect("clicked", self.close_application)
        #box2.pack_start(button, True, True, 0)
        #button.set_flags(gtk.CAN_DEFAULT)
        #button.grab_default()
        #button.show()
        #window.show()

        box2 = gtk.VBox(False, 10)
        box2.set_border_width(10)
        box1.pack_start(box2, False, True, 0)
        box2.show()

        button = gtk.Button("close")
        button.connect("clicked", self.close_application)
        box2.pack_start(button, True, True, 0)
        button.set_flags(gtk.CAN_DEFAULT)
        button.grab_default()
        button.show()

        self.text_window=window
        self.text_window.show()


  
class plotChoices(guiFrame):
    ''' Provides a small set of cf-plot aware plot choices '''
    def __init__(self,callback,xsize=None,ysize=None):
        ''' Constructor places buttons etc in frame, users interact,
        and then press one of the two key buttons: simple plot, or normal plot.
        They can also do advanced configuration via the advanced config button.
        Caller needs to provide a callback for when one of the two plot
        buttons is called (and deal with either {} for a simple plot, or 
        a dictionary of dictionaries with cf plots etup information for the
        normal plot. 
        Optional arguements are size hints. '''
        super(plotChoices,self).__init__(
            'Configure and Generate Plots',xsize=xsize,ysize=ysize)
        self.callback=callback
        self.vbox=gtk.VBox()
        self.row1=gtk.HBox()
        self.row2=gtk.HBox()
        self.row3=gtk.HBox()
        self.row4=gtk.HBox()
        self.add(self.vbox)
        self._row1(callback)
        self._row2()
        self._row3()
        self._row4(callback)
    
    def _row1(self,callback):
        ''' Sets up the basic action buttons '''
        #sp=smallButton('Simple Plot')
        #np=smallButton('Plot (Configured)')
        np=smallButton('<b>Plot</b>')

        #np.set_use_size(16)
        #hb=smallButton('Help')
        #s1=gtk.VSeparator()
        #s2=gtk.VSeparator()
        #for b in [sp,s1,np,s2,hb]:
        #for b in [np,s2,hb]:
        #    self.row1.pack_start(b,padding=2)
        self.row1.pack_start(np,padding=80)
        self.vbox.pack_start(self.row1,expand=False,padding=2)
        self.vbox.pack_start(gtk.HSeparator(),expand=False,padding=2)
        #sp.connect('clicked',callback,{})
        np.connect('clicked',self._getConfig,None)
        #hb.connect('clicked',self._help,None)
    
    def _row2(self):
        ''' Lays out the buttons for standard plots '''
        #self.projComboShown=1
        #ptypes=['X-Y','X-Z','Y-Z','X-T','Y-T']
        ptypes=['contour', 'vector', 'contour+vector']
        nup=['1','2x1', '1x2', '2x2' , '3x2', '2x3','3x3']
        #self.proj=['cyl','moll','npolar','spolar']
        #self.typCombo=myCombo(ptypes,label='type',initial='X-Y',callback=self._showProj)
        #self.typCombo=myCombo(ptypes,label='type',initial='contour',callback=self._showProj)
        self.typCombo=myCombo(ptypes,label='type',initial='contour')
        self.nupCombo=myCombo(nup,label='n-up',initial='1')
        #self.projCombo=myCombo(self.proj,label='projection',initial='cyl')
        self.row2.pack_start(self.nupCombo,expand=True,padding=2)
        self.row2.pack_start(self.typCombo,expand=True,padding=2)
        #self.row2.pack_start(self.projCombo,expand=True,padding=2)
        self.vbox.pack_start(self.row2,expand=False,padding=2)

    def _row3(self):
        ''' Lays out the buttons for configuring contours '''
        contours=['Filled','Blockfill', 'None']
        lines=['On','Negative dashed', 'Off']
        #self.conCombo=myCombo(contours,label='contours',initial='Filled',
        #        callback=self._showCbar)
        #self.lineCombo=myCombo(lines,label='lines',initial='On',
        #        callback=self._showCbar)
        self.conCombo=myCombo(contours,label='contour fill',initial='Filled')
        self.lineCombo=myCombo(lines,label='contour lines',initial='On')


        #self.cbarCombo=myCombo(self.cbar,label='bar',initial='Off')
        for w in [self.conCombo,self.lineCombo]:
            self.row3.pack_start(w,padding=2)
        self.vbox.pack_start(self.row3,expand=False,padding=2)
        

        
    def _row4(self,callback):
        labels=['On','Off']
        self.cbar=['On','On-X','On-Y', 'Off']
        #self.cbarChoiceShown=1
        self.labCombo=myCombo(labels,label='labels',initial='On')
        self.cbarCombo=myCombo(self.cbar,label='bar',initial='On')
        ''' Lays out axes information'''
        logv=['Normal','log-x','log-y','log-xy']
        self.axeCombo=myCombo(logv,label='axes',initial='Normal')
        #ac=smallButton('Advanced Config')
        #ac.connect('clicked', AdvancedConfig)
        for w in [self.labCombo,self.cbarCombo,self.axeCombo]:
            self.row4.pack_start(w,padding=2)

        #self.row4.pack_start(self.axeCombo,padding=2,expand=False)
        #self.row4.pack_start(ac,padding=2,expand=True)
        self.vbox.pack_start(self.row4,expand=False,padding=2)
        
    #def _showCbar(self,w,value):
    #    ''' Callback used to turn off the colour bar choice as appropriate '''
    #    if value=='lines' and self.cbarChoiceShown:
    #        self.cbarCombo.destroy()
    #        self.cbarChoiceShown=0
    #    elif not self.cbarChoiceShown:
    #        self.cbarCombo=myCombo(self.cbar,initial='Off')
    #        self.row4.pack_start(self.cbarCombo,padding=2)
    #        self.cbarChoiceShown=1
    #        self.cbarCombo.show()
        
    #def _showProj(self,w,value):
    #    ''' Callback used for the typcombo to allow projections for X-Y '''
    #    if value=='contour' and not self.projComboShown:
    #        self.projCombo=myCombo(self.proj,initial='cyl')
    #        self.row2.pack_start(self.projCombo,expand=False,padding=2)
    #        self.projCombo.show()
    #    if value!='contour' and self.projComboShown:
    #        self.projCombo.destroy()
    #        self.projComboShown=0
            
    def _help(self,w,data):
        ''' Show configuration help '''
        helpstr='Making vector plots - select u field and then click plot  \n\
                                         - select v field and then click plot\n\
                     Making contour and vector plots - select contour field and click plot\n\
                                         - select u field and then click plot\n\
                                         - select v field and then click plot\n\
                 \n'
        CFview_help(helpstr, helpstr)



        #dialog.run()
        #dialog.destroy()
   

    def _advancedConfig(self,w,data):
        ''' Generate advanced plot configuration information via a dialog popup '''
        dialog=gtk.MessageDialog(None,gtk.DIALOG_DESTROY_WITH_PARENT,
                    gtk.MESSAGE_INFO,gtk.BUTTONS_OK,
                    'Sorry advanced config not yet implemented')
        dialog.run()
        dialog.destroy()


        
    def _getConfig(self,w,d):
        ''' Return all the configuration information in dictionaries
        suitable for use as arguments for cf plot via the callback.
        This is the intersection between cf-gui and cf-plot 
        (i.e. it implements most of the cf-plot API (more of it
        is implemented via the advanced config.) '''
        config={
            #'nup':int(self.nupCombo.get_value()),
            'nup':self.nupCombo.get_value(),
            'gopen':{
                'rows':
                    {'1':None, '2x1':1, '1x2':2, '2x2':2, '3x2':2, '2x3':3, '3x3':3}[self.nupCombo.get_value()],
                'columns':
                    {'1':None, '2x1':2, '1x2':1, '2x2':2, '3x2':3, '2x3':2, '3x3':3}[self.nupCombo.get_value()],
                    },
            #'mapset':{
            #    'proj':{'cyl':'cyl','moll':'moll','npolar':'npstere',
            #                'spolar':'spstere'}[self.projCombo.get_value()]
            #        },
            'con':{
                'ptype':
                    #{'X-Y':1,'X-Z':3,'Y-Z':2,'X-T':5,'Y-T':4}[self.typCombo.get_value()],
                    {'contour':1,'vector':2, 'contour+vector':3}[self.typCombo.get_value()],
                'line_labels':
                    {'On':True,'Off':False}[self.labCombo.get_value()],
                #'negative_linestyle':
                #    {'On':None,'Off':None,'On--':1}[self.linCombo.get_value()],
                'colorbar':
                    {'On':1,'Off':None,'On-X':1,'On-Y':1}[self.cbarCombo.get_value()],
                'colorbar_orientation':
                    #{'Off':None,'On':None,'On-X':'horizontal','On-Y':'vertical'}
                    {'On':'horizontal','Off':None,'On-X':'horizontal','On-Y':'vertical'}
                        [self.cbarCombo.get_value()],
                'contour_style': 
                        {'Filled':1,'Blockfill':2,'None':3}[self.conCombo.get_value()],
                'contour_line': 
                        {'On':1,'Off':2,'Negative dashed':3}[self.lineCombo.get_value()],

                #'blockfill': 
                #        {'contour_style':3}[self.conCombo.get_value()],
                #'blockfill with lines': 
                #        {'contour_style':4}[self.conCombo.get_value()],
                #'lines': 
                #        {'contour_style':5}[self.conCombo.get_value()],
                #'lines with negative dashed': 
                #        {'contour_style':6}[self.conCombo.get_value()],
                #'blockfill':
                #    {'lines':None,'filled':None,'block':1}[self.conCombo.get_value()],
                #'lines':
                #    {'lines':True,'filled':True,'block':None}[self.conCombo.get_value()],
                #'lines ':
                #    {'lines with dashed negative':True,'filled':True,'block':None,'negative_linestyle':True}[self.conCombo.get_value()],
                #'fill':
                #    {'lines':None,'filled':True,'block':None}[self.conCombo.get_value()],
                'xlog':
                    {'Normal':None,'log-x':True,'log-y':None,'log-xy':True}
                        [self.axeCombo.get_value()],
                'ylog':
                    {'Normal':None,'log-x':None,'log-y':True,'log-xy':True}
                        [self.axeCombo.get_value()],
                    }
                }
        self.callback('Configured',config)
   
    def show(self):
        super(plotChoices,self).show_all()
        
def checkConsistency(field,plotOptions):
    ''' Check consistency between the data chosen and the plot options and
    generate error messages if appropriate. Return '' if ok! '''
    fixit='\nPlease use the grid selector to choose a 1d or 2d field'
    message=''
    #if plotOptions=={}:
    #    # simple plot option, we expect a 2d field
    #    if (len(xyshape(field)) < 1 or len(xyshape(field)) > 2):
    #        message= 'cfview can only plot 1D or 2D fields'+fixit
    #        return message
    #    else: message=''
    #else:
        # The key thing we need to check is for consistency between
        # plot options and the shape, so we can work out what to do with
        # for example, and XY plot which is 6-up.
    #    multi=plotOptions['nup']<>'1'
    #    message=plotPossibleWithField(field,plotOptions['con']['ptype'],multi)
    #    if message<>'': 
    #        if not multi: message+=fixit
    return message
        
def axes_sizes(f):
    ''' Return the sizes of the X,Y,Z,T arrays in field,f , if that's possible. 
    Much of this is temporary code, needed because 0.9.8.1 of cf-python
    can't do this trivially, 0.9.8.3 can ...'''
    sizes,results={},{}
    axes=f.domain.axes()
    # After this next line, we have an array keyed by 'dim'
    for axis in axes: sizes[axis]=f.domain.axes_sizes(key=True)[axis]

    # We need to know those for the short names. 
    for axis in ['X','Y','Z','T']:
        if f.domain.axis(axis) is not None:
            try:
                results[axis]=sizes[f.domain.axis(axis)]
            except ValueError:
                results[axis]=None

    #for axis in ['X','Y','Z','T']:
    #    try:
    #        results[axis]=sizes[f.domain.axis(axis)]
    #    except ValueError:
    #        results[axis]=None

    return results
    
def xyshape(f):
    ''' Return the shape of a field as a string, e.g. XY, or XYT '''
    sizes=axes_sizes(f)
    shapeString=''
    for s in sizes:
        if sizes[s]>1: shapeString+=s
    return shapeString
    
#def ptype2string(ptype):
#    ''' Take a plot type understood by cf-plot, and convert to an XYZT string '''
#    return {1:'XY',3:'XZ',2:'YZ',5:'XT',4:'YT'}[ptype]
    
#def plotPossibleWithField(f,ptype,multi=False):
#    ''' For a given field, is a plot of ptype possible?
#            ptype is the integer understood by cf-plot.
#        One extra dimension can be allowed to be non-singular,
#        but only if multi is true.
#        Returns '' for success, otherwise a string with an error message!
#    '''
#    ss=ptype2string(ptype)
#    fs_shape=xyshape(f)
#    nd=len(fs_shape)
#    message=''
#    if nd>3:
#        message='Dimensionality (%s) too great'%nd
#    elif nd==3 and not multi:
#        message='Dimensionality (3) not allowed unless multiple plots'
#    elif nd<2:
#        message='Dimensionality (%s) too small'%nd
#    elif nd==2 and multi:
#        message='Dimensionality (2) too small for multiple plots'
#    elif nd==3 and multi:
#        for s in ss:
#            if s not in fs_shape:
#                message='Missing axis %s'%s
#    else:
#        raise ValueError('This should not occur')
#    #print multi,nd,ss,ptype,fs_shape,f.shape,message
#    #return message
#    return ''

def getSlicesAndTitles(field,plotOptions):
    ''' Get appropriate title information for each plot, and for multiple
    plots, extract the slicing information necessary to extract each
    plot from the field. '''
    grid=cfGrid(field)
    # start with common title
    title=''
    simple=False
    if plotOptions=={}:
        simple=True
    else:
        if plotOptions['nup']==1 or len(xyshape(field))==2: simple=True
    simple=True
    if simple:
        # it's easy, just find the singleton dimension values
        for dim in grid.axes:
            if len(grid.axes[dim].array)==1:
                title+=' %s:%s '%(grid.names[dim],grid.axes[dim].array[0])
        # just return the title, no subspace argument selector necessary.
        r=[(title,None),]
    #else:
    #    # find the dimension we're stepping through.
    #    myplot={1:'XY',3:'XZ',2:'YZ',5:'XT',4:'YT'}[plotOptions['con']['ptype']]
    #    shape=xyshape(field)                                 # eg XYT
    #    stepper=shape.strip(myplot)                          # eg T
    #    dim=field.domain.axis(stepper)                       # eg 'dim2'
    #    r=[]
    #    # how many, minimum of length of field or nup
    #    howmany=min(plotOptions['nup'],len(grid.axes[dim].array))
    #    #howmany=len(grid.axes[dim].array)
    #    for i in range(howmany):
    #        thisTitle=title
    #        key,value=grid.names[dim],grid.axes[dim].array[i]
    #        thisTitle+=' %s:%s '%(key,value)
    #        # need to use dim in the next command to avoid possible name ambiguity
    #        # in non-cf compliant files.
    #        r.append((thisTitle,{dim:value}))
    #    print shape,stepper,dim,key,value
    return r
            
             

class Config_contour_levels:
    ''' Set contour levels via a dialog popup '''

    def close_application(self, widget, data=None):
        self.levels_window.destroy()

    def automatic_set(self, widget):
        if self.toggle_automatic.get_active():
            self.toggle_levs_set.set_active(False)
            self.toggle_levs_manual_set.set_active(False)
            self.levels_hbox.set_sensitive(False)
            self.levels_hbox2.set_sensitive(False)
            plotvars.levs_set=False
            plotvars.levs_manual_set=False
            plotvars.levs_extend_lower=True
            plotvars.levs_extend_upper=True
            self.toggle_lower.set_active(True)
            self.toggle_upper.set_active(True)

    def levs_set(self, widget):
        if self.toggle_levs_set.get_active():
            plotvars.levs_set=True
            plotvars.levs_manual_set=False
            self.levels_hbox.set_sensitive(True);
            self.levels_hbox2.set_sensitive(False);
            self.toggle_levs_manual_set.set_active(False)
            self.toggle_automatic.set_active(False)
        else:
            plotvars.levs_set=False
            plotvars.levs_manual_set=False
            self.levels_hbox.set_sensitive(False)
            self.toggle_automatic.set_active(True)


    def levs_manual_set(self, widget):
        if self.toggle_levs_manual_set.get_active():
            plotvars.levs_manual_set=True
            plotvars.levs=False
            self.levels_hbox.set_sensitive(False)
            self.levels_hbox2.set_sensitive(True)
            self.toggle_levs_set.set_active(False)
            self.toggle_automatic.set_active(False)
        else:
            plotvars.levs_set=False
            plotvars.levs_manual_set=False
            self.levels_hbox2.set_sensitive(False);
            self.toggle_automatic.set_active(True)


    def lower_set(self, widget):
        if self.toggle_lower.get_active():
            plotvars.levs_extend_lower=True
        else:
            plotvars.levs_extend_lower=False


    def upper_set(self, widget):
        if self.toggle_upper.get_active():
            plotvars.levs_extend_upper=True
        else:
            plotvars.levs_extend_upper=False


    def set_levs_min(self, widget):
        plotvars.levs_min=self.levs_min.get_text()

    def set_levs_max(self, widget):
        plotvars.levs_max=self.levs_max.get_text()

    def set_levs_step(self, widget):
        plotvars.levs_step=self.levs_step.get_text()

    def set_levs_manual(self, widget):
        plotvars.levs_manual=self.levs_manual.get_text()


    def levs_help(self, widget):
        helpstr='<T>Contour level options\n\
        Contour levels are initially set automatically based on the range of the field and split into \
        reasonable contour levels. If a region of the field is chosen for contouring then the range of \
        the full field is still used.\
        \n\
        <H>Setting contour levels\r\
        When making plots that compare data click on the evenly or unevenly spaced levels tick boxes\
        and set the as appropriate.  Uneven levels must be separated by spaces and ascend in value.\
        \n\
        <H>Colorbar extensions\r\
        Colorbar extensions are a way of extendind the contours to cover all the data at the ends of the\
        contour range. Click on the colorbar extension tick boxes as appropriate if these are bot required.\
        \n'
        CFview_help(helpstr, helpstr)

    def levs_reset(self, widget):

        self.toggle_levs_set.set_active(False)
        self.toggle_levs_manual_set.set_active(False)
        self.levels_hbox.set_sensitive(False)
        self.levels_hbox2.set_sensitive(False)
        plotvars.levs_set=False
        plotvars.levs_manual_set=False
        plotvars.levs_extend_lower=True
        plotvars.levs_extend_upper=True
        self.toggle_lower.set_active(True)
        self.toggle_upper.set_active(True)
        plotvars.levs_min=''
        plotvars.levs_max=''
        plotvars.levs_step=''
        self.levs_min.set_text('')
        self.levs_max.set_text('')
        self.levs_step.set_text('')
        plotvars.levs_manual=''
        self.levs_manual.set_text('')




    def __init__(self, widget):
        # create a new window
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_usize(500, 400)
        window.set_title("Contour levels")


        vbox = gtk.VBox(gtk.FALSE, 0)
        window.add(vbox)
        vbox.show()



        ######################################
        #Master check box for automatic levels
        ######################################

        toggle_automatic = gtk.CheckButton("Automatic levels")
        vbox.add(toggle_automatic)
        toggle_automatic.show()
        toggle_automatic.set_active(True)

        separator1 = gtk.HSeparator()
        vbox.add(separator1)
        separator1.show()




        #####################
        #Evenly spaced levels
        #####################
        levels_manual_box=gtk.VBox()
        levels_manual_box.show()

        #Tick box for min, max, step input
        toggle_levs_set = gtk.CheckButton("Evenly spaced levels")
        toggle_levs_set.set_active(False)
        vbox.add(toggle_levs_set)
        toggle_levs_set.show()

        # levels_hbox - horizontal box for evenly spaced levels
        levels_hbox=gtk.HBox()
        levels_hbox.show()
        vbox.add(levels_hbox)
        separator2 = gtk.HSeparator()
        vbox.add(separator2)
        separator2.show()
                            
        #Levels - set up labels and inputs
        levs_label_min=gtk.Label("Min:")
        levs_min = gtk.Entry()
        levs_min.set_width_chars(5)
        levs_min.set_text(plotvars.levs_min)
        levs_label_max=gtk.Label("Max:")
        levs_max = gtk.Entry()
        levs_max.set_width_chars(5)
        levs_max.set_text(plotvars.levs_max)
        levs_label_step=gtk.Label("Step:")
        levs_step = gtk.Entry()
        levs_step.set_width_chars(5)
        levs_step.set_text(plotvars.levs_step)


        #Pack the widgets
        levels_hbox.pack_start(levs_label_min, padding=5, expand=False)
        levels_hbox.pack_start(levs_min, padding=5, expand=False)
        levels_hbox.pack_start(levs_label_max, padding=20, expand=False)
        levels_hbox.pack_start(levs_max, padding=0, expand=False)
        levels_hbox.pack_start(levs_label_step, padding=20, expand=False)
        levels_hbox.pack_start(levs_step, padding=0, expand=False)
        levels_hbox.set_sensitive(False)

        #Show the widgets
        levs_label_min.show()
        levs_min.show()
        levs_label_max.show()
        levs_max.show()
        levs_label_step.show()
        levs_step.show()


        #######################
        #Unevenly spaced levels
        #######################
        #Tick box for manual input
        vbox.add(levels_manual_box)
        toggle_levs_manual_set = gtk.CheckButton("Unevenly spaced levels")
        toggle_levs_manual_set.set_active(False)


        # hbox2 - box for unevenly spaced levels
        levels_hbox2=gtk.HBox()
        levels_hbox2.show()
        vbox.add(levels_hbox2)
        levs_label_manual=gtk.Label("Ascending values separated by spaces")
        levs_manual = gtk.Entry()
        levs_manual.set_width_chars(25)
        levs_manual.set_text(plotvars.levs_manual)

        separator3 = gtk.HSeparator()
        vbox.add(separator3)
        separator3.show()

        levels_manual_box.pack_start(toggle_levs_manual_set, True, True, 0)
        toggle_levs_manual_set.set_active(False)
        levels_hbox2.pack_start(levs_label_manual, padding=5, expand=False)
        levels_hbox2.pack_start(levs_manual, padding=5, expand=False)
        levels_hbox2.set_sensitive(False)


        #Show the widgets
        levs_label_min.show()
        levs_min.show()
        levs_label_max.show()
        levs_max.show()
        levs_label_step.show()
        levs_step.show()

        toggle_levs_manual_set.show()
        levs_label_manual.show()
        levs_manual.show()
        toggle_levs_manual_set.show()


        ###########################
        #Colorbar extension toggles
        ###########################
        hbox_extension_label=gtk.HBox()
        hbox_extension_label.show()
        vbox.add(hbox_extension_label)
        extension_text=gtk.Label("Colorbar extensions - extend contours to cover all the data")
        extension_text.show()
        hbox_extension_label.pack_start(extension_text, padding=5, expand=False)

        hbox3=gtk.HBox()
        hbox3.show()
        vbox.add(hbox3)
        separator4 = gtk.HSeparator()
        vbox.add(separator4)
        separator4.show()

        toggle_lower = gtk.CheckButton("Lower")
        toggle_lower.show()
        toggle_lower.set_active(True)

        toggle_upper = gtk.CheckButton("Upper")
        toggle_upper.show()
        toggle_upper.set_active(True)

        hbox3.pack_start(toggle_lower)
        hbox3.pack_start(toggle_upper)


        #######################################
        #Box to contain reset and close buttons
        #######################################
        box2 = gtk.HBox(False, 10)
        box2.set_border_width(10)
        box2.show()
        vbox.pack_end(box2, False, True, 0)

        button_reset = gtk.Button("reset")
        button_reset.connect("clicked", self.levs_reset)
        box2.pack_start(button_reset, True, True, 0)
        button_reset.set_flags(gtk.CAN_DEFAULT)
        button_reset.grab_default()
        button_reset.show()

        button_help = gtk.Button("help")
        button_help.connect("clicked", self.levs_help)
        box2.pack_start(button_help, True, True, 0)
        button_help.set_flags(gtk.CAN_DEFAULT)
        button_help.grab_default()
        button_help.show()

        button_close = gtk.Button("close")
        button_close.connect("clicked", self.close_application)
        box2.pack_start(button_close, True, True, 0)
        button_close.set_flags(gtk.CAN_DEFAULT)
        button_close.grab_default()
        button_close.show()



        ####################
        #Connect the widgets
        ####################
        toggle_automatic.connect("toggled", self.automatic_set)
        toggle_levs_set.connect("toggled", self.levs_set)
        toggle_levs_manual_set.connect("toggled", self.levs_manual_set)
        toggle_lower.connect("toggled", self.lower_set)
        toggle_upper.connect("toggled", self.upper_set)

        levs_min.connect("changed", self.set_levs_min)
        levs_max.connect("changed", self.set_levs_max)
        levs_step.connect("changed", self.set_levs_step)
        levs_manual.connect("changed", self.set_levs_manual)


        ###############################
        #Populate self with the widgets
        ###############################
        self.toggle_automatic=toggle_automatic
        self.toggle_levs_set=toggle_levs_set
        self.toggle_levs_manual_set=toggle_levs_manual_set
        self.toggle_lower=toggle_lower
        self.toggle_upper=toggle_upper

        self.levs_min=levs_min
        self.levs_max=levs_max
        self.levs_step=levs_step
        self.levs_manual=levs_manual
        self.levels_hbox=levels_hbox
        self.levels_hbox2=levels_hbox2
        self.levels_window=window
        self.levels_window.show()


###########################
#Map settings configuration
###########################    
class Config_map_settings:
    ''' Configure amp settings via a dialog popup '''

    def close_application(self, widget, data=None):
        self.map_window.destroy()


    def cylin(self, widget):
        if self.toggle_cylin.get_active():
            plotvars.proj='cyl'
            self.cylin_hbox1.set_sensitive(True)
            self.cylin_hbox2.set_sensitive(True)
            self.toggle_north_pole.set_active(False)
            self.toggle_south_pole.set_active(False)
            self.polar_hbox1.set_sensitive(False)
            self.polar_hbox2.set_sensitive(False)
            self.toggle_mollweide.set_active(False)


    def set_lonmin(self, widget):
        plotvars.lonmin=self.lonmin.get_text()

    def set_lonmax(self, widget):
        plotvars.lonmax=self.lonmax.get_text()

    def set_latmin(self, widget):
        plotvars.latmin=self.latmin.get_text()

    def set_latmax(self, widget):
        plotvars.latmax=self.latmax.get_text()




    def north_pole(self, widget):
        if self.toggle_north_pole.get_active():
            plotvars.proj='npstere'
            self.toggle_south_pole.set_active(False)
            self.toggle_cylin.set_active(False)
            self.cylin_hbox1.set_sensitive(False)
            self.cylin_hbox2.set_sensitive(False)
            self.polar_hbox1.set_sensitive(True)
            self.polar_hbox2.set_sensitive(True)
            self.toggle_mollweide.set_active(False)

    def south_pole(self, widget):
        if self.toggle_south_pole.get_active():
            plotvars.proj='spstere'
            self.toggle_north_pole.set_active(False)
            self.toggle_cylin.set_active(False)
            self.cylin_hbox1.set_sensitive(False)
            self.cylin_hbox2.set_sensitive(False)
            self.polar_hbox1.set_sensitive(True)
            self.polar_hbox2.set_sensitive(True)
            self.toggle_mollweide.set_active(False)


    def set_boundinglat(self, widget):
        plotvars.boundinglat=self.boundinglat.get_text()

    def set_lon_0(self, widget):
        plotvars.lon_0=self.lon_0.get_text()


    def mollweide(self, widget):
        if self.toggle_mollweide.get_active():
            plotvars.proj='moll'
            self.toggle_north_pole.set_active(False)
            self.toggle_south_pole.set_active(False)
            self.toggle_cylin.set_active(False)
            self.cylin_hbox1.set_sensitive(False)
            self.cylin_hbox2.set_sensitive(False)
            self.polar_hbox1.set_sensitive(False)
            self.polar_hbox2.set_sensitive(False)


    def crude_set(self, widget):
        if self.toggle_crude.get_active():
            plotvars.resolution='c'
            self.toggle_low.set_active(False)
            self.toggle_intermediate.set_active(False)
            self.toggle_full.set_active(False)
            self.toggle_high.set_active(False)
            self.toggle_none.set_active(False)


    def low_set(self, widget):
        if self.toggle_low.get_active():
            plotvars.resolution='l'
            self.toggle_crude.set_active(False)
            self.toggle_intermediate.set_active(False)
            self.toggle_full.set_active(False)
            self.toggle_high.set_active(False)
            self.toggle_none.set_active(False)

    def intermediate_set(self, widget):
        if self.toggle_intermediate.get_active():
            plotvars.resolution='i'
            self.toggle_crude.set_active(False)
            self.toggle_low.set_active(False)
            self.toggle_full.set_active(False)
            self.toggle_high.set_active(False)
            self.toggle_none.set_active(False)

    def high_set(self, widget):
        if self.toggle_high.get_active():
            plotvars.resolution='h'
            self.toggle_crude.set_active(False)
            self.toggle_low.set_active(False)
            self.toggle_intermediate.set_active(False)
            self.toggle_full.set_active(False)
            self.toggle_none.set_active(False)

    def full_set(self, widget):
        if self.toggle_full.get_active():
            plotvars.resolution='f'
            self.toggle_crude.set_active(False)
            self.toggle_low.set_active(False)
            self.toggle_intermediate.set_active(False)
            self.toggle_high.set_active(False)
            self.toggle_none.set_active(False)

    def none_set(self, widget):
        if self.toggle_none.get_active():
            plotvars.resolution='n'
            self.toggle_crude.set_active(False)
            self.toggle_low.set_active(False)
            self.toggle_intermediate.set_active(False)
            self.toggle_high.set_active(False)
            self.toggle_full.set_active(False)

    def set_thickness(self, widget):
        plotvars.continent_thickness=self.continent_thickness.get_text()


    def set_color(self, widget):
        plotvars.continent_color=self.continent_color.get_text()


    def map_help(self, widget):
        helpstr='<T>Map options\n\
        <H>Cylindrical projection\r\
        The default map projection is the cylindrical equidistant projection with the limits \
        of -180 to 180 degrees in longitude and -90 to 90 degreesin latitude.  Change these as \
        appropriate to focus the plot onto the area of interest.\n\
        <H>Polar stereographic plots\r\
        Polar plots are focussed on either the north or south pole.  The bounding latitude is \
        the edge of the viewable latitudes and is set to the equator by default.  The centre of \
        the map domain is where the map is centred.  By default this is 0 degrees which is the \
        Greenwich meridian in the case of the north pole.  For the South Pole plot thie is usually\
        changed ot 180 degrees.\n\
        <H>Map resolution\r\
        The map resolution is set to a default of Crude.  Higher resolutions take more time to plot\
        with Full taking several minutes to plot.\n\
        <H>Continent color and thickness\r\
        These default to 1.5 and k is black.  Change these as appropriate. Matplotlib named colors\
        can be seen using the following Python code\n\
        import matplotlib\r\
        for name in matplotlib.colors.cnames.iteritems(): print(name)\n\
        \n'
        CFview_help(helpstr, helpstr)

    def map_reset(self, widget):
        self.toggle_cylin.set_active(True)
        self.cylin_hbox1.set_sensitive(True)
        self.cylin_hbox2.set_sensitive(True)
        plotvars.proj='cyl'
        plotvars.lonmin='-180'
        plotvars.lonmax='180'
        plotvars.latmin='-90'
        plotvars.latmax='90'
        self.toggle_north_pole.set_active(False)
        self.toggle_south_pole.set_active(False)
        self.toggle_mollweide.set_active(False)
        self.polar_hbox1.set_sensitive(False)
        self.polar_hbox2.set_sensitive(False)
        self.lonmin.set_text('-180')
        self.lonmax.set_text('180')
        self.latmin.set_text('-90')
        self.latmax.set_text('90')
        plotvars.boundinglat='0'
        plotvars.lon_0='0'
        self.boundinglat.set_text('0')
        self.lon_0.set_text('0')

        self.toggle_crude.set_active(True)
        self.toggle_low.set_active(False)
        self.toggle_intermediate.set_active(False)
        self.toggle_high.set_active(False)
        self.toggle_full.set_active(False)
        self.toggle_none.set_active(False)
        plotvars.continent_color='black'
        self.continent_color.set_text('black')
        plotvars.continent_thickness='1.5'
        self.continent_thickness.set_text('1.5')



    def __init__(self, widget):
        # create a new window
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_usize(500, 500)
        window.set_title("Map options")


        vbox = gtk.VBox(gtk.FALSE, 0)
        window.add(vbox)
        vbox.show()




        #######################
        #Cylindrical projection
        #######################
        #Tick box for min, max, step input
        toggle_cylin = gtk.CheckButton("Cylindrical projection")
        toggle_cylin.set_active(True)
        vbox.add(toggle_cylin)
        toggle_cylin.show()

        # cylin_hbox1 and cylin_hbox2 - horizontal boxes for plot limits
        cylin_hbox1=gtk.HBox()
        cylin_hbox1.show()
        vbox.add(cylin_hbox1)
        cylin_hbox2=gtk.HBox()
        cylin_hbox2.show()
        vbox.add(cylin_hbox2)
        separator1 = gtk.HSeparator()
        vbox.add(separator1)
        separator1.show()
                            
        #Levels - set up labels and inputs
        lonmin_label=gtk.Label("Lonmin:")
        lonmin = gtk.Entry()
        lonmin.set_width_chars(4)
        lonmin.set_text(plotvars.lonmin)
        lonmax_label=gtk.Label("Lonmax:")
        lonmax = gtk.Entry()
        lonmax.set_width_chars(4)
        lonmax.set_text(plotvars.lonmax)
        latmin_label=gtk.Label("Latmin:")
        latmin = gtk.Entry()
        latmin.set_width_chars(4)
        latmin.set_text(plotvars.latmin)
        latmax_label=gtk.Label("Latmax:")
        latmax = gtk.Entry()
        latmax.set_width_chars(4)
        latmax.set_text(plotvars.latmax)


        #Pack the widgets
        cylin_hbox1.pack_start(lonmin_label, padding=5, expand=False)
        cylin_hbox1.pack_start(lonmin, padding=45,expand=False)
        cylin_hbox1.pack_start(lonmax_label, padding=5, expand=False)
        cylin_hbox1.pack_start(lonmax, padding=51,expand=False)
        cylin_hbox1.set_sensitive(True)
        cylin_hbox2.pack_start(latmin_label, padding=5, expand=False)
        cylin_hbox2.pack_start(latmin, padding=50,expand=False)
        cylin_hbox2.pack_start(latmax_label, padding=5, expand=False)
        cylin_hbox2.pack_start(latmax, padding=50,expand=False)
        cylin_hbox2.set_sensitive(True)



        #Show the widgets
        lonmin_label.show()
        lonmin.show()
        lonmax_label.show()
        lonmax.show()
        latmin_label.show()
        latmin.show()
        latmax_label.show()
        latmax.show()




        ###############################
        #Polar stereographic projection
        ###############################
        polar_label_hbox=gtk.HBox()
        polar_label_hbox.show()
        vbox.add(polar_label_hbox)
        polar_label=gtk.Label("Polar stereographic projection")
        polar_label.show()
        polar_label_hbox.pack_start(polar_label, padding=5, expand=False)


        #Tick box for north and south poles
        toggle_north_pole = gtk.CheckButton("North Pole")
        toggle_north_pole.set_active(False)
        vbox.add(toggle_north_pole)
        toggle_north_pole.show()

        toggle_south_pole = gtk.CheckButton("South Pole")
        toggle_south_pole.set_active(False)
        vbox.add(toggle_south_pole)
        toggle_south_pole.show()

                  
        #polar_hbox1 and polar_hbox2 - horizontal boxes for plot limits
        polar_hbox1=gtk.HBox()
        polar_hbox1.show()
        vbox.add(polar_hbox1)
        polar_hbox2=gtk.HBox()
        polar_hbox2.show()
        vbox.add(polar_hbox2)
        polar_hbox1.set_sensitive(False)
        polar_hbox2.set_sensitive(False)

        #Bounding latitude and centre of plot - set up labels and inputs        
        boundinglat_label=gtk.Label("Bounding latitude - edge of the viewable latitudes:")
        boundinglat = gtk.Entry()
        boundinglat.set_width_chars(4)
        boundinglat.set_text(plotvars.boundinglat)
        polar_hbox1.pack_start(boundinglat_label, padding=5, expand=False)
        polar_hbox1.pack_start(boundinglat, padding=50,expand=False)
        lon_0_label=gtk.Label("Centre of map domain:")
        lon_0 = gtk.Entry()
        lon_0.set_width_chars(4)
        lon_0.set_text(plotvars.lon_0)
        polar_hbox2.pack_start(lon_0_label, padding=5, expand=False)
        polar_hbox2.pack_start(lon_0, padding=220,expand=False)

        separator2 = gtk.HSeparator()
        vbox.add(separator2)
        separator2.show()

        #Show the widgets
        boundinglat_label.show()
        boundinglat.show()
        lon_0_label.show()
        lon_0.show()


        #####################
        #Mollweide projection
        #####################
        #Tick box 
        toggle_mollweide = gtk.CheckButton("Mollweide projection")
        toggle_mollweide.set_active(False)
        vbox.add(toggle_mollweide)
        toggle_mollweide.show()

        separator3 = gtk.HSeparator()
        vbox.add(separator3)
        separator3.show()


        ###############
        #Map resolution
        ############### 
        resolution_label_hbox=gtk.HBox()
        resolution_label_hbox.show()
        vbox.add(resolution_label_hbox)
        resolution_label=gtk.Label("Map resolution")
        resolution_label.show()
        resolution_label_hbox.pack_start(resolution_label, padding=5, expand=False)

        #resolution_hbox1 and resolution_hbox2 - horizontal boxes for resolution setting
        resolution_hbox1=gtk.HBox()
        resolution_hbox1.show()
        vbox.add(resolution_hbox1)
        resolution_hbox2=gtk.HBox()
        resolution_hbox2.show()
        vbox.add(resolution_hbox2)

        toggle_crude = gtk.CheckButton("Crude")
        toggle_crude.set_active(True)
        toggle_crude.show()
        resolution_hbox1.pack_start(toggle_crude, padding=1, expand=False)

        toggle_low = gtk.CheckButton("Low")
        toggle_low.set_active(False)
        toggle_low.show()
        resolution_hbox1.pack_start(toggle_low, padding=20, expand=False)

        toggle_intermediate = gtk.CheckButton("Intermediate")
        toggle_intermediate.set_active(False)
        toggle_intermediate.show()
        resolution_hbox1.pack_start(toggle_intermediate, padding=20, expand=False)

        toggle_high = gtk.CheckButton("High")
        toggle_high.set_active(False)
        toggle_high.show()
        resolution_hbox2.pack_start(toggle_high, padding=1, expand=False)

        toggle_full = gtk.CheckButton("Full")
        toggle_full.set_active(False)
        toggle_full.show()
        resolution_hbox2.pack_start(toggle_full, padding=30, expand=False)


        toggle_none = gtk.CheckButton("None")
        toggle_none.set_active(False)
        toggle_none.show()
        resolution_hbox2.pack_start(toggle_none, padding=14, expand=False)

        separator4 = gtk.HSeparator()
        vbox.add(separator4)
        separator4.show()


        ###############################
        #Continent colour and thickness
        ###############################
        #continent_hbox1 and continent_hbox2 - horizontal boxes for continent settings
        continent_hbox1=gtk.HBox()
        continent_hbox1.show()
        vbox.add(continent_hbox1)
        continent_hbox2=gtk.HBox()
        continent_hbox2.show()
        vbox.add(continent_hbox2)

        #Entry label and text
        continent_thickness_label=gtk.Label("Continent thickness:")
        continent_thickness = gtk.Entry()
        continent_thickness.set_width_chars(5)
        continent_thickness.set_text(plotvars.continent_thickness)
        continent_hbox1.pack_start(continent_thickness_label, padding=5, expand=False)
        continent_hbox1.pack_start(continent_thickness, padding=80, expand=False)

        continent_color_label=gtk.Label("Continent color:")
        continent_color = gtk.Entry()
        continent_color.set_width_chars(7)
        continent_color.set_text(plotvars.continent_color)
        continent_hbox2.pack_start(continent_color_label, padding=5, expand=False)
        continent_hbox2.pack_start(continent_color, padding=107, expand=False)


        #Show the widgets
        continent_thickness_label.show()
        continent_thickness.show()
        continent_color_label.show()
        continent_color.show()




        #######################################
        #Box to contain reset and close buttons
        #######################################
        box2 = gtk.HBox(False, 10)
        box2.set_border_width(10)
        box2.show()
        vbox.pack_end(box2, False, True, 0)

        button_reset = gtk.Button("reset")
        button_reset.connect("clicked", self.map_reset)
        box2.pack_start(button_reset, True, True, 0)
        button_reset.set_flags(gtk.CAN_DEFAULT)
        button_reset.grab_default()
        button_reset.show()

        button_help = gtk.Button("help")
        button_help.connect("clicked", self.map_help)
        box2.pack_start(button_help, True, True, 0)
        button_help.set_flags(gtk.CAN_DEFAULT)
        button_help.grab_default()
        button_help.show()

        button_close = gtk.Button("close")
        button_close.connect("clicked", self.close_application)
        box2.pack_start(button_close, True, True, 0)
        button_close.set_flags(gtk.CAN_DEFAULT)
        button_close.grab_default()
        button_close.show()



        ####################
        #Connect the widgets
        ####################
        toggle_cylin.connect("toggled", self.cylin)
        lonmin.connect("changed", self.set_lonmin)
        lonmax.connect("changed", self.set_lonmax)
        latmin.connect("changed", self.set_latmin)
        latmax.connect("changed", self.set_latmax)
        boundinglat.connect("changed", self.set_boundinglat)
        lon_0.connect("changed", self.set_lon_0)

        toggle_north_pole.connect("toggled", self.north_pole)
        toggle_south_pole.connect("toggled", self.south_pole)
        toggle_mollweide.connect("toggled", self.mollweide)

        toggle_crude.connect("toggled", self.crude_set)
        toggle_low.connect("toggled", self.low_set)
        toggle_intermediate.connect("toggled", self.intermediate_set)
        toggle_full.connect("toggled", self.full_set)
        toggle_high.connect("toggled", self.high_set)
        toggle_none.connect("toggled", self.none_set)

        continent_thickness.connect("changed", self.set_thickness)
        continent_color.connect("changed", self.set_color)


        ###############################
        #Populate self with the widgets
        ###############################
        self.toggle_cylin=toggle_cylin
        self.toggle_north_pole=toggle_north_pole
        self.toggle_south_pole=toggle_south_pole
        self.toggle_mollweide=toggle_mollweide
        self.toggle_crude=toggle_crude
        self.toggle_low=toggle_low
        self.toggle_intermediate=toggle_intermediate
        self.toggle_full=toggle_full
        self.toggle_high=toggle_high
        self.toggle_none=toggle_none

        self.lonmin=lonmin
        self.lonmax=lonmax
        self.latmin=latmin
        self.latmax=latmax
        self.boundinglat=boundinglat
        self.lon_0=lon_0
        self.cylin_hbox1=cylin_hbox1
        self.cylin_hbox2=cylin_hbox2
        self.polar_hbox1=polar_hbox1
        self.polar_hbox2=polar_hbox2

        self.continent_thickness=continent_thickness
        self.continent_color=continent_color

        self.map_window=window
        self.map_window.show()


class Config_vectors:
    ''' Configure vectors via a dialog popup '''

    def close_application(self, widget, data=None):
        self.map_window.destroy()

    def auto(self, widget):
        if self.toggle_vect_auto.get_active():
            plotvars.vect_auto_set=True
            self.vect_hbox1.set_sensitive(False)
            self.vect_hbox2.set_sensitive(False)
        else:
            plotvars.vect_auto_set=False
            self.vect_hbox1.set_sensitive(True)
            self.vect_hbox2.set_sensitive(True)

    def toggle_stride(self, widget):
        if self.toggle_vect_stride.get_active():
            self.toggle_vect_pts.set_active(False)
            plotvars.vect_stride_set=True
            plotvars.vect_stride_set=True
            plotvars.vect_pts_set=False
            self.vect_pts.set_sensitive(False)
            self.vect_stride.set_sensitive(True)
        else:
            plotvars.vect_auto_set=True
            plotvars.vect_stride_set=False
            plotvars.vect_pts_set=False
            self.vect_stride.set_sensitive(False)


    def toggle_pts(self, widget):
        if self.toggle_vect_pts.get_active():
            self.toggle_vect_stride.set_active(False)
            self.vect_pts.set_sensitive(True)
            plotvars.vect_stride_set=False
            plotvars.vect_pts_set=True
            self.vect_stride.set_sensitive(False)
        else:
            plotvars.vect_auto_set=True
            plotvars.vect_stride_set=False
            plotvars.vect_pts_set=False
            self.vect_pts.set_sensitive(False)



    def set_vect_length(self, widget):
        plotvars.vect_length=self.vect_length.get_text()

    def set_vect_scale(self, widget):
        plotvars.vect_scale=self.vect_scale.get_text()

    def set_vect_stride(self, widget):
        plotvars.vect_stride=self.vect_stride.get_text()
        self.vect_pts.set_sensitive(False)

    def set_vect_pts(self, widget):
        plotvars.vect_pts=self.vect_pts.get_text()
        self.vect_stride.set_sensitive(False)

    def vect_reset(self, widget):
        plotvars.vect_length='10'
        plotvars.scale='100'
        plotvars.vect_stride='2'
        plotvars.scale='30'
        plotvars.vect_auto_set=True
        plotvars.vect_stride_set=False
        plotvars.vect_pts_set=False
        self.toggle_vect_auto.set_active(True)
        self.vect_hbox1.set_sensitive(False)
        self.vect_hbox2.set_sensitive(False)
        self.vect_length.set_text('10')
        self.vect_scale.set_text('100')
        self.vect_stride.set_text('1')
        self.vect_pts.set_text('30')
        self.toggle_vect_stride.set_active(False)
        self.toggle_vect_pts.set_active(False)
        self.vect_stride.set_sensitive(False)
        self.vect_pts.set_sensitive(False)


    def vect_help(self, widget):
        helpstr='<T>Vector options\
        \n\
        Vectors will be automatically drawn based on the reference vector being half of the maximum vector length.\
        \n\
        <H>Length and scale\r\
        When comparing vector plots a common vector length is needed.  Click on the Automatic vectors tick box to \
        reveal the vector length and scale input boxes. Smaller values of scale give a longer vector length.\
        \n\
        If different vector lengths and scales are required in the X and Y directions then enter two values in the \
        length and scale inputs separated by a space.\
        \n\
        <H>Vector spacing\r\
        Vectors can be spaced either by:\r\
        striding plotted vectors with a stride of 2 plotting every other vector\r\
        or:\r\
        Interpolating the data to a number of vectors in x and y.  An input of 30 in this case will give 30 vectors \
        in x and y.\
        \n\
        The vector spacing inputs will take two space separated values to give different strides or interpolations in x and y.\n\
        \n'
        CFview_help(helpstr, helpstr)



    def __init__(self, widget):
        # create a new window
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_usize(500, 500)
        window.set_title("Vector options")

        vbox = gtk.VBox(gtk.FALSE, 0)
        window.add(vbox)
        vbox.show()

        #Tick auto vectors
        toggle_vect_auto = gtk.CheckButton("Automatic vectors")
        toggle_vect_auto.set_active(True)
        vbox.add(toggle_vect_auto)
        toggle_vect_auto.show()
        separator1 = gtk.HSeparator()
        vbox.add(separator1)
        separator1.show()

        ########################
        #Length, scale and label
        ######################## 
        vector_label_hbox1=gtk.HBox()
        vector_label_hbox1.show()
        vbox.add(vector_label_hbox1)
        vector_label1=gtk.Label("Vector length, scale and label")
        vector_label1.show()
        vector_label_hbox1.pack_start(vector_label1, padding=5, expand=False)


        # vect_hbox1 and vect_hbox2 - horizontal boxes for main vector attibutes
        vect_hbox1=gtk.HBox()
        vect_hbox1.show()
        vbox.add(vect_hbox1)
        vect_hbox2=gtk.HBox()
        vect_hbox2.show()
        vbox.add(vect_hbox2)

                            
        #Vector length and scale - set up labels and inputs
        vect_length_label=gtk.Label("Reference vector length:")
        vect_length = gtk.Entry()
        vect_length.set_width_chars(4)
        vect_length.set_text(plotvars.vect_length)
        vect_scale_label=gtk.Label("Scale (smaller is longer):")
        vect_scale = gtk.Entry()
        vect_scale.set_width_chars(4)
        vect_scale.set_text(plotvars.vect_scale)


        #Pack the widgets
        vect_hbox1.pack_start(vect_length_label, padding=5, expand=False)
        vect_hbox1.pack_start(vect_length, padding=5, expand=False)
        vect_hbox2.pack_start(vect_scale_label, padding=5, expand=False)
        vect_hbox2.pack_start(vect_scale, padding=5, expand=False)
        vect_hbox1.set_sensitive(False)
        vect_hbox2.set_sensitive(False)


        #Show the widgets
        vect_length_label.show()
        vect_length.show()
        vect_scale_label.show()
        vect_scale.show()


        #Horizontal separator
        separator2 = gtk.HSeparator()
        vbox.add(separator2)
        separator2.show()



        #########################
        #Stride and interpolation
        #########################
        vector_label_hbox2=gtk.HBox()
        vector_label_hbox2.show()
        vbox.add(vector_label_hbox2)
        vector_label2=gtk.Label("Vector spacing")
        vector_label2.show()
        vector_label_hbox2.pack_start(vector_label2, padding=5, expand=False)

        # vect_hbox3 and vect_hbox4 - horizontal boxes for main vector attibutes
        vect_hbox3=gtk.HBox()
        vect_hbox3.show()
        vbox.add(vect_hbox3)
        vect_hbox4=gtk.HBox()
        vect_hbox4.show()
        vbox.add(vect_hbox4)

                            
        #Vector stride and interpolation - set up labels and inputs
        toggle_vect_stride = gtk.CheckButton("Stride between vectors")
        toggle_vect_stride.set_active(False)
        vect_stride = gtk.Entry()
        vect_stride.set_width_chars(3)
        vect_stride.set_text(plotvars.vect_stride)
        vect_stride.set_sensitive(False)
        toggle_vect_pts = gtk.CheckButton("Interpolation - number of vectors in x and y")
        toggle_vect_pts.set_active(False)
        vect_pts = gtk.Entry()
        vect_pts.set_width_chars(3)
        vect_pts.set_text(plotvars.vect_pts)
        vect_pts.set_sensitive(False)


        #Pack the widgets
        vect_hbox3.pack_start(toggle_vect_stride, padding=5, expand=False)
        vect_hbox3.pack_start(vect_stride, padding=5, expand=False)
        vect_hbox4.pack_start(toggle_vect_pts, padding=5, expand=False)
        vect_hbox4.pack_start(vect_pts, padding=5, expand=False)


        #Show the widgets
        toggle_vect_stride.show()
        vect_stride.show()
        toggle_vect_pts.show()
        vect_pts.show()


        #Horizontal separator
        separator3 = gtk.HSeparator()
        vbox.add(separator3)
        separator3.show()



                            

        #######################################
        #Box to contain reset and close buttons
        #######################################
        box2 = gtk.HBox(False, 10)
        box2.set_border_width(10)
        box2.show()
        vbox.pack_end(box2, False, True, 0)

        button_reset = gtk.Button("reset")
        button_reset.connect("clicked", self.vect_reset)
        box2.pack_start(button_reset, True, True, 0)
        button_reset.set_flags(gtk.CAN_DEFAULT)
        button_reset.grab_default()
        button_reset.show()

        button_help = gtk.Button("help")
        button_help.connect("clicked", self.vect_help)
        box2.pack_start(button_help, True, True, 0)
        button_help.set_flags(gtk.CAN_DEFAULT)
        button_help.grab_default()
        button_help.show()

        button_close = gtk.Button("close")
        button_close.connect("clicked", self.close_application)
        box2.pack_start(button_close, True, True, 0)
        button_close.set_flags(gtk.CAN_DEFAULT)
        button_close.grab_default()
        button_close.show()


        ####################
        #Connect the widgets
        ####################
        toggle_vect_auto.connect("toggled", self.auto)
        toggle_vect_stride.connect("toggled", self.toggle_stride)
        toggle_vect_pts.connect("toggled", self.toggle_pts)
        vect_length.connect("changed", self.set_vect_length)
        vect_scale.connect("changed", self.set_vect_scale)
        vect_stride.connect("changed", self.set_vect_stride)
        vect_pts.connect("changed", self.set_vect_pts)


        ###############################
        #Populate self with the widgets
        ###############################
        self.toggle_vect_auto=toggle_vect_auto
        self.toggle_vect_stride=toggle_vect_stride
        self.toggle_vect_pts=toggle_vect_pts
        self.vect_hbox1=vect_hbox1
        self.vect_hbox2=vect_hbox2
        self.vect_length=vect_length
        self.vect_scale=vect_scale
        self.vect_stride=vect_stride
        self.vect_pts=vect_pts

        self.map_window=window
        self.map_window.show()


class Config_color_scales:
    ''' Configure color scales via a dialog popup '''

    def close_application(self, widget, data=None):
        self.map_window.destroy()

    def cscale_auto(self, widget):
        if self.toggle_cscale_auto.get_active():
            plotvars.cscale_auto_set=True
            self.toggle_cscale_reverse.set_sensitive(False)
            self.cscale_hbox1.set_sensitive(False)
            self.cscale_hbox2.set_sensitive(False)
            self.cscale_hbox3.set_sensitive(False)
            self.cscale_hbox4.set_sensitive(False)
	    self.cscale_label1.set_sensitive(False)
            self.cscale_label2.set_sensitive(False)
    	    self.cscale_label3.set_sensitive(False)
	    self.cscale_label4.set_sensitive(False)
	    self.cscale_label5.set_sensitive(False)
	    self.cscale_label6.set_sensitive(False)
	    self.cscale_label7.set_sensitive(False)
            for scale in cscales.cscales:
                exec('self.hbox_'+scale+'.set_sensitive(False)')
        else:
            plotvars.cscale_auto_set=False
            self.toggle_cscale_reverse.set_sensitive(True)
            self.cscale_hbox1.set_sensitive(True)
            self.cscale_hbox2.set_sensitive(True)
            self.cscale_hbox3.set_sensitive(True)
            self.cscale_hbox4.set_sensitive(True)
	    self.cscale_label1.set_sensitive(True)
            self.cscale_label2.set_sensitive(True)
    	    self.cscale_label3.set_sensitive(True)
	    self.cscale_label4.set_sensitive(True)
	    self.cscale_label5.set_sensitive(True)
	    self.cscale_label6.set_sensitive(True)
	    self.cscale_label7.set_sensitive(True)	    
            for scale in cscales.cscales:
                exec('self.hbox_'+scale+'.set_sensitive(True)')
            exec('self.toggle_'+plotvars.cscale+'.set_active(True)')

    def cscale_reverse(self, widget):
        if self.toggle_cscale_reverse.get_active():
            plotvars.cscale_reverse_set=True
        else:
            plotvars.cscale_reverse_set=False

    def set_white(self, widget):
        plotvars.cscale_white=self.set_white.get_text()

    def set_ncols(self, widget):
        plotvars.cscale_ncols=self.set_ncols.get_text()

    def set_above(self, widget):
        plotvars.cscale_above=self.set_above.get_text()

    def set_below(self, widget):
        plotvars.cscale_below=self.set_below.get_text()

    def cscale_set(self, widget, data=None):
        #turn off previous selection
        exec('self.toggle_'+plotvars.cscale+'.set_active(False)')
        plotvars.cscale=data

    def cscale_reset(self, widget):
        plotvars.cscale_auto_set=True
        plotvars.cscale_reverse_set=False
        plotvars.cscale_white=''
        plotvars.cscale_ncols=''
        plotvars.cscale_above=''
        plotvars.cscale_below=''
        exec('self.toggle_'+plotvars.cscale+'.set_active(False)')
        plotvars.cscale='viridis'
        self.toggle_cscale_auto.set_active(True)
        self.toggle_cscale_reverse.set_active(False)
        self.set_white.set_text('')
        self.set_ncols.set_text('')
        self.set_above.set_text('')
        self.set_below.set_text('')


    def cscale_help(self, widget):
        helpstr='<T>Colour scale options\
        \n\
        With the automatic colour scale option ticked cfview selects the colour scale as:\n\
        viridis - fields that don\'t span zero - temperature in Kelvin etc.  This is a perceptually\
        neutral colour scale that doesn\'t draw the eye to any part of the scale and is suitable for colour\
        blind viewers.\r\
        \r\
        scale1 - fields that span zero - zonal wind etc.  This is a blue - red scale suitable for\
        contour plots of fields that have a zero in their contour levels.  The scale is automatically\
        adjusted so that blue colours are below zero and red colours above.\r\
	\n\
        <H>Selecting a colour scale\r\
        When a different colour scale is selected the scale will be automatically adjusted\
        to fit the number of contour levels.\
	\n\
        <H>Selecting the number of colours\r\
        Set the number of colours in the scale to gain more control over the colour sca;e.  Once this is done then\
        the white indicies and scale midpoint options become useful to allow more precise\
        scale manipulation.  Set a number of white colour indicies by leaving a space between the indices required.\
        \n'
        CFview_help(helpstr, helpstr)

    def __init__(self, widget):
        import matplotlib
        import matplotlib.pyplot as plot

        # create a new window
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_usize(800, 850)
        window.set_title("Colour scale options")

        vbox = gtk.VBox(gtk.FALSE, 0)
        window.add(vbox)
        vbox.show()
        window.show()

        #Tick auto vectors
        toggle_cscale_auto = gtk.CheckButton("Automatic colour scale")
        toggle_cscale_auto.set_active(True)
        vbox.add(toggle_cscale_auto)
        toggle_cscale_auto.show()
        separator1 = gtk.HSeparator()
        vbox.add(separator1)
        separator1.show()


        #Tick for reverse
        toggle_cscale_reverse = gtk.CheckButton("Reverse colour scale")
        toggle_cscale_reverse.set_active(False)
        toggle_cscale_reverse.set_sensitive(False)
        vbox.add(toggle_cscale_reverse)
        toggle_cscale_reverse.show()


        #HBoxes for white, ncols, above and below
        cscale_hbox1=gtk.HBox()
        cscale_hbox1.show()
        vbox.add(cscale_hbox1)
        cscale_hbox1.set_sensitive(False)
        cscale_hbox2=gtk.HBox()
        cscale_hbox2.show()
        vbox.add(cscale_hbox2)
        cscale_hbox2.set_sensitive(False)
        cscale_hbox3=gtk.HBox()
        cscale_hbox3.show()
        vbox.add(cscale_hbox3)
        cscale_hbox3.set_sensitive(False)
        cscale_hbox4=gtk.HBox()
        cscale_hbox4.show()
        vbox.add(cscale_hbox4)
        cscale_hbox4.set_sensitive(False)
        separator2 = gtk.HSeparator()
        vbox.add(separator2)
        separator2.show()

        #labels and input boxes
        cscale_ncols_label=gtk.Label("Number of colours in the scale")
	cscale_ncols_label.set_alignment(xalign=0.05, yalign=0.5) 
        cscale_ncols = gtk.Entry()
        cscale_ncols.set_width_chars(3)
        cscale_ncols.set_text(plotvars.cscale_ncols)
        cscale_ncols.set_sensitive(True)
        cscale_white_label=gtk.Label("Set these colour indicies to white")
	cscale_white_label.set_alignment(xalign=0.05, yalign=0.5) 
        cscale_white = gtk.Entry()
        cscale_white.set_width_chars(8)
        cscale_white.set_text(plotvars.cscale_white)
        cscale_white.set_sensitive(True)
        cscale_above_label=gtk.Label("Number of colours above the scale midpoint")
	cscale_above_label.set_alignment(xalign=0.05, yalign=0.5) 
        cscale_above = gtk.Entry()
        cscale_above.set_width_chars(3)
        cscale_above.set_text(plotvars.cscale_above)
        cscale_above.set_sensitive(True)
        cscale_below_label=gtk.Label("Number of colours below the scale midpoint")
        cscale_below_label.set_alignment(xalign=0.05, yalign=0.5) 
        cscale_below = gtk.Entry()
        cscale_below.set_width_chars(3)
        cscale_below.set_text(plotvars.cscale_below)
        cscale_below.set_sensitive(True)

        #Show the widgets
        cscale_white_label.show()
        cscale_white.show()
        cscale_ncols_label.show()
        cscale_ncols.show()
        cscale_above_label.show()
        cscale_above.show()
        cscale_below_label.show()
        cscale_below.show()

        #Pack the widgets 
        cscale_hbox1.pack_start(cscale_ncols_label, padding=5, expand=False)
        cscale_hbox1.pack_start(cscale_ncols, padding=140,expand=False)
        cscale_hbox2.pack_start(cscale_white_label, padding=5, expand=False)
        cscale_hbox2.pack_start(cscale_white, padding=125,expand=False)
        cscale_hbox3.pack_start(cscale_above_label, padding=5, expand=False)
        cscale_hbox3.pack_start(cscale_above, padding=55,expand=False)
        cscale_hbox4.pack_start(cscale_below_label, padding=5, expand=False)
        cscale_hbox4.pack_start(cscale_below, padding=58,expand=False)


        ##############
        #Colour scales
        ##############

        #create a scrolled window and put a new vbox in it
        sw = gtk.ScrolledWindow()
        sw.set_usize(400, 500)
        sw.set_border_width(10)	
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)
        vbox.pack_start(sw, True, True, 0)
        sw.show()

        sw_vbox=gtk.VBox(gtk.FALSE, 0)
        sw.add_with_viewport(sw_vbox)
        sw_vbox.show()

        #Generate xpm colour codes for up to 260 colours
        #Maximum of 256 colours required
        letters=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',\
                 'w','x','y','z']
        codes=[]
        for j in np.arange(10):
            for i in np.arange(26):
                codes.append(letters[j]+letters[i])

        #Loop over the colour scales
        for scale in cscales.cscales:
		
            #Add section labels
            if scale == 'viridis': 	
		cscale_label1=gtk.Label('Perceptually neutral colour scales')
		cscale_label1.modify_font(pango.FontDescription("sans 16"))
		cscale_label1.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label1.set_sensitive(False)
                cscale_label1.show()
                sw_vbox.add(cscale_label1)		
            if scale == 'hotcold_18lev': 		
		cscale_label2=gtk.Label('\nNCAR Command Language - MeteoSwiss colour maps')
		cscale_label2.modify_font(pango.FontDescription("sans 16"))
		cscale_label2.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label2.set_sensitive(False)
                cscale_label2.show()
                sw_vbox.add(cscale_label2)			
            if scale == 'amwg': 	
		cscale_label3=gtk.Label('\nNCAR Command Language - small color maps (<50 colours)')
		cscale_label3.modify_font(pango.FontDescription("sans 16"))
		cscale_label3.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label3.set_sensitive(False)
                cscale_label3.show()
                sw_vbox.add(cscale_label3)		
            if scale == 'amwg256': 	
		cscale_label4=gtk.Label('\nNCAR Command Language - large color maps (>50 colours)')
		cscale_label4.modify_font(pango.FontDescription("sans 16"))
		cscale_label4.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label4.set_sensitive(False)
                cscale_label4.show()
                sw_vbox.add(cscale_label4)			
            if scale == 'StepSeq25': 	
		cscale_label5=gtk.Label('\nNCAR Command Language - Enhanced to help with colour blindness')
		cscale_label5.modify_font(pango.FontDescription("sans 16"))
		cscale_label5.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label5.set_sensitive(False)
                cscale_label5.show()
                sw_vbox.add(cscale_label5)									
            if scale == 'os250kmetres': 	
		cscale_label6=gtk.Label('\nOrography/bathymetry colour scales')
		cscale_label6.modify_font(pango.FontDescription("sans 16"))
		cscale_label6.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label6.set_sensitive(False)
                cscale_label6.show()
                sw_vbox.add(cscale_label6)		
            if scale == 'scale1': 	
		cscale_label7=gtk.Label('\nIDL guide scales')
		cscale_label7.modify_font(pango.FontDescription("sans 16"))
		cscale_label7.set_alignment(xalign=0.0, yalign=0.0) 
		cscale_label6.set_sensitive(False)
                cscale_label7.show()
                sw_vbox.add(cscale_label7)		
						
		
		
            #horizontal box for colour scales
            exec('hbox_'+scale+'=gtk.HBox()')
            exec('hbox_'+scale+'.show()')
            exec('hbox_'+scale+'.set_sensitive(False)')
            exec('sw_vbox.add(hbox_'+scale+')')

            #Add toggle
            exec('toggle_'+scale+" = gtk.CheckButton('"+scale+"')")
            exec('toggle_'+scale+'.set_active(False)')
            exec('toggle_'+scale+'.show()')
            exec('hbox_'+scale+'.pack_start(toggle_'+scale+')')
             
            #Connect the widget
            exec('toggle_'+scale+'.connect("toggled", self.cscale_set, "'+scale+'")')

            #Populate self with the widgets
            exec('self.toggle_'+scale+'=toggle_'+scale)
            exec('self.hbox_'+scale+'=hbox_'+scale)


            #Make the colour scales into images
            #Create an xpm file 1 pixel high with one pixel per colour scale element
            cscale=cfp.cscale(scale)
            xpm_data=[str(len(cfp.plotvars.cs))+' 1 '+ str(len(cfp.plotvars.cs))+' 2']

            npts=np.size(cfp.plotvars.cs)
            for col in np.arange(npts):
                xpm_data.append(codes[col]+' c '+cfp.plotvars.cs[col])

            codes_inc=''
            for col in np.arange(npts):
                codes_inc=codes_inc+codes[col]

            xpm_data.append(codes_inc)


            #Load xpm_data into a pixmap
            pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(window.window, None, xpm_data)

            #Convert to a pixbuf
            w,h = pixmap.get_size()
            pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, w, h)
            pixbuf.get_from_drawable(pixmap, pixmap.get_colormap(), 0, 0, 0, 0, w, h)

            #Scale up to a larger image
            pixbuf2=pixbuf.scale_simple(500, 15, gtk.gdk.INTERP_BILINEAR)
            image = gtk.image_new_from_pixbuf(pixbuf2)
            image.show()


   	    button = gtk.Button()
            button.props.relief = gtk.RELIEF_NONE
            button.add(image)
            button.set_alignment(1.0, 0.5)
 	    button.show() 
            exec('hbox_'+scale+'.pack_start(button)')


        #Set default scale to be viridis
        toggle_viridis.set_active(True)


        #######################################
        #Box to contain reset and close buttons
        #######################################
        box2 = gtk.HBox(False, 10)
        box2.set_border_width(10)
        box2.show()
        vbox.pack_end(box2, False, True, 0)

        button_reset = gtk.Button("reset")
        button_reset.connect("clicked", self.cscale_reset)
        box2.pack_start(button_reset, True, True, 0)
        button_reset.set_flags(gtk.CAN_DEFAULT)
        button_reset.grab_default()
        button_reset.show()

        button_help = gtk.Button("help")
        button_help.connect("clicked", self.cscale_help)
        box2.pack_start(button_help, True, True, 0)
        button_help.set_flags(gtk.CAN_DEFAULT)
        button_help.grab_default()
        button_help.show()

        button_close = gtk.Button("close")
        button_close.connect("clicked", self.close_application)
        box2.pack_start(button_close, True, True, 0)
        button_close.set_flags(gtk.CAN_DEFAULT)
        button_close.grab_default()
        button_close.show()


        ####################
        #Connect the widgets
        ####################
        toggle_cscale_auto.connect("toggled", self.cscale_auto)
        toggle_cscale_reverse.connect("toggled", self.cscale_reverse)
        cscale_white.connect("changed", self.set_white)
        cscale_ncols.connect("changed", self.set_ncols)
        cscale_above.connect("changed", self.set_above)
        cscale_below.connect("changed", self.set_below)

        ###############################
        #Populate self with the widgets
        ###############################
        self.toggle_cscale_auto=toggle_cscale_auto
        self.toggle_cscale_reverse=toggle_cscale_reverse
        self.cscale_hbox1=cscale_hbox1
        self.cscale_hbox2=cscale_hbox2
        self.cscale_hbox3=cscale_hbox3
        self.cscale_hbox4=cscale_hbox4
        self.set_white=cscale_white
        self.set_ncols=cscale_ncols
        self.set_above=cscale_above
        self.set_below=cscale_below
	self.cscale_label1=cscale_label1
	self.cscale_label2=cscale_label2
	self.cscale_label3=cscale_label3
	self.cscale_label4=cscale_label4
	self.cscale_label5=cscale_label5
	self.cscale_label6=cscale_label6
        self.cscale_label7=cscale_label7

        self.map_window=window
        #self.map_window.show()


class Config_reset_all:
    ''' Reset all the configuration settings back to the defaults '''

    def __init__(self, nodata):
        plotvars.levs_min='0'
        plotvars.levs_max='0'
        plotvars.levs_step='0'
        plotvars.levs_set=False
        plotvars.levs_manual_set=False
        plotvars.levs_manual=''
        plotvars.levs_extend_lower=True
        plotvars.levs_extend_upper=True



class CFview_help: 
    ''' Display help text '''
    def close_application(self, widget):
        #widget.destroy()
        self.text_window.destroy()

    def __init__(self, widget, helpstr):
        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        window.set_resizable(True)  
        window.connect("destroy", self.close_application)


        window.set_border_width(0)
        window.set_size_request(700, 900)


        box1 = gtk.VBox(False, 0)
        window.add(box1)
        box1.show()

        box2 = gtk.VBox(False, 10)
        box2.set_border_width(10)        
        box1.pack_start(box2, True, True, 0)
        box2.show()

        sw = gtk.ScrolledWindow()
        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        textview = gtk.TextView()
        textview.set_wrap_mode(gtk.WRAP_WORD)
        textbuffer = textview.get_buffer()
        sw.add(textview)
        sw.show()
        textview.show()

        box2.pack_start(sw)               

        hbox = gtk.HButtonBox()
        hbox.set_size_request(200,100)
        box2.pack_start(hbox, False, False, 0)
        box2.set_size_request(200,100)
        hbox.show()

        vbox = gtk.VBox()
        vbox.show()
        hbox.pack_start(vbox, False, False, 0)

        #Create tags using pango
        title_tag = textbuffer.create_tag( "h", size_points=20, font="Helvetica", weight=pango.WEIGHT_BOLD) 
        header_tag = textbuffer.create_tag( "tb", size_points=16, font="Helvetica", weight=pango.WEIGHT_BOLD) 
        text_tag = textbuffer.create_tag( "t", size_points=14, font="Helvetica") 

        #Set size of tabs
        tab_size=14

        #Add to text 
        for lines in helpstr.splitlines(True):
           #line=(lines.lstrip()).rstrip()
           #Strip the extra spaces out of the line
           line=' '.join(filter(None,lines.split(' ')))

           position = textbuffer.get_end_iter()
           if line[:3] == '<T>' : 
               window.set_title(line[3:])
               textbuffer.insert_with_tags( position, line[3:]+'\n', title_tag)
           elif line[:3] == '<H>' : 
               textbuffer.insert_with_tags( position, line[3:]+'\n', header_tag)
           else:
               textbuffer.insert_with_tags( position, line+'\n', text_tag)


        box2 = gtk.VBox(False, 10)
        box2.set_border_width(10)
        box1.pack_start(box2, False, True, 0)
        box2.show()

        button = gtk.Button("close")
        button.connect("clicked", self.close_application)
        box2.pack_start(button, True, True, 0)
        button.set_flags(gtk.CAN_DEFAULT)
        button.grab_default()
        button.show()

        self.text_window=window
        self.text_window.show()


def main(filename):
    ''' main loop for cfview '''
    c=cfview(filename)
    gtk.main()
    return 0
        
if __name__=="__main__":
    args=sys.argv
    if len(args)>2:
        print 'Usage: cfview <filename>   (filename is optional)'
    elif len(args)==2:
        main(args[1])
    else: main(None)


