#!/usr/bin/env python3

# Temporary fix for pillow warning
# DeprecationWarning: Support for PyQt5 is deprecated and will be removed in Pillow 10 (2023-07-01). Use PyQt6 or PySide6 instead.
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

import cf
import cfplot as cfp
import numpy as np
import numpy.ma as ma
import os
import sys
import argparse
import matplotlib.pyplot as plt
import matplotlib
from PyQt5.QtWidgets import QApplication, QLabel, QCheckBox, QFileDialog, QDialog, QRadioButton, QAction
from PyQt5.QtWidgets import QComboBox, QListWidget, QListWidgetItem, QHBoxLayout, QVBoxLayout, QWidget, QPushButton
from PyQt5.QtWidgets import QLineEdit, QTextEdit, QSlider, QMenu, QFontDialog, QMenuBar
from PyQt5.QtWidgets import QMainWindow, QFrame, QSizePolicy, QScrollBar, QScrollArea, QFormLayout, QGroupBox, QTableWidget
from PyQt5.QtWidgets import QTableWidgetItem, QTableView, QSlider, QAbstractScrollArea, QSpacerItem, QHeaderView
from PyQt5.QtCore import Qt, QObject, pyqtSignal, QAbstractTableModel, QVariant, QCoreApplication
from PyQt5.QtGui import QFont, QPalette, QColor, QTextDocument, QPixmap, QImage, QStandardItemModel, QFontDatabase, QFontMetrics
from copy import deepcopy
from functools import partial
from PIL import Image, ImageDraw
from PIL.ImageQt import ImageQt
import csv



# Initiate the pvars class
# This is used for storing plotting variables in plotvars
class pvars(object):
    def __init__(self, **kwargs):
        '''Initialize a new Pvars instance'''
        for attr, value in kwargs.items():
            setattr(self, attr, value)

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

cols = ['#abb6d4', '#7085c4', '#ffffff', '#f1f4fd', '#000000', '#000000']

plotvars=pvars(pos=0, nplots=1, code='', plot_counter=0, title='', field_selected='',\
               file_selected=None, data=None, levs_automatic_set=True, 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, colorbar=True,\
               colorbar_orientation=None,\
               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,\
               cscales=None, cscale_automatic_set=True, cscale_reverse_set=False, cscale_white='',\
               cscale_ncols='', cscale_above='', cscale_below='',cscale='viridis',\
               invalid_input=False, index_number=0, fields=None,\
               field__widget_indicies=None, field_widget_names=None, field_widget_title=None,\
               field_widget_listing='index', plot_type = 'Contour', contour_type='x-y', \
               collapse_x='Off', collapse_y='Off', collapse_z='Off', collapse_t='Off',\
               collapse_labels=['X', 'Y', 'Z', 'T'],\
               nx_length=None, ny_length=None, nz_length=None, nt_length=None,\
               stored = {},\
               stored_dimensions=None, stored_collapses=None, stored_collapse_values=None,\
               stored_lock = False,\
               background_colour=cols[0], button_colour=cols[1], buttontext_colour=cols[2],\
               highlight_colour=cols[3], text_colour=cols[4], windowtext_colour=cols[5],\
               text_colour_insensitive = '#708090', font="DejaVu Sans", font_mono='DejaVu Sans Mono',\
               fontsize=15, fill=True, blockfill=False, lines=False, line_labels=False, titles=True,\
               blockfill_fast=False,\
               write_defaults=False, collapse_types=None, line_type='x', line_colour = 'C0',\
               line_style='solid', line_xmin=None, line_xmax=None, line_ymin=None, line_ymax=None,\
               line_title='', line_width='1.0', line_marker = '', line_markersize='',\
               line_markeredgecolor='', line_markeredgewidth='', line_limits='automatic',\
               vector_title='', vector_scale=100, vector_stride='', vector_pts='',\
               vector_key_length=10, vector_pivot='middle', \
               trajectory_title='', trajectory_marker_shape='o', trajectory_marker_size='5',\
               trajectory_marker_face_colour='red', trajectory_marker_edge_colour='green', \
               trajectory_line_style='solid', trajectory_line_colour='blue', \
               trajectory_line_width='1.0', trajectory_vector=False, trajectory_legend=False,\
               trajectory_date_min='', trajectory_date_max='', dim_names=['x', 'y', 'z', 't'],\
               code_output=True, data_source_list=None, terminal_output=False, next_field_index=0,\
               max_axes=10, nlevs=False)


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']

plotvars.cscales=cscales

plotvars.collapse_types = ['Off', 'mean', 'minimum', 'maximum', 'variance', 'standard_deviation']

# Mac issue with pyqt5 as described at 
# https://stackoverflow.com/questions/64833558/apps-not-popping-up-on-macos-big-sur-11-0-1
# has resurfaced with pyqt5 = 4.19.18
# pip install PyQt5==5.15.4 fixes it and also the following environment variable
os.environ["QT_MAC_WANTS_LAYER"] = "1"

class EmittingStream(QObject):

    textWritten = pyqtSignal(str)

    def write(self, text):
        self.textWritten.emit(str(text))

    def flush(self):
        pass


def my_excepthook(type, value, tback):
    # log the exception here

    # then call the default handler
    sys.__excepthook__(type, value, tback)

sys.excepthook = my_excepthook


class Cfview(QMainWindow):
    '''
     Main class for cfview
    '''
    def __init__(self, filename, defaults, parent=None):
        super(Cfview, self).__init__()
        plotvars.file_selected = filename
        
        # Set the file string in plotvars
        if plotvars.file_selected is not None:
            if len(plotvars.file_selected) == 1:
                plotvars.file_read_str = "'"+ plotvars.file_selected[0] + "'"
            else:
                fstring = "['"
                for i in np.arange(len(plotvars.file_selected)):
                    fstring += plotvars.file_selected[i] + "', '"
                fstring = fstring[:-4] + "']"
                plotvars.file_read_str = fstring 
        
        # Set self.parent to self
        self.parent = self

        # Read in the defaults file if it exists
        full_path = os.path.expanduser(defaults)
        check = os.path.isfile(full_path) 

        if check:
            datafile = open(full_path, 'r')
            for line in datafile:
                vars = line.split(' ')
                if line[0] != '#' and len(vars) > 1:
                    var = vars[0]
                    value = ''
                    for i in np.arange(len(vars[1:])):
                        if i > 0:
                            value += ' ' 
                        value += vars[i+1]
                    value = value.replace("\n", "")

                    if value == 'True':
                        value = True
                    if value == 'False':
                        value = False
            
                    if var == 'fontsize':
                        value = int(value)

                    setattr(plotvars, var, value)
        else:
            if defaults != '~/.cfview_defaults':
                print("\nDefaults file " + defaults + " doesn't exist\n")


        self.initUI()


    def initUI(self):

        # Add a top menu bar
        bar = self.menuBar()
        # Turn off Mac bar relocation 
        bar.setNativeMenuBar(False)

        file = bar.addMenu("File")
        file.addAction("Open")
        file.addAction("Save")
        file.addAction("Exit")
        file.triggered.connect(self.file_menu)


        edit = bar.addMenu("Edit")
        edit.addAction("Copy field")
        edit.addAction("Delete selected fields")
        edit.addAction("Delete all fields")
        edit.triggered.connect(self.menu)


        setup = bar.addMenu("Setup")
        setup.addAction("cfview defaults")
        setup.addAction("contours")
        setup.addAction("lines")
        setup.addAction("vectors")
        setup.addAction("trajectories")
        setup.addAction("map")
        setup.addAction("colour scale")
        setup.addAction("contour levels")
        setup.triggered.connect(self.menu)

        view = bar.addMenu("View")
        view.addAction("data")
        view.addAction("properties")
        view.addAction("transform")
        view.triggered.connect(self.menu)

        help = bar.addMenu("Help")
        help.addAction("cfview")
        help.addAction("about")
        help.triggered.connect(self.menu)


        # Set up some buttons
        plotButton = QPushButton("Plot")
        plotButton.clicked.connect(self.plot)

        reset_dimensionButton = QPushButton("Reset Dimensions")
        reset_dimensionButton.clicked.connect(lambda: self.dim_traj_reset('Reset Dimensions'))



        # Plot type combobox
        plot_type_ComboBox = QComboBox()
        plot_type_ComboBox.addItem('Contour')
        plot_type_ComboBox.addItem('Vector')
        plot_type_ComboBox.addItem('Contour and vector')
        plot_type_ComboBox.addItem('Line')
        plot_type_ComboBox.addItem('Trajectory')
        plot_type_ComboBox.activated[str].connect(self.plot_type)


        # Contour type drop down menu
        contour_type_ComboBox = QComboBox()
        contour_type_ComboBox.activated[str].connect(self.contour_type)
        self.contour_type_ComboBox = contour_type_ComboBox


        # Line type drop down menu
        line_type_ComboBox = QComboBox()

        line_type_ComboBox.activated[str].connect(self.line_type)
        self.line_type_ComboBox = line_type_ComboBox

        # Dimension selection
        dimLabel = QLabel('Select:')

        # Field selection widgets
        indexCheckBox = QCheckBox("index")
        indexCheckBox.setChecked(True)
        indexCheckBox.clicked.connect(lambda: self.field_widget_listing('index'))
        fieldCheckBox = QCheckBox("field name")
        fieldCheckBox.setChecked(False)
        fieldCheckBox.clicked.connect(lambda: self.field_widget_listing('field'))
        sort_byLabel = QLabel('Order by:')
        search_fieldsLabel = QLabel('Search:')
        search_fieldsTextbox = QLineEdit()
        search_fieldsTextbox.textChanged.connect(self.search_fields)


        fieldlist = QListWidget()
        fieldlist.setSelectionMode(QListWidget.ExtendedSelection)
        fieldlist.itemSelectionChanged.connect(self.fieldnumber)
        
        field_titles = QLabel('Index  nX   nY   nZ   nT   field name')

        # Set fixed width font for field titles
        custom_font_fixed_width = QFont(plotvars.font_mono, plotvars.fontsize)
        field_titles.setFont(custom_font_fixed_width)
        fieldlist.setFont(custom_font_fixed_width)


        # Messages label
        messagesLabel = QLabel('Output messages')
        messagesLabel.setAlignment(Qt.AlignCenter)


        # Dimension lists
        dimension_titles = QLabel('Dimension view')
        
        # New ajh code - extend up to 10 data dimensions
        # self.lists contain axis values
        # self.lists_collapse contain just one value for the collapse centre
        self.axis_RadioButtons = []
        self.lists = []
        self.lists_collapse = []


        for i in np.arange(plotvars.max_axes):
            self.axis_RadioButtons.append(QRadioButton(''))
            self.axis_RadioButtons[i].setVisible(False)       
            self.axis_RadioButtons[i].setChecked(False)
            self.axis_RadioButtons[i].clicked.connect(partial(self.dim_view, i))
            
            self.lists.append(QListWidget())
            self.lists[i].setSelectionMode(QListWidget.ExtendedSelection)
            self.lists[i].setFont(custom_font_fixed_width)
            self.lists[i].itemSelectionChanged.connect(self.reset_field_title)
            self.lists[i].setVisible(False)
            
            self.lists_collapse.append(QListWidget())
            self.lists_collapse[i].setSelectionMode(QListWidget.ExtendedSelection)
            self.lists_collapse[i].setFont(custom_font_fixed_width)
            self.lists_collapse[i].itemSelectionChanged.connect(self.reset_field_title)
            self.lists_collapse[i].setVisible(False)

        self.lists[0].setVisible(True)
            


        # Set the stored_dimensions and stored_collapses for each of the fields in the field list
        # -1 indicates that the dimension is unchanged by the user
        plotvars.stored_dimensions = {}
        plotvars.stored_collapses = {}
        plotvars.stored_collapse_values = {}



        # Vector and contour and vector hbox
        contour_vector_indexHbox = QHBoxLayout()
        contour_indexLabel = QLabel('Contour Index')
        contour_indexComboBox = QComboBox()
        vector_x_indexLabel = QLabel('Vector x Index')
        vector_x_indexComboBox = QComboBox()
        vector_y_indexLabel = QLabel('Vector y Index')
        vector_y_indexComboBox = QComboBox()
        contour_vector_indexHbox.addWidget(contour_indexLabel)
        contour_vector_indexHbox.addWidget(contour_indexComboBox)
        contour_vector_indexHbox.addWidget(vector_x_indexLabel)
        contour_vector_indexHbox.addWidget(vector_x_indexComboBox)
        contour_vector_indexHbox.addWidget(vector_y_indexLabel)
        contour_vector_indexHbox.addWidget(vector_y_indexComboBox)

        objs = [contour_indexLabel, contour_indexComboBox,
        vector_x_indexLabel, vector_x_indexComboBox,
        vector_y_indexLabel, vector_y_indexComboBox]

        for obj in objs:
            obj.setVisible(False)




        # Output window
        output_terminal_textEdit = QTextEdit()

        # Define the top level containers to put the widgets in
        outerLayout = QHBoxLayout()
        leftLayout = QVBoxLayout()
        rightLayout = QVBoxLayout()

        # Define the left layout widget boxes
        leftHbox0 = QHBoxLayout()
        leftHbox1 = QHBoxLayout()
        leftHbox2 = QHBoxLayout()
        leftHbox3 = QHBoxLayout()
        leftHbox4 = QHBoxLayout()
        leftHbox5 = QHBoxLayout()

        # Define the right layout widget boxes
        rightHbox1 = QHBoxLayout()
        rightHbox2 = QHBoxLayout()
        rightHbox3 = QHBoxLayout()
        rightHbox4 = QHBoxLayout()

        # containers for field and dimension lists
        field_container = QVBoxLayout()
        field_container1 = QVBoxLayout()
        field_container1.setContentsMargins(2,0,0,0)
        field_container2 = QVBoxLayout()
        dimension_container = QVBoxLayout()

        # Assemble the layout boxes
        leftLayout.addLayout(leftHbox1)
        leftLayout.addLayout(leftHbox2)
        leftLayout.addLayout(leftHbox3)
        leftLayout.addLayout(leftHbox4)
        leftLayout.addLayout(leftHbox5)

        rightLayout.addLayout(rightHbox1)
        rightLayout.addLayout(contour_vector_indexHbox)
        rightLayout.addLayout(rightHbox2)
        rightLayout.addLayout(rightHbox3)
        rightLayout.addLayout(rightHbox4)

        outerLayout.addLayout(leftLayout)
        outerLayout.addLayout(rightLayout)

        # Add widgets to the appropriate boxes
        rightHbox1.addWidget(plot_type_ComboBox)
        rightHbox1.addWidget(contour_type_ComboBox)
        rightHbox1.addWidget(line_type_ComboBox)
        line_type_ComboBox.setVisible(False)

        rightHbox1.addWidget(plotButton)

        leftHbox2.addLayout(field_container)

        field_container.addLayout(field_container1)
        field_container.addLayout(field_container2)
        field_container1.addWidget(field_titles)
        field_container2.addWidget(fieldlist)


        rightHbox2.addLayout(dimension_container)
        dimension_container.addWidget(dimension_titles)
        
        for i in np.arange(plotvars.max_axes):
            dimension_container.addWidget(self.lists[i])
            dimension_container.addWidget(self.lists_collapse[i])        

        self.rightHbox3 = rightHbox3


        leftHbox3.addWidget(sort_byLabel)
        leftHbox3.addWidget(indexCheckBox)
        leftHbox3.addWidget(fieldCheckBox)
        leftHbox3.addWidget(search_fieldsLabel)
        leftHbox3.addWidget(search_fieldsTextbox)
        leftHbox4.addWidget(messagesLabel)
        leftHbox5.addWidget(output_terminal_textEdit)
        

        widget = QWidget()
        widget.setLayout(outerLayout)
        self.setCentralWidget(widget)

       


        self.field_titles = field_titles
        self.dimension_titles = dimension_titles
        self.fieldlist = fieldlist
        self.plot_type_ComboBox = plot_type_ComboBox
        

        self.dimLabel = dimLabel
        self.reset_dimensionButton = reset_dimensionButton


        self.contour_vector_indexHbox = contour_vector_indexHbox
        self.contour_indexLabel = contour_indexLabel
        self.contour_indexComboBox = contour_indexComboBox
        self.vector_x_indexLabel = vector_x_indexLabel
        self.vector_x_indexComboBox = vector_x_indexComboBox
        self.vector_y_indexLabel = vector_y_indexLabel
        self.vector_y_indexComboBox = vector_y_indexComboBox

        self.indexCheckBox = indexCheckBox
        self.fieldCheckBox = fieldCheckBox
        self.search_fieldsTextbox = search_fieldsTextbox

        self.cfview_defaults = None  # No external window yet.
        self.contour_window = None  # No external window yet.
        self.contour_levels = None  # No external window yet.
        self.line_window = None  # No external window yet.
        self.vector_window = None  # No external window yet.
        self.trajectory_window = None  # No external window yet.
        self.colour_scale = None  # No external window yet.
        self.map_window = None  # No external window yet.
        self.data_window = None  # No external window yet.
        self.names_window = None  # No external window yet.
        self.transform_window = None  # No external window yet.
        self.about_window = None  # No external window yet.
        self.cfview_window = None  # No external window yet.

        self.output_terminal_textEdit = output_terminal_textEdit


        # ajh - new - Add axis radio buttons to rightHbox3
        # Only four initially as they are set to X, Y, Z, T
        rightHbox3.addWidget(self.dimLabel)
        for i in np.arange(plotvars.max_axes):
            rightHbox3.addWidget(self.axis_RadioButtons[i])
        
        # ajh - new - Initial setting for axes
        myaxes = ['X', 'Y', 'Z', 'T']
        for i in np.arange(len(myaxes)):
            self.axis_RadioButtons[i].setVisible(True)
            self.axis_RadioButtons[i].setText(myaxes[i])
        self.axis_RadioButtons[0].setChecked(True)

            
        rightHbox3.addWidget(reset_dimensionButton)
        
            

        # Interface colour settings
        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)


        # Set main font
        db = QFontDatabase()
        fonts = QFontDatabase().families()
        test_fonts = ['Times', 'Helvetica', 'Arial', deepcopy(plotvars.font)]
        for font in test_fonts:
            if font in fonts:
                plotvars.font = font

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)
        file.setFont(custom_font)
        edit.setFont(custom_font)
        setup.setFont(custom_font)
        view.setFont(custom_font)
        help.setFont(custom_font)

        # Set mono font for field titles and field list
        test_fonts = ['Times', 'Helvetica', 'Courier', 'Menlo', 'Monaco', deepcopy(plotvars.font_mono)]
        for font in test_fonts:
            if font in fonts:
                plotvars.font_mono = font

        custom_font_mono = QFont(plotvars.font_mono, plotvars.fontsize)
        self.field_titles.setFont(custom_font_mono)
        self.fieldlist.setFont(custom_font_mono)

        # Set stdout and stderr to go to EmittingStream
        if not plotvars.terminal_output:
            sys.stdout = EmittingStream(textWritten=self.output_terminal_written)
            sys.stderr = EmittingStream(textWritten=self.output_terminal_written)
        else:
            sys.stdout = sys.__stdout__
            sys.stderr = sys.__stderr__
                

        # Set window size and show the window
        #self.setGeometry(0, 0, 1280, 1060)
        self.setWindowTitle('cfview')
        
        # Read in the data and set widgets if file(s) are specified
        if plotvars.file_selected is not None:
            self.read_file(self)

                
        self.show()
        self.raise_()


    def open_file(self, s):
        '''
         Open a file and populate dimension lists
        ''' 
        dialog = QFileDialog()
        
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        #dialog.setOption(QCoreApplication.AA_DontUseNativeDialogs, True)
        dialog.setAcceptMode(QFileDialog.AcceptOpen)
        #dialog.setNameFilters(["(*.nc *.pp)", "(*.nc)", "(*.pp)", "(*.*)"])
        dialog.setNameFilters(["*.nc *.pp", "*.nc", "*.pp", "*.*"])
        dialog.setFileMode(QFileDialog.ExistingFiles)

 
        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        dialog.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        dialog.setFont(custom_font)

        path =  ''
        if dialog.exec_() == QDialog.Accepted:
            path =  dialog.selectedFiles()
        

        plotvars.file_selected = path    
        
        # Read in the data and set widgets
        self.read_file(self)
        




    def read_file(self, s):
        '''
         Read file(s) and set widget values
        '''
    
        path = plotvars.file_selected
        
        try:
            if path != '':
                f = cf.read(path)
            else:
                print("\nNo file selected\n")
                return
        except:
            errstr = 'One of the files selected is not a valid, netCDF, PP or fields file or does not exist - '
            for file in path:
                errstr += file + ' '
            print(errstr)
                
            return
        
        
        plotvars.data_source_list = []
        for i in np.arange(len(f)):
            plotvars.data_source_list.append(plotvars.file_selected)



        if f[0].get_property('featureType', False) is not False:
            if f[0].featureType == 'trajectory':
                self.dimension_titles.setText('Trajectory number')
                self.plot_type_ComboBox.setCurrentIndex(4)
                plotvars.plot_type='Trajectory'
                self.dimLabel.setVisible(False)
                self.reset_dimensionButton.setText('Reset Tracks')
        else:
            self.dimension_titles.setText('Dimension view')
            self.plot_type_ComboBox.setCurrentIndex(0)
            plotvars.plot_type = 'Contour'
            self.dimLabel.setVisible(True)
            self.reset_dimensionButton.setText('Reset Dimensions')



        self.fieldlist.clear()
        self.search_fieldsTextbox.clear()
        plotvars.index_number = 0
        plotvars.fields = f
        plotvars.field_widget_indicies = None
        plotvars.next_field_index = len(f)



        # Set the stored_dimensions
        # -1 indicates that the dimension is unchanged
        plotvars.stored_dimensions = {}
        plotvars.stored_collapses = {}
        plotvars.stored = {}
        max_axes_number = 0
        maximum_axis_lengths = []
        
        if plotvars.file_selected is not None:
            for i in np.arange(len(f)):       
                                
                data_type = 'dim'
                if f[i].get_property('featureType', False) is not False:
                    if f[i].featureType == 'trajectory':
                        data_type = 'trajectory'
                        
          
                axes = []
                collapses =[]
                collapse_values = []        
                        
                        
                if data_type == 'dim':
                

                    # Axes X, Y, Z, T and dimensioncoordinate1, dimensioncoordinate0, ax0, ax1
                    data_axes = Cf_funcs.find_dim_names(f[i])        
                                  
                                  
                    axis_sizes = []
                    for j in np.arange(len(data_axes)):
                        axis_sizes.append(len(f[i].coord(data_axes[j]).array))
                
                    # ax coordinates - ax2, ax1, ax0
                    daxes = list(f[i].get_data_axes())
                    if len(data_axes) == 0 and len(daxes) > 0:
                        data_type = 'axis'
                        for j in np.arange(len(daxes)):
                            data_axes.append('ax' + str(j))
                            axis_sizes.append(f[i].domain_axis(daxes[j]).get_size())

                    naxes = len(data_axes)
                    for k in np.arange(naxes):
                        axes.append(-1)
                        collapses.append('Off')

                
                else:
                     data_axes = ['tracks']
                     axes.append(-1)
                     coords = list(f[i].coords())
                     for mycoord in coords:
                         if f[i].coord(mycoord).nc_get_variable() == 'longitude':
                             ntracks = np.shape(f[i].auxiliary_coordinate('longitude').array)[0]

                     axis_sizes = [ntracks]
                     
                # Initial population of maximum_data_sizes
                if len(data_axes) > max_axes_number:
                    max_axes_number = len(data_axes)

                    
                # Change to axes, X, Y, Z, T and dim1, dim0, ax0, ax1
                for j in np.arange(len(data_axes)):    
                    if data_axes[j][:19] == 'dimensioncoordinate':
                        data_axes[j] = 'dim' + data_axes[j][19:]                         
                    
                field_name = Cf_funcs.field_name(f[i])
                
                
                field_dict = {'data_axes': data_axes, 'axis_sizes': axis_sizes,\
                              'axes': axes, 'collapses': collapses, 'collapse_value': False,\
                              'data_type': data_type, 'source': plotvars.file_selected,\
                              'field_name': field_name}
                              
                plotvars.stored['f' + str(i)] = field_dict
                
                
                
                
                
        # Calculate maximum lengths of the data values in characters
        # Use a minimum of 5 characters - dim0 plus a space = 5
        for i in np.arange(max_axes_number):
            maximum_axis_lengths.append(5)

        for i in np.arange(len(f)):
            myaxis_sizes = plotvars.stored['f' + str(i)]['axis_sizes']
            for j in np.arange(len(myaxis_sizes)):
                if len(str(myaxis_sizes[j])) > maximum_axis_lengths[j]:
                    maximum_axis_lengths[j] = len(str(myaxis_sizes[j]))
                    
        plotvars.maximum_axis_lengths = maximum_axis_lengths
            
        

        
        field_widget_names, field_widget_title = Cf_funcs.fields_list(self, fields=f, order=plotvars.field_widget_listing)       
        self.fieldlist.addItems(list(str(w) for w in field_widget_names))
        self.fieldlist.setCurrentRow(0) 


        self.field_titles.setText(field_widget_title)

      

        # Set the file string in plotvars
        if len(plotvars.file_selected) == 1:
            plotvars.file_read_str = "'" + plotvars.file_selected[0] + "'"
        else:
            fstring = "['"
            for i in np.arange(len(plotvars.file_selected)):
                fstring += plotvars.file_selected[i] + "', '"
            fstring = fstring[:-4] + "']"
            plotvars.file_read_str = fstring 

        # Call fieldnumber to set the field number
        self.fieldnumber()


        # Print which file has been opened in the messages window
        print('File read: ' + str(path))



    def save_file(self, s):
        '''
         Save a file using selected fields, diemsion selections and collapses
        '''

        dialog = QFileDialog()

        dialog.setAcceptMode(QFileDialog.AcceptSave)
        dialog.setNameFilters(["netCDF Files (*.nc)", "All Files (*.*)"])
        dialog.setOption(QFileDialog.DontUseNativeDialog, True)
        #dialog.setOption(QCoreApplication.DontUseNativeDialogs, True)
        #dialog.setOption(QFileDialog.AA_DontUseNativeDialogs, True)
 
        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        dialog.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        dialog.setFont(custom_font)

        path =  ''
        if dialog.exec_() == QDialog.Accepted:
            path =  dialog.selectedFiles()[0]

        # Return if no file specified or if cancel has been pressed
        if len(path) == 0:
            return


        # Assemble cf-python code to write the file
        com = 'import cf\n'
        com += "f = cf.read(" + plotvars.file_read_str + ")\n\n"
        com += '# Create a list of fields to be saved'
        com += '\nsave_fields = []\n'

        # Assemble the selected field indicies from the field titles
        selected = self.fieldlist.selectedItems()
        indicies = []
        for i in np.arange(len(selected)):
            field_title = selected[i].text()
            indicies.append(int(field_title.split()[0]))

        #dims_regular = ['X', 'Y', 'Z', 'T']
        #dims = Cf_funcs.find_dim_names(plotvars.fields[plotvars.index_number])


        save_fields = []

        # Set the data file
        f = plotvars.fields

        # Loop over the selected fields
        for index in np.arange(len(indicies)):
        
            if plotvars.stored['f' + str(indicies[index])]['source'] == 'Changed':
                com +='field index = ' + str(indicies[index]) + " - see previous code for how to change this field's properties\n"
            if plotvars.stored['f' + str(indicies[index])]['source'] == 'Computed':
                com +='field index = ' + str(indicies[index]) + ' - see previous code for how to compute this field\n'
            if plotvars.stored['f' + str(indicies[index])]['source'] == 'Copied':
                com +='field index = ' + str(indicies[index]) + ' - see previous code for how this field was copied\n'

            # Form the subspace dictionary
            stored = plotvars.stored['f' + str(indicies[index])]
            data_axes = stored['data_axes']
            data_axes_orig = deepcopy(data_axes)
            for i in np.arange(len(data_axes)):
                if data_axes[i][:3] == 'dim':
                    data_axes[i] = 'dimensioncoordinate' + data_axes[i][3:]
            mycollapses = stored['collapses']
            myaxes = stored['axes']
            subspace_args = {}

            # Find the original nx, ny, nz, nt for this field
            field = f[indicies[index]]
            com += 'field = f[' + str(indicies[index]) + ']\n'
            
            for i in np.arange(len(data_axes)):
                # dim_indicies = -1 means no user change to this dimension
                myaxis = data_axes[i]
                myaxis_orig = data_axes_orig[i]
                axis_indicies = myaxes[i]
                if field.coord(myaxis).T:
                    axis_vals = field.coord(myaxis).dtarray
                else:
                    axis_vals = field.coord(myaxis).array


                if axis_indicies != -1:
                    if len(axis_indicies) == 1:
                        subspace_args[myaxis_orig] = axis_vals[axis_vals[i][0]]
                        com += 'field = field.subspace(' + mydim + '=' + str(axis_vals[i][0]) + ')\n'
                    else:

                        # Check for contiguous data selection
                        contig = True
                        if max(axis_indicies) - min(axis_indicies) != len(axis_indicies) - 1:
                            contig = False
                        
                        valmin = axis_vals[min(axis_indicies)]
                        valmax = axis_vals[max(axis_indicies)]
                        
                        # cf-python needs ascending values for cf.wi
                        if valmax < valmin:
                            valmin, valmax = valmax, valmin

                        if contig:
                            subspace_args[data_axes[i]] = cf.wi(valmin, valmax)
                            com += 'field = field.subspace(' + data_axes[i] + ' = cf.wi('
                            com +=  str(valmin) + ', ' + str(valmax) + '))\n'
                        else:
                            subspace_args[data_axes[i]] = sorted(axis_indicies)
                            com += 'field = field.subspace(' + data_axes[i] + '= ['
                            for ind in np.arange(len((axis_indicies))):
                                com += str(sorted(axis_indicies)[ind]) 
                                if ind < len(axis_indicies) - 1:
                                    com += ', ' 
                            com += '])\n'


            # Form the collapse string
            collapse_str = ''
            #stored = plotvars.stored_collapses['f' + str(indicies[index])] 
            for i in np.arange(4):
                 if mycollapses[i] != 'Off':
                     collapse_str += data_axes[i] + ': ' + mycollapses[i] + ' '
            if len(collapse_str) > 0:
                if collapse_str[-1] == ' ':
                    collapse_str = collapse_str[:-1]


            # Subspace and collapse the data if required
            if len(subspace_args) > 0:
                field = field.subspace(**subspace_args)

            if len(collapse_str) > 0:
                field = field.collapse(collapse_str)
                com += "field = field.collapse('" + collapse_str + "')\n" 

            save_fields.append(field)
            com += 'save_fields.append(field)\n' 



        # Write the netCDF file
        cf.write(save_fields, path)

        # Show the cf-python code
        com += '\n# Save the data\n'
        com += "cf.write(save_fields, '" + path + "')"
        
        if plotvars.code_output:
           print('### Code to write the file is ###\n', com)


        


    def plot(self, s):
        '''
         Make a contour, vector, line or trajectory plot
        '''

        index = plotvars.index_number
        if plotvars.plot_type == 'Contour and vector':
            index = self.contour_indexComboBox.currentIndex()

        # Return if no field
        if index == -1:
            return

        # Make a copy of the field for plotting
        f = deepcopy(plotvars.fields[index])
        
               
        
        # Reset the file title in case the user has changed the field properties
        self.reset_field_title()

        # Check data and subspace if necessary
        subspace_args = {}
        con_args = {}
        
        # ajh - to be removed
        #dims = ['X', 'Y', 'Z', 'T']
        
        # ajh - new code 
        mystored = plotvars.stored['f' + str(index)]
              
        myaxes = deepcopy(mystored['data_axes'])
        
        for i in np.arange(len(myaxes)):
            if myaxes[i][:3] == 'dim':
                myaxes[i] = 'dimensioncoordinate' + myaxes[i][3:]
        

        if plotvars.plot_type == 'Contour' or plotvars.plot_type == 'Contour and vector':
            
            # Check selected data has enough items to be a contour plot
            axes = plotvars.contour_type.split('-')
            if len(axes) == 1 and axes[0] == '':
                print('Not enough data to make a contour plot')
                return
            
            if axes[0][:4] == 'log(':
                axes[0] = axes[0][4:-1]
            if axes[1][:4] == 'log(':
                axes[1] = axes[1][4:-1]
            axes_orig = deepcopy(axes)

                
            if axes[0][:3] == 'dim':
                axes[0] = 'dimensioncoordinate' + axes[0][3:]
            if axes[1][:3] == 'dim':
                axes[1] = 'dimensioncoordinate' + axes[1][3:]     
                
            
            # Work out the sizes of the data for the x and y directions
            x_ind = myaxes.index(axes[0])
            y_ind = myaxes.index(axes[1])
            nx = mystored['axis_sizes'][x_ind]
            ny = mystored['axis_sizes'][y_ind]
            if mystored['axes'][x_ind] != -1:
                nx = len(mystored['axes'][x_ind])
            if mystored['axes'][y_ind] != -1:
                ny = len(mystored['axes'][y_ind])
            if mystored['collapses'][x_ind] != 'Off':
                nx = 1
            if mystored['collapses'][y_ind] != 'Off':
                ny = 1                 
                 
            
            if nx < 2 or ny < 2:
                errstr = '\nContour error - not enough data to make requested '
                errstr += plotvars.contour_type + ' contour plot\n'
                errstr += 'Need a minimum of two values per contour plot dimension\n'
                errstr += 'Field dimensions are\n'
                errstr += axes[0] + ' = ' + str(nx) + '\n'
                errstr += axes[1] + ' = ' + str(ny) + '\n\n'
                print(errstr)
                return


            # Form the subspace dictionary
            subspace_args = Cf_funcs.subspace_args(self.parent, index)

                 
            # Form the contour dictionary
            if plotvars.fill and not plotvars.lines:
                con_args['lines'] = False
            if not plotvars.fill and plotvars.lines:
                con_args['fill'] = False
                con_args['lines'] = True


            if plotvars.blockfill and not plotvars.lines:
                con_args['blockfill'] = True
                con_args['lines'] = False

            if plotvars.blockfill and plotvars.lines:
                con_args['blockfill'] = True
                con_args['lines'] = True
                    
            if plotvars.blockfill_fast and not plotvars.lines:
                con_args['blockfill_fast'] = True
                con_args['lines'] = False

            if plotvars.blockfill_fast and plotvars.lines:
                con_args['blockfill_fast'] = True
                con_args['lines'] = True                   
                    
            if plotvars.lines and not plotvars.line_labels:
                con_args['line_labels'] = False
                
            if plotvars.nlevs:
                all_ints, errstr = Data_check.test_integers(plotvars.nlevs)
                if all_ints:
                    con_args['nlevs'] = int(plotvars.nlevs)
                else:
                    print('Error - number of levels defined in contour settings pop up window is not an integer')
                    return                  
                
            if plotvars.colorbar is False:
                con_args['colorbar'] = False
            else:
                if plotvars.colorbar_orientation == 'horizontal':
                    con_args['colorbar_orientation'] = 'horizontal'
                if plotvars.colorbar_orientation == 'vertical':
                    con_args['colorbar_orientation'] = 'vertical'

            if 'log' in plotvars.contour_type:
                con_args['ylog'] = True

            if plotvars.title == '':
                if plotvars.titles:
                    con_args['titles'] = True
                else:
                    con_args['titles'] = False
            else:
                con_args['title'] = plotvars.title
                
                

            if plotvars.contour_type[0] == 'T':
                con_args['swap_axes'] = True 

            # Swap the axes if required
            if x_ind > y_ind:
                con_args['swap_axes'] = True 



            # Set the map
            if plotvars.contour_type == 'X-Y':
                cfp.mapset(proj=plotvars.proj)



            # Get the subspace and collapse commands
            com_subspace, com_collapse = Cf_funcs.com_subspace_collapse(subspace_args)

            # generate the code to read the data and apply subspace and collapse args
            variable_names = ['f', 'g', 'h']         

            com_con = Cf_funcs.generate_code(com_subspace, com_collapse)

            

            # Add contour levels if specified
            if plotvars.levs_set:
                vals = [plotvars.levs_min, plotvars.levs_max, plotvars.levs_step]

                # Check all values are numbers
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in evenly spaced contour levels\n' + errstr)
                    return

                # Check min < max
                vals_okay, errstr = Data_check.test_val0_lt_val1([vals[0], vals[1]])
                if not vals_okay:
                    print('contour level error\n' + errstr)
                    return
    
                # Check step > 0
                vals_okay, errstr = Data_check.test_val_gt_zero(vals[2])
                if not vals_okay:
                    print('contour level step must be greater than zero\n')
                    return

                # Test for ints or floats
                all_ints, errstr = Data_check.test_integers(vals)
                
                # Set up levels and add to the com_con code
                if all_ints:
                    cfp.levs(min=int(vals[0]), max=int(vals[1]), step=int(vals[2]))
                else:
                    cfp.levs(min=float(vals[0]), max=float(vals[1]), step=float(vals[2]))
                com_con += 'cfp.levs(min=' + vals[0] + ', max=' + vals[1] + ', step=' + vals[2] + ')\n'

            elif plotvars.levs_manual_set:
                # Check inputs are numbers
                vals = plotvars.levs_manual.split(' ')
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in manual contour levels\n' + errstr)
                    return


                # Check inputs are ascending
                vals_okay, errstr = Data_check.test_ascending(vals)
                if not vals_okay:
                    print('Error in manual contour levels\n' + errstr)
                    return

                # Check if all values are integers
                myint, errstr = Data_check.test_integers(vals)


                # Set up levels as a integer or float array
                levels = []
                for j in np.arange(np.size(vals)):
                    if myint:
                        levels.append(int(vals[j]))
                    else:
                        levels.append(float(vals[j]))

                # Set up levels as a string array
                levels_str = '['
                for j in np.arange(np.size(vals)):
                    levels_str += vals[j]
                    if j < np.size(vals)-1:
                        levels_str += ', '
                levels_str += ']'

                # Convert array to integer or float as appropriate
                if myint:
                    levels = np.array(levels).astype(int)
                else:
                    levels = np.array(levels).astype(float)


                cfp.levs(manual=levels)
                com_con += 'cfp.levs(manual=' + levels_str + ')\n'


            elif plotvars.levs_automatic_set:
                cfp.levs()



            # Colour scale settings
            if plotvars.cscale_automatic_set is False:
            
                # Check any inputs are integers
                if plotvars.cscale_ncols != '':
                    vals_okay, errstr = Data_check.test_integers(plotvars.cscale_ncols)
                    if not vals_okay:
                        print('Error in colour scale numbers\n' + errstr)
                        return

                if plotvars.cscale_above != '':
                    vals_okay, errstr = Data_check.test_integers(plotvars.cscale_above)
                    if not vals_okay:
                        print('Error in colour scale numbers\n' + errstr)
                        return

                if plotvars.cscale_below != '':
                    vals_okay, errstr = Data_check.test_integers(plotvars.cscale_below)
                    if not vals_okay:
                        print('Error in colour scale numbers\n' + errstr)
                        return


                # Check white are numbers if specified
                errstr = 'Error in colour white values\n'
                white_vals = plotvars.cscale_white.split(' ')
                if plotvars.cscale_white != '' and len(white_vals) > 0:
                    vals_okay, errstr = Data_check.test_integers(white_vals)
                    if not vals_okay:
                        print('Error in colour scale white numbers\n' + errstr)
                        return

                    white_vals = [int(i) for i in white_vals]
                    if len(white_vals) == 1:
                        white_vals = white_vals[0]
    



            # Assemble the cscale command
            # Reset cscale
            cfp.cscale()

            cscale_args = {}
            if plotvars.cscale != 'viridis':
                cscale_args['scale'] = plotvars.cscale
            if plotvars.cscale_ncols != '':
                cscale_args['ncols'] = int(plotvars.cscale_ncols)
            if plotvars.cscale_above != '':
                cscale_args['above'] = int(plotvars.cscale_above)
            if plotvars.cscale_below != '':
                cscale_args['below'] = int(plotvars.cscale_below)
            if plotvars.cscale_white != '':
                cscale_args['white'] = white_vals
            if plotvars.cscale_reverse_set:
                cscale_args['reverse'] = True

            if len(cscale_args) > 0:
                cfp.cscale(**cscale_args)
                com_cscale = 'cfp.cscale('
                for key in cscale_args:
                    if key == 'scale':
                        com_cscale += "cscale='" + str(cscale_args[key]) + "',"
                    else: 
                        com_cscale += str(key) + '=' + str(cscale_args[key]) + ','
                com_cscale = com_cscale[:-1] + ')\n'
                com_con += com_cscale
            


            # Map settings
            # Reset map
            cfp.mapset()

            if plotvars.proj == 'cyl':
                # Check inputs are numbers
                vals = [plotvars.lonmin, plotvars.lonmax, plotvars.latmin, plotvars.latmax]
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in cylindrical projection limits\n' + errstr)
                    return

                # Check lonmin < lonmax
                vals = [plotvars.lonmin, plotvars.lonmax]
                vals_okay, errstr = Data_check.test_ascending(vals)
                if not vals_okay:
                    myerr = 'Error in cylindrical projection longitude limits\n'
                    myerr += 'lonmax must be > lonmin\n'
                    print(myerr)
                    return

                # Check latmin < latmax
                vals = [plotvars.latmin, plotvars.latmax]
                vals_okay, errstr = Data_check.test_ascending(vals)
                if not vals_okay:
                    myerr = 'Error in cylindrical projection latitude limits\n'
                    myerr += 'latmax must be > latmin\n'
                    print(myerr)
                    return


                if float(plotvars.lonmin) != -180.0 or float(plotvars.lonmax) != 180 or \
                   float(plotvars.latmin) != -90.0 or float(plotvars.latmax) != 90:
                    com_con += 'cfp.mapset(lonmin=' + str(plotvars.lonmin) + ', '
                    com_con += 'lonmax=' + str(plotvars.lonmax) + ', '
                    com_con += 'latmin=' + str(plotvars.latmin) + ', '
                    com_con += 'latmax=' + str(plotvars.latmax) + ')\n'
                    cfp.mapset(float(plotvars.lonmin), float(plotvars.lonmax), \
                               float(plotvars.latmin), float(plotvars.latmax))



            if plotvars.proj == 'spstere' or plotvars.proj == 'npstere':
                # Check inputs are numbers
                vals = [plotvars.boundinglat]
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in bounding latitude\n' + errstr)
                    return

                vals = [plotvars.lon_0]
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in longitude centre of map domain\n' + errstr)
                    return

                cfp.mapset(proj=plotvars.proj, boundinglat=float(plotvars.boundinglat),\
                           lon_0 = float(plotvars.lon_0))

                com_con += 'cfp.mapset(proj=' + plotvars.proj
                if str(plotvars.boundinglat) != '0':
                    com_con += ', boundinglat=' + str(plotvars.boundinglat)
                if str(plotvars.lon_0) != '0':
                    com_con += ', lon_0=' + plotvars.lon_0
                com_con += ')\n'


            if plotvars.proj in ['EuroPP', 'lcc', 'merc', 'ortho', 'OSGB', 'robin', 'UKCP']:
                cfp.mapset(proj=plotvars.proj)
                com_con += 'cfp.mapset(proj=' + plotvars.proj + ')\n'


            # Set continent properties if changed
            if plotvars.continent_thickness != '1.5' or plotvars.continent_color != 'black':
                cfp.setvars(continent_thickness=float(plotvars.continent_thickness),\
                            continent_color=plotvars.continent_color)
                continent_opts = 0
                com_con += 'cfp.setvars('
                if plotvars.continent_thickness != '1.5':
                    com_con += 'continent_thickness=' + plotvars.continent_thickness
                    continent_opts += 1
                if plotvars.continent_color != 'black':
                    if continent_opts != 0:
                        com_con += ', '
                    com_con += 'continent_color=' + plotvars.continent_color

                com_con += ')\n'
 


            com_con += 'cfp.con(' + 'f'

           
            if len(con_args) > 0:
                for arg in con_args:
                    if arg != 'title':
                        com_con += ', ' + arg + '=' + str(con_args[arg])
                    else:
                        com_con += ", '" + arg + '=' + str(con_args[arg]) + "'"
            com_con += ')'
    
            
            # Switch based on whether the data is axis data
            axis_switch = False
            if len(subspace_args) > 0:
                if {'ax0', 'ax1', 'ax2', 'ax3'} > set(subspace_args):
                    axis_switch = True
                
                
            if axis_switch is False:
            
                if len(subspace_args) > 0:
                    f = f.subspace(**subspace_args)

                # Add bounds for those axes that need them
                # ajh - new code
                for i in np.arange(len(myaxes)):
                    if mystored['collapses'] != 'Off':
                        coord = f.coord(myaxes[i])
                        if len(coord.array) > 1:
                            if coord.has_bounds() is False:
                                bounds = coord.create_bounds()
                                coord.set_bounds(bounds)                
                
                
                
                
                if com_collapse != '':
                
                    print('com_collapse is ', com_collapse)
                    f = f.collapse(com_collapse.strip("'"), weights=True)


                # Use cfp.gopen if a contour and vector plot
                if plotvars.plot_type == 'Contour and vector':
                    cfp.gopen()
                

                if plotvars.plot_type != 'Contour and vector':
                    if plotvars.code_output:
                        print('### Code to make the plot is ###\n', com_con)
                    

                cfp.con(f, **con_args) 

                
            if axis_switch:
                
                # Just in case the data units are messed up
                try:
                    data = f.array
                except:
                    f.del_property('units')
                    f.set_property('units', 'K')
                    data = f.array
                
                
                naxes = np.ndim(data)
                myslice = []
                for i in np.arange(naxes):
                    if 'ax' + str(i) in subspace_args:
                        vals = subspace_args['ax' +  str(i)]
                        if np.size(vals) == 1:
                            myslice.append(vals)
                        else:
                            myslice.append(slice(vals[0], vals[1] + 1))
                    else:
                        # A slice of None, None selects all the data
                        myslice.append(slice(None, None))

                data = data[tuple(myslice)]

                

                

                    
                    
                axes = f.get_data_axes()
                contour_dims = plotvars.contour_type.split('-')
                axes = ['domainaxis' + contour_dims[0][2], 'domainaxis' + contour_dims[1][2]]
                axis_values = ['', '']
                axis_names = ['', '']
                
                # Extract axis names if available in the other netCDF variables
                for iaxis in np.arange(2):
                    axis = axes[iaxis]
                    my_nc_name = f.domain_axis(axis).nc_get_dimension()

                    match = False
                    for ifield in np.arange(len(plotvars.fields)):
                        test_data = plotvars.fields[ifield]
                        if len(test_data.get_data_axes()) == 1:
                            if my_nc_name == test_data.domain_axis('domainaxis0').nc_get_dimension():
                                match = True
                                axis_names[iaxis] = Cf_funcs.field_name(test_data)

                                
                    if match is False:
                        axis_names[iaxis] = 'No axis name'
                        
                    ax_number = int(contour_dims[iaxis][2:])
                    lists = [self.xlist, self.ylist, self.zlist, self.tlist]
                    
                    min_ind = lists[ax_number].selectedIndexes()[0].row() 
                    max_ind = lists[ax_number].selectedIndexes()[-1].row()


                                        
                    items = []
                    for index in np.arange(lists[ax_number].count()):
                        item = Data_check.test_type(lists[ax_number].item(index).text())
                        items.append(item)

                    items = np.array(items)

                    axis_values[iaxis] = items[min_ind:max_ind + 1]

                         
                        
                         
                         
                x, xlabel = axis_values[1], axis_names[1]
                y, ylabel = axis_values[0], axis_names[0]
                
                if int(contour_dims[1][2:]) < int(contour_dims[0][2:]):
                    data = np.flipud(np.rot90(data))


                
                con_args['titles'] = False
                
                myfield = plotvars.fields[plotvars.index_number]
                colorbar_title = Cf_funcs.field_name(myfield)
                myunits = getattr(myfield, 'units', '()')
                if myunits != '()':
                    myunits = '(' + myunits + ')'
                con_args['colorbar_title'] = colorbar_title + ' ' + myunits
                
                warnstr = '### This data is very basic and has no dimensions\n'
                warnstr += '### The code to make the plot is not available for axis only data\n'
                print(warnstr)
                
                cfp.con(f=data, x=x, y=y, xlabel=xlabel, ylabel=ylabel, **con_args)
                         
                return            
            
                
              
    
        if plotvars.plot_type == 'Line':
            # Make a line plot


            # No line plots available for axis only data
            if 'ax0' in plotvars.dim_names:
                print('### Line plots are not available for axis only data')
                return



            # Check selected data has enough items to make a line plot
            mydim = plotvars.line_type
            if mydim[:2] == 'dim':
                mydim = 'dimensioncoordinate' + mydim[2:]
            
            
            # Return if coordinate doesn't exist
            if not f.has_construct(mydim):
                print('lineplot error - coordinate ' + mydim + ' does not exist\n')
                return
                
            if np.size(f.coord(mydim).array) < 2:
                errstr = '\nLine plot error - not enough data to make requested '
                errstr += mydim + ' line plot\n'
                errstr += 'Need a minimum of two values along this dimension\n'
                errstr += 'Field dimensions are\n'
                print('There are ', str(np.size(f.coord(mydim).array)) + 'points in this axis')
                print(errstr)
                return


            # Assemble the line arguments
            line_args = {}
            if plotvars.line_colour != 'C0':
                line_args['color'] =  plotvars.line_colour
            if plotvars.line_width != '1.0':
                line_args['linewidth'] =  plotvars.line_width
            if plotvars.line_style != 'solid':
                line_args['linestyle'] =  plotvars.line_style

            if plotvars.line_marker != '':
                line_args['marker'] =  plotvars.line_marker
            if plotvars.line_markersize != '':
                line_args['markersize'] =  plotvars.line_markersize
            if plotvars.line_markeredgecolor != '':
                line_args['markeredgecolor'] =  plotvars.line_markeredgecolor
            if plotvars.line_markeredgewidth != '':
                line_args['markeredgewidth'] =  plotvars.line_markeredgewidth

            if plotvars.line_title != '':
                line_args['title'] =  plotvars.line_title
            else:
                line_args['titles'] =  True


            # Form the subspace dictionary
            subspace_args = Cf_funcs.subspace_args(self.parent, index)

            # Get the subspace and collapse commands
            com_subspace, com_collapse = Cf_funcs.com_subspace_collapse(subspace_args)

            
            
            # Generate the code to read the data and apply subspace and collapse args
            com_line= Cf_funcs.generate_code(com_subspace, com_collapse)


            # Set data limits if requested
            cfp.gset()
            if plotvars.line_limits == 'user':
                # Check the input user data are numbers
                cfp.gset(float(plotvars.line_xmin), float(plotvars.line_xmax), \
                         float(plotvars.line_ymin), float(plotvars.line_ymax))

                com_line += 'cfp.gset(xmin=' + plotvars.line_xmin + ', '
                com_line += 'xmax=' + plotvars.line_xmax + ', '
                com_line += 'ymin=' + plotvars.line_ymin + ', '

                com_line += 'ymax=' + plotvars.line_ymax + ')\n'

              
            # Make the line plot            
            if len(subspace_args) > 0:
                f = f.subspace(**subspace_args)

                
            # Add bounds for those axes that need them
            for i in np.arange(len(myaxes)):
                if mystored['collapses'] != 'Off':
                    coord = f.coord(myaxes[i])
                    if len(coord.array) > 1:
                        if coord.has_bounds() is False:
                            bounds = coord.create_bounds()
                            coord.set_bounds(bounds)                          
                

            
            if com_collapse != '':
                f = f.collapse(com_collapse.strip("'"), weights=True)

            cfp.lineplot(f, **line_args)

            # Show the commands to make this line plot
            com_line += 'cfp.lineplot(' + 'f'

            if len(line_args) > 0:
                for arg in line_args:
                    if arg == 'thickness':
                        com_line += ', thickness =' + str(line_args[arg])
                    else:
                        if line_args[arg] not in [True, False]:
                            com_line += ', ' + arg + "='" + str(line_args[arg]) + "'"
                        else:
                            com_line += ', ' + arg + "=" + str(line_args[arg])


            com_line += ')'

            if plotvars.code_output:
                print('### Code to make plot is ###\n', com_line)
            

        if plotvars.plot_type == 'Vector' or plotvars.plot_type =='Contour and vector':


            # Reject plot if other than x-y at present
            if plotvars.contour_type != 'X-Y':
                print('Only longitude-latitude vector plots are supported at present')
                return

            index_x = self.vector_x_indexComboBox.currentIndex()
            index_y = self.vector_y_indexComboBox.currentIndex()
            
            # Check dimensions have X and Y 
            if 'X' not in myaxes or 'Y' not in myaxes:
                print("Error - need both 'X' and 'Y' dimensions to make a longitude-latitude vector plot\n")
                return
                      

            # Check selected data has enough items to be a vector plot
            # First the x component
            myx = 'X'
            myy = 'Y'
            myindicies = [index_x, index_y]
            for i_index in np.arange(len(myindicies)): 
                mystored = plotvars.stored['f' + str(myindicies[i_index])]
                mysizes = mystored['axis_sizes']
                myaxes = mystored['data_axes']
                if mysizes[myaxes.index(myx)] <= 1 or mysizes[myaxes.index(myy)] <= 1:
                    errstr = '\nVector error - not enough data to make requested '
                    errstr += plotvars.contour_type + ' vector plot\n'
                    errstr += 'Need a minimum of two values per vector plot dimension\n'
                    errstr += 'Field dimensions are\n'
                    for i in np.arange(len(myaxes)):
                        errstr += myaxes[i] + ' = ' + str(mysizes[i]) + '\n'
                    errstr += '\n'
                    print(errstr)
                    return







            # Form the subspace dictionaries and apply if necessary
            u_subspace_args = Cf_funcs.subspace_args(self.parent, index_x)
            v_subspace_args = Cf_funcs.subspace_args(self.parent, index_y)

            u = deepcopy(plotvars.fields[index_x])
            if len(u_subspace_args) > 0:
                u = u.subspace(**u_subspace_args)

            v = deepcopy(plotvars.fields[index_y])
            if len(v_subspace_args) > 0:
                v = v.subspace(**v_subspace_args)

            # Set the map
            if plotvars.contour_type == 'x-y':
                cfp.mapset(proj=plotvars.proj)



            # Assemble vector arguments
            vect_args = {}
                            
            if plotvars.vector_title == '':
                vect_args['titles'] = True
            else:
                vect_args['title'] = plotvars.vector_title

            vals_okay, errstr = Data_check.test_numbers([plotvars.vector_key_length])
            if not vals_okay:
                print('Error in vector key length\n' + errstr)
                return
            vect_args['key_length'] = Data_check.test_type(plotvars.vector_key_length)

            vals_okay, errstr = Data_check.test_numbers([plotvars.vector_scale])
            if not vals_okay:
                print('Error in vector scale\n' + errstr)
                return
            vect_args['scale'] = int(float(plotvars.vector_scale))

            if plotvars.vector_stride != '' and plotvars.vector_pts == '':
                vals_okay, errstr = Data_check.test_numbers([plotvars.vector_stride])
                if not vals_okay:
                    print('Error in vector stride\n' + errstr)
                    return
                vect_args['stride'] = int(float(plotvars.vector_stride))

            if plotvars.vector_pts != '':
                vals_okay, errstr = Data_check.test_numbers([plotvars.vector_pts])
                if not vals_okay:
                    print('Error in vector interpolation pts\n' + errstr)
                    return
                vect_args['pts'] = int(float(plotvars.vector_pts))
         
         
            com = ''
            if plotvars.plot_type =='Contour and vector':
                com += com_con + '\n\n'
                
                
                
            # Generate code to make the plot
           
            # Get the subspace and collapse commands
            ucom_subspace, ucom_collapse = Cf_funcs.com_subspace_collapse(u_subspace_args)
            vcom_subspace, vcom_collapse = Cf_funcs.com_subspace_collapse(v_subspace_args)

            # Generate the code to read the data and apply subspace and collapse args
            if plotvars.plot_type == 'Vector':
                com_u = Cf_funcs.generate_code(ucom_subspace, ucom_collapse, index=index_x, var_index=0)
                com_v = Cf_funcs.generate_code(vcom_subspace, vcom_collapse, index=index_y, var_index=1)
            else:
                com_u = Cf_funcs.generate_code(ucom_subspace, ucom_collapse, index=index_x, var_index=1)
                com_v = Cf_funcs.generate_code(vcom_subspace, vcom_collapse, index=index_y, var_index=2)
                
            
            com += com_u + com_v

            # Assemble the cfp.vect command
            if plotvars.plot_type == 'Vector':
                vect_com = 'cfp.vect(u=f, v=g'
            else:
                vect_com = 'cfp.vect(u=g, v=h'

            for arg in vect_args:
                if arg != 'title':
                    vect_com += ', ' + arg + "=" + str(vect_args[arg])
                else:
                    vect_com += ', ' + arg + "='" + str(vect_args[arg]) + "'"
            vect_com += ')'      
            
            
            com += vect_com
                      
            if plotvars.plot_type == 'Contour and vector':
                com += '\ncfp.gclose()'

            if plotvars.code_output:
                print('### Code to make the plot is ###\n', com)




            cfp.vect(u=u, v=v, **vect_args)


            if plotvars.plot_type == 'Contour and vector':
                cfp.gclose()

        if plotvars.plot_type == 'Trajectory':

            traj_args = {}
            com_traj = 'import cf\n'
            com_traj += 'import cfplot as cfp\n'

            index = plotvars.index_number
            com_traj += "f = cf.read(" + plotvars.file_read_str + ")[" + str(index) +']\n\n'

            # Check if a date range has been specified
            if plotvars.trajectory_date_min != ''  or plotvars.trajectory_date_max != '':
                ref_time = f.construct('T').units
                if hasattr(f.construct('T'), 'calendar'):
                    ref_calendar = f.construct('T').calendar
                else:
                    ref_calendar = 'standard'
                time_units = cf.Units(ref_time, ref_calendar)

                try:
                    tmin = cf.Data(cf.dt(plotvars.trajectory_date_min), units=time_units)
                except:
                    print('Error in trajectory minimum time')
                    return

                try:
                    tmax = cf.Data(cf.dt(plotvars.trajectory_date_max), units=time_units)
                except:
                    print('Error in trajectory maximum time')
                    return
                
                print('times are ', type(tmin), tmin, tmin.array)

                if tmax.array <= tmin.array:
                    print('Error - Trajectory maximum time must be grater than trajectory minimum time')
                    return


                f = f.subspace(T=cf.wi(tmin.dtarray, tmax.dtarray))
                com_traj += 'f = f.subspace(T=cf.wi(' + str(tmin.dtarray) + ', '
                com_traj += str(tmax.dtarray) + ')\n'





            # Map settings
            # Reset map
            cfp.mapset()

            if plotvars.proj == 'cyl':
                # Check inputs are numbers
                vals = [plotvars.lonmin, plotvars.lonmax, plotvars.latmin, plotvars.latmax]
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in cylindrical projection limits\n' + errstr)
                    return

                # Check lonmin < lonmax
                vals = [plotvars.lonmin, plotvars.lonmax]
                vals_okay, errstr = Data_check.test_ascending(vals)
                if not vals_okay:
                    myerr = 'Error in cylindrical projection longitude limits\n'
                    myerr += 'lonmax must be > lonmin\n'
                    print(myerr)
                    return

                # Check latmin < latmax
                vals = [plotvars.latmin, plotvars.latmax]
                vals_okay, errstr = Data_check.test_ascending(vals)
                if not vals_okay:
                    myerr = 'Error in cylindrical projection latitude limits\n'
                    myerr += 'latmax must be > latmin\n'
                    print(myerr)
                    return


                if float(plotvars.lonmin) != -180.0 or float(plotvars.lonmax) != 180 or \
                   float(plotvars.latmin) != -90.0 or float(plotvars.latmax) != 90:
                    com_traj += 'cfp.mapset(lonmin=' + str(plotvars.lonmin) + ', '
                    com_traj += 'lonmax=' + str(plotvars.lonmax) + ', '
                    com_traj += 'latmin=' + str(plotvars.latmin) + ', '
                    com_traj += 'latmax=' + str(plotvars.latmax) + ')\n'
                    cfp.mapset(float(plotvars.lonmin), float(plotvars.lonmax), \
                               float(plotvars.latmin), float(plotvars.latmax))



            if plotvars.proj == 'npstere' or plotvars.proj == 'spstere':
                # Check inputs are numbers
                vals = [plotvars.boundinglat]
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in bounding latitude\n' + errstr)
                    return

                vals = [plotvars.lon_0]
                vals_okay, errstr = Data_check.test_numbers(vals)
                if not vals_okay:
                    print('Error in longitude centre of map domain\n' + errstr)
                    return


                cfp.mapset(proj=plotvars.proj, boundinglat=float(plotvars.boundinglat),\
                           lon_0 = float(plotvars.lon_0))

                com_traj += 'cfp.mapset(proj=' + plotvars.proj
                if str(plotvars.boundinglat) != '0':
                    com_traj += ', boundinglat=' + str(plotvars.boundinglat)
                if str(plotvars.lon_0) != '0':
                    com_traj += ', lon_0=' + plotvars.lon_0
                com_traj += ')\n'
                


            if plotvars.trajectory_legend:
                if plotvars.levs_set:

                    vals = [plotvars.levs_min, plotvars.levs_max, plotvars.levs_step]

                    print('vals are', vals)
                    # Check all values are numbers
                    vals_okay, errstr = Data_check.test_numbers(vals)
                    if not vals_okay:
                        print('Error in evenly spaced contour levels\n' + errstr)
                        return

                    # Check min < max
                    vals_okay, errstr = Data_check.test_val0_lt_val1([vals[0], vals[1]])
                    if not vals_okay:
                        print('contour level error\n' + errstr)
                        return
    
                    # Check step > 0
                    vals_okay, errstr = Data_check.test_val_gt_zero(vals[2])
                    if not vals_okay:
                        print('contour level step must be greater than zero\n')
                        return

                    # Test for ints or floats
                    all_ints, errstr = Data_check.test_integers(vals)

                    # Set up levels and add to the com_con code
                    if all_ints:
                        cfp.levs(min=int(vals[0]), max=int(vals[1]), step=int(vals[2]))
                    else:
                        cfp.levs(min=float(vals[0]), max=float(vals[1]), step=float(vals[2]))
                    com_traj += 'cfp.levs(min=' + vals[0] + ', max=' + vals[1] + ', step=' + vals[2] + ')\n'

                else:
                    print('Error - no levels set for the legend')
                    print('Enter min, max and step in the contour levels set-up menu\n')
                    return

            # Assemble trajectory dictionary
            if plotvars.trajectory_title != '':
                traj_args['title'] = plotvars.trajectory_title
            if plotvars.trajectory_marker_shape != 'o':
                traj_args['marker'] = plotvars.trajectory_marker_shape
            if plotvars.trajectory_marker_size != '5':
                traj_args['markersize'] = Data_check.test_type(plotvars.trajectory_marker_size)
            if plotvars.trajectory_marker_face_colour != 'red':
                traj_args['markerfacecolor'] = plotvars.trajectory_marker_face_colour
            if plotvars.trajectory_marker_edge_colour != 'green':
                traj_args['markeredgecolor'] = plotvars.trajectory_marker_edge_colour
            if plotvars.trajectory_line_style != 'solid':
                traj_args['linestyle'] = plotvars.trajectory_line_style
            if plotvars.trajectory_line_colour != 'blue':
                traj_args['linecolor'] = plotvars.trajectory_line_colour
            if plotvars.trajectory_line_width != '1.0':
                traj_args['linewidth'] = Data_check.test_type(plotvars.trajectory_line_width)
            if plotvars.trajectory_vector:
                traj_args['vector'] = True
            if plotvars.trajectory_legend:
                traj_args['legend'] = True


            # Calculate selected tracks
            selected_tracks = self.lists[i].selectedItems()
            tracks = []
            tracks_all = True
            for j in np.arange(len(selected_tracks)):
                tracks.append(int(selected_tracks[j].text()))

            if len(tracks) < self.lists[i].count():
                tracks_all = False
                tracks = sorted(tracks)
                tracks_contig = True
                if tracks[0] + len(tracks) -1 != tracks[-1]:
                    tracks_contig = False


            # Assemble code to make plot
            com_traj += 'cfp.traj(f'
            if tracks_all is False:
                if tracks_contig:
                    if len(tracks) == 1:
                        com_traj += '[' + str(tracks[0]) + ', :]'
                    else:
                        com_traj += '['+ str(tracks[0]) + ':' + str(tracks[-1] + 1) + ', :]'
                else:
                    track_subspace_str = '[['
                    for track in tracks:
                        track_subspace_str += str(track) + ','
                    track_subspace_str = track_subspace_str[0:-1] + '],:]'
                    com_traj += track_subspace_str


            for arg in traj_args:
                if arg != 'title':
                    com_traj += ', ' + arg + "=" + str(traj_args[arg])
                else:
                    com_traj += ', ' + arg + "='" + str(traj_args[arg]) + "'"
            com_traj += ')'

            if tracks_all:
                cfp.traj(f, **traj_args)
            else:
                cfp.traj(f[tracks, :], **traj_args)

            if plotvars.code_output:
                print('*** Code to make plot is ***\n', com_traj)




    def fieldnumber(self):
        '''
         Change the field number 
        '''

        # Find out which fields are selected.
        # If multiple fields then select the first.
        selected = self.fieldlist.selectedItems()
        if len(selected) == 0:
            return

        s = int(selected[0].text().split(' ')[0])

        plotvars.index_number = s
        f = plotvars.fields[s]
        stored_vals = plotvars.stored['f' + str(plotvars.index_number)]
        myaxes = stored_vals['data_axes']

        # Lock changes to stored variables while the new lists are made
        # This is used to stop recursive calls 
        plotvars.stored_lock = True


        # ajh - new code
        # Clear the dimension and collapse lists
        for i in np.arange(plotvars.max_axes):
            self.lists[i].clear()
            self.lists_collapse[i].clear()        
        
        # Set the axis lists based on the field dimensions
        axis_vals = Cf_funcs.field_axis_values(field=f)
        
        
        for i in np.arange(len(myaxes)):
            # Axis lists
            str_list = list((str(w) for w in axis_vals[i]))

            self.lists[i].addItems(str_list)
            
            # Set the sected values to all or previously selected indicies
            indicies = stored_vals['axes'][i]
            
            if indicies == -1:
                self.lists[i].selectAll()
            else:
                for index in indicies:
                    self.lists[i].item(indicies[index]).setSelected(True)
        
            # Collapse lists
            if stored_vals['data_type'] != 'trajectory':
                if stored_vals['collapses'][i] != 'Off':
                    self.lists_collapse[i].addItem(stored_vals['collapse_value'])
                    self.lists_collapse[i].selectAll()
                

        # Restore access to changing stored variables
        plotvars.stored_lock = False


        # Reset the field titles
        self.reset_field_title()
        
        # Reset field names if names_window is open
        if self.names_window is not None:
            self.names_window.reset()
            
        # Reset dimension collapse method names if transform_window is open
        if self.transform_window is not None:
            self.transform_window.Populate_collapses()
                      
                
        # Reset table data if data window is open
        if self.data_window is not None:
            Populate_table(self.data_window, initial = False)
            
            
        # Reset the Contour plotting dimensions
        contour_index = self.contour_type_ComboBox.currentIndex()
        if contour_index < 0:
            contour_index = 0
        
        self.contour_type_ComboBox.clear()                  
        c_options = []
        
        # New code
        if len(myaxes) > 1:
            for iax in np.arange(len(myaxes)):
                for jax in np.arange(len(myaxes)):
                    if myaxes[iax] != myaxes[jax] and myaxes[iax] != 'Z':
                        if myaxes[iax] == 'Y' and myaxes[jax] == 'X':
                            pass
                        else:
                            c_options.append(myaxes[iax] + '-' + myaxes[jax])
                            if myaxes[iax] in ['X', 'Y', 'T'] and myaxes[jax] == 'Z':
                                c_options.append(myaxes[iax] + '-log(' + myaxes[jax] + ')')
                
        self.contour_type_ComboBox.addItems(c_options)
        if len(c_options) > 0 and contour_index != -1:
            if len(c_options) - 1 > contour_index:
                self.contour_type_ComboBox.setCurrentIndex(contour_index)
            else:
                self.contour_type_ComboBox.setCurrentIndex(0)            
        plotvars.contour_type = str(self.contour_type_ComboBox.currentText())
            
        # Set the dimension titles text to Axis view if axis data
        self.reset_dimensionButton.setText('Reset Dimensions')
        if myaxes[0][:2] == 'ax':
            self.dimension_titles.setText('Axis view')
            self.reset_dimensionButton.setText('Reset Axes')

        # Reset the Line plotting dimensions
        line_index = self.line_type_ComboBox.currentIndex()
        if line_index < 0:
            line_index = 0
        self.line_type_ComboBox.clear()

        l_options = []
        for axis in myaxes:
            l_options.append(axis)
        self.line_type_ComboBox.addItems(l_options)
        self.line_type_ComboBox.setCurrentIndex(line_index)
        plotvars.line_type = str(self.line_type_ComboBox.currentText())


        # If the data_window is open reset the data type dropdown 
        if self.data_window:

            # Reset the data
            Populate_table(self.data_window, initial = False)
            
            self.data_window.data_ComboBox.clear()
            
            options = []                                            
            for i in np.arange(len(myaxes)):     
                for j in np.arange(len(myaxes)):    
                    xtype = myaxes[i]
                    if xtype != 'Z':
                        if len(myaxes) > 1:
                            ytype = myaxes[j]                  
                    
                            if xtype != ytype:
                                if xtype == 'Y' and ytype == 'X':
                                    pass
                                else:
                                    options.append(xtype + '-' + ytype)
                
                        else:
                            options.append(xtype)                        

            self.data_window.data_ComboBox.addItems(options)
        

                    
        # Change the text and visibility of the axis RadioButtons
        for i in np.arange(plotvars.max_axes):
            self.axis_RadioButtons[i].setVisible(False)       
            self.axis_RadioButtons[i].setChecked(False)
            self.lists[i].setVisible(False)            
            
        for i in np.arange(len(myaxes)):
            self.axis_RadioButtons[i].setText(myaxes[i])
            self.axis_RadioButtons[i].setVisible(True)    

          
        # Set the visible axis button and list to be the first
        self.axis_RadioButtons[0].setChecked(True)        
        self.lists[0].setVisible(True)
                    
        # Add the reset dimension button
        getattr(self, 'rightHbox3').addWidget(getattr(self, 'reset_dimensionButton'))


    def dim_traj_reset(self, option):
        '''
         Reset the dimension or trajectory selection to all
        '''
        
        if plotvars.fields is None:
            return
        if len(plotvars.fields) == 0:
            return
            
        f = deepcopy(plotvars.fields[plotvars.index_number])
        
        
        # ajh - new code
        index_str = 'f' + str(plotvars.index_number)
        
        axis_vals = Cf_funcs.field_axis_values(field=f)
        
        
        mystored = plotvars.stored[index_str]
        myaxes = mystored['data_axes']
        
        
        # Set all lists to be invisible
        for i in np.arange(plotvars.max_axes):
            self.lists[i].setVisible(False)                
            self.lists_collapse[i].setVisible(False)          
        
        # Select all data in the lists
        for i in np.arange(len(myaxes)):
            self.lists[i].selectAll()
            plotvars.stored[index_str]['axes'][i] = -1

                                   
        # Find which list needs to be displayed based on the selected axis
        for i in np.arange(len(myaxes)):
            if self.axis_RadioButtons[i].isChecked():
                iaxis = i
                      
        
        # Reset any collapses to a value based on all the coordinate data
        for myindex in np.arange(len(myaxes)):
            if mystored['collapses'][myindex] != 'Off':
                indicies = [i.row() for i in self.parent.lists[myindex].selectedIndexes()]
                mymin = min(indicies)
                mymax = max(indicies)


                # Code for bounds calculation
                myaxis = myaxes[myindex]
                if myaxis[:3] == 'dim':
                    myaxis = 'dimensioncoordinate' + myaxis[3:]               
                
                coord = f.coord(myaxis)
                if coord.has_bounds() is False:
                    bounds = coord.create_bounds()
                    coord.set_bounds(bounds)

                if not coord.T:
                    # Code for bounds calculation
                    mybounds = coord.bounds.array[mymin:mymax+1, :]
                    val = str((np.max(mybounds) - np.min(mybounds))/2.0 + np.min(mybounds))
                else:
                    ref_time = coord.units
                    ref_calendar = coord.calendar
                    time_units = cf.Units(ref_time, ref_calendar)

                    # Code for bounds calculation
                    mybounds = coord.bounds.array[mymin:mymax+1, :]
                    midpt = (np.max(mybounds) - np.min(mybounds)) / 2.0 + np.min(mybounds)
                    val = str(cf.cftime.num2date(midpt, ref_time, ref_calendar))



                self.lists_collapse[myindex].clear()

                QListWidgetItem(str(val), self.lists_collapse[myindex]) 
                self.lists_collapse[myindex].selectAll()        
            
        if mystored['collapses'][iaxis] == 'Off':
            self.lists[iaxis].setVisible(True)     
        else:    
            self.lists_collapse[iaxis].setVisible(True)             

        
        # Reset the field title
        self.reset_field_title()       



    def contour_type(self, text):
        '''
         Set the contour plot type in plotvars
        '''

        plotvars.contour_type = text
        


    def line_type(self, text):
        '''
         Set the line plot type in plotvars
        '''

        plotvars.line_type = text



    def plot_type(self, text):
        '''
         Set the contour plot type in plotvars
        '''
        plotvars.plot_type = text

        objs = [self.contour_indexLabel, self.contour_indexComboBox,
        self.vector_x_indexLabel, self.vector_x_indexComboBox,
        self.vector_y_indexLabel, self.vector_y_indexComboBox]

        # Clear contour and vector comboboxes and repopulate
        self.contour_indexComboBox.clear()
        self.vector_x_indexComboBox.clear()
        self.vector_y_indexComboBox.clear()

        for i in np.arange(len(plotvars.fields)):
            self.contour_indexComboBox.addItem(str(i))
            self.vector_x_indexComboBox.addItem(str(i))
            self.vector_y_indexComboBox.addItem(str(i))

        if text == 'Contour':
            self.contour_type_ComboBox.setVisible(True)
            self.line_type_ComboBox.setVisible(False)
            for obj in objs:
                obj.setVisible(False)

        if text == 'Vector':
            self.contour_type_ComboBox.setVisible(True)
            self.line_type_ComboBox.setVisible(False)

            # Set vector indexes to be visible
            for obj in objs:
                obj.setVisible(True)
            objs[0].setVisible(False)
            objs[1].setVisible(False)




        if text == 'Contour and vector':
            self.contour_type_ComboBox.setVisible(True)
            self.line_type_ComboBox.setVisible(False)
            for obj in objs:
                obj.setVisible(True)

        if text == 'Line':
            self.contour_type_ComboBox.setVisible(False)
            self.line_type_ComboBox.setVisible(True)
            for obj in objs:
                obj.setVisible(False)


 


    def dim_view(self, iaxis):

        '''    
         Set the visible state of the dimension lists and lists_collapse 
        '''
                      
        # Turn RadioButton and list visibilty off
        for i in np.arange(plotvars.max_axes):
            self.axis_RadioButtons[i].setChecked(False)
            self.lists[i].setVisible(False)
            self.lists_collapse[i].setVisible(False)
            
        # Turn on RadioButton and specified list
        self.axis_RadioButtons[iaxis].setChecked(True)
        #if self.collapse_ComboBoxes[iaxis].currentText() == 'Off':
        if plotvars.stored['f' + str(plotvars.index_number)]['collapses'][iaxis] == 'Off':
            self.lists[iaxis].setVisible(True)
        else:
            self.lists_collapse[iaxis].setVisible(True)



    def field_widget_listing(self, listing):
        '''
         Change field widget listing based on order or name
        '''



        plotvars.field_widget_listing = listing

        listings = ['index', 'field']
        ticked_state = [False, False]
        loc = listings.index(listing)
        ticked_state[loc] = True

        self.indexCheckBox.setChecked(ticked_state[0])
        self.fieldCheckBox.setChecked(ticked_state[1])
        self.fieldlist.clear()
        
        field_widget_names, field_widget_title = Cf_funcs.fields_list(self, order=plotvars.field_widget_listing,
                                                order_changed=True)

        for i in np.arange(len(field_widget_names)):
            QListWidgetItem(field_widget_names[i], self.fieldlist) 
        self.fieldlist.setCurrentRow(0)
        self.field_titles.setText(field_widget_title)


    def search_fields(self, text):
        '''
         change field list based on search
        '''
        field_widget_names, field_widget_title = Cf_funcs.fields_list(self, order=plotvars.field_widget_listing,
                                                 order_changed=True, search=text)

        self.fieldlist.clear()
        for i in np.arange(len(field_widget_names)):
            QListWidgetItem(field_widget_names[i], self.fieldlist) 
        self.fieldlist.setCurrentRow(0)
        self.field_titles.setText(field_widget_title)



    def output_terminal_written(self, text):
        '''
         Output any text to the cfview output text panel
        '''

        self.output_terminal_textEdit.append(text)


    
    
             



    def reset_field_title(self):
        '''
         Reset the field title
        '''

        # Return if no fieldlist or if plotvars.fields is None
        if not hasattr(self, 'fieldlist') or plotvars.fields is None:
            return


        # Check if this field is a trajectory
        traj = False
        if plotvars.stored['f' + str(plotvars.index_number)]['data_type'] == 'trajectory':
            traj = True
        
        f = deepcopy(plotvars.fields[plotvars.index_number])
        
        

        # Reset the dimension sizes in the field string 
        selected = self.fieldlist.selectedItems()
        if len(selected) > 1:
            return
               

        selected = self.fieldlist.selectedItems()
        axis_lengths = plotvars.maximum_axis_lengths
        if len(selected) == 1:
            index_str = selected[0].text()[0:6]
            index_int = int(index_str)
            new_title = index_str     
               
            mystored = plotvars.stored['f' + str(index_int)]

            
            # Work out the new title
            for i in np.arange(len(plotvars.maximum_axis_lengths)):
                
                axis_str  = (plotvars.maximum_axis_lengths[i] + 1) * ' '
                myaxes = mystored['data_axes']
                if i + 1 <= len(myaxes):

                    mysize = len(self.lists[i].selectedItems())

                    if traj is False:
                        if mystored['collapses'][i] != 'Off':
                            mysize = 1


                    axis_str = str(mysize) + (plotvars.maximum_axis_lengths[i] + 1 - len(str(mysize))) * ' '

                new_title += axis_str



        # Store selected indicies
        for i in np.arange(len(mystored['data_axes'])):
            vals = [v.row() for v in self.lists[i].selectedIndexes()]
            if plotvars.stored_lock is False:
                if len(self.lists[i]) == len(vals) or len(vals) == 0:
                    plotvars.stored['f' + str(index_int)]['axes'][i] = -1
                else:
                    plotvars.stored['f' + str(index_int)]['axes'][i] = vals
        



            # Find the field widget titles
            field_widget_names, field_widget_title = Cf_funcs.fields_list(self, fields=[f], \
                                                     order=plotvars.field_widget_listing, call='reset_field_title')

            # Make a different title if this field is a trajectory
            if traj:
                new_title += '  '
            
            
            
            field_widget_title = 'index '
            myaxes = plotvars.stored['f' + str(plotvars.index_number)]['data_axes']
            for i in np.arange(len(plotvars.maximum_axis_lengths)):
                title_axis_label = (plotvars.maximum_axis_lengths[i] + 1) * ' '
                if i + 1 <= len(myaxes):
                    title_axis_label = 'n' + myaxes[i] + (plotvars.maximum_axis_lengths[i] - len(myaxes[i])) * ' '
                field_widget_title += title_axis_label
            if traj is False:
                field_widget_title += 'field name'
            else:
                field_widget_title += ' field name'


        new_title += mystored['field_name']

        self.field_titles.setText(field_widget_title)
        
        
        # Set tooltip for field_titles if a dim is in the field_widget_title
        tip = ''

        traj = False
        if plotvars.stored['f' + str(plotvars.index_number)]['data_type'] == 'trajectory':
            traj = True
            

        if not traj:
            mydims = field_widget_title.split(' ')
            dims = Cf_funcs.find_dim_names(f)

            dcoords = list(f.coords())
            daxes = list(f.get_data_axes())
            
            # Convert f.get_data_axes() 'domainaxis1' output to 'dimensioncoordinate1'
            for iaxis in np.arange(len(daxes)):
                for idim in np.arange(len(dcoords)):
                    if daxes[iaxis] == f.get_data_axes(dcoords[idim])[0]:
                        daxes[iaxis] = dcoords[idim]

            # Reverse coordinates so that they are of the form X, Y, Z, T
            daxes.reverse()
        
            if len(dims) == len(daxes):
                dim_names = []
                for idim in np.arange(len(daxes)):
                    dim_names.append(f.coord(dims[idim]).identity())
                    if dims[idim][:3] == 'dim':
                        dims[idim] = 'dim' + dims[idim][19:]               
                    if daxes[idim][:3] == 'dim':
                        daxes[idim] = 'dim' + daxes[idim][19:]          
        
                # Assemble tip information
                for idim in np.arange(len(dims)):
                    tip += dims[idim] + ' : ' + daxes[idim] + ' : ' + dim_names[idim]
                    if idim + 1 < len(dims):
                        tip += '\n'
                
        
        # Set field tip information
        self.field_titles.setToolTip(tip) 
        
        selected[0].setText(new_title)


        # Set lists and lists_collapse visibility based on the selected axis radio button and collapse status
        for i in np.arange(plotvars.max_axes):
             self.lists[i].setVisible(False)
             self.lists_collapse[i].setVisible(False)       

        iaxis = 0
        for i in np.arange(len(myaxes)):
            if self.axis_RadioButtons[i].isChecked():
                iaxis = i
                
        
        if traj is False:
            if plotvars.stored['f' + str(index_int)]['collapses'][iaxis] == 'Off':
                self.lists[iaxis].setVisible(True)
            else:
                self.lists_collapse[iaxis].setVisible(True)
                items = []
                for index in np.arange(self.lists_collapse[iaxis].count()):
                    items.append(self.lists_collapse[iaxis].item(index))




    def menu(self, value):
        '''Pop up a window based on which defaults are required'''
        

        if value.text() == 'cfview defaults':
            if self.cfview_defaults is None:
                self.cfview_defaults = Cfview_defaults_window(self.parent)
            self.cfview_defaults.show()


        if value.text() == 'Copy field':

            selected = self.fieldlist.selectedItems()

            if len(selected) == 0:
                print('\nNo fields selected for copy\n')
                return
                
            if len(selected) > 1:
                print("\nOnly copying the first selected field\n")

            # Only copy the first selected field
            selected = selected[0]
            index = plotvars.index_number
            next = self.fieldlist.count()
            
            new_name = selected.text()
            QListWidgetItem(deepcopy(new_name), self.fieldlist)
            plotvars.stored['f' + str(next)] = deepcopy(plotvars.stored['f' + str(index)])
            plotvars.fields.append(deepcopy(plotvars.fields[index]))

            # Set the new index number in the field title
            new_title = str(next) + (6 - len(str(next))) * ' ' + selected.text()[6:]

            self.fieldlist.item(next).setText(new_title)      
            plotvars.field_widget_names.append(new_title)
            

            # Set a new line in plotvars.data_source_list
            plotvars.stored['f' + str(next)]['source'] = 'Copied'
            plotvars.next_field_index += 1


        if value.text() == 'Delete selected fields':
        
            listItems=self.fieldlist.selectedItems()
            indicies = [i.row() for i in self.fieldlist.selectedIndexes()]
            
            # Call menu agin with "Delete all fields" if all fields selected
            if len(indicies) == len(self.fieldlist):
                self.menu(QAction('Delete all fields'))
                return
            
            if not listItems: 
                return        
            for i in np.arange(len(listItems)):
                self.fieldlist.takeItem(self.fieldlist.row(listItems[i]))

                plotvars.fields[indicies[i]].standard_name = 'deleted'

            if len(self.fieldlist) > 0:
                self.fieldlist.setCurrentRow(0)


        if value.text() == 'Delete all fields':
            self.fieldlist.clear()
            plotvars.file_selected = None
            plotvars.index_number = -1

            self.search_fieldsTextbox.clear()
            plotvars.fields = None
            plotvars.field_widget_indicies = None

            plotvars.stored = {}
            for i in np.arange(plotvars.max_axes):
                self.lists[i].clear()
                self.lists_collapse[i].clear()                
                

        if value.text() == 'contour levels':
            if self.contour_levels is None:
                self.contour_levels = Contour_levels_window(self.parent)
            self.contour_levels.show()


        if value.text() == 'colour scale':
            if self.colour_scale is None:
                self.colour_scale = Colour_scale_window(self.parent)
            self.colour_scale.show()


        if value.text() == 'map':
            if self.map_window is None:
                self.map_window = Map_window(self.parent)
            self.map_window.show()

        if value.text() == 'contours':
            if self.contour_window is None:
                self.contour_window = Contour_window(self.parent)
            self.contour_window.show()

        if value.text() == 'lines':
            if self.line_window is None:
                self.line_window = Line_window(self.parent)
            self.line_window.show()

        if value.text() == 'vectors':
            if self.vector_window is None:
                self.vector_window = Vector_window(self.parent)
            self.vector_window.show()

        if value.text() == 'trajectories':
            if self.trajectory_window is None:
                self.trajectory_window = Trajectory_window(self.parent)
            self.trajectory_window.show()



        if value.text() == 'data':
            if self.data_window is None:
                self.data_window = Data_window(self.parent)
            self.data_window.show()

        if value.text() == 'properties':
            if self.names_window is None:
                self.names_window = Names_window(self.parent)
            self.names_window.show()

        if value.text() == 'transform':
            if self.transform_window is None:
                self.transform_window = Transform_window(self.parent)
            self.transform_window.show()

        if value.text() == 'about':
            html = '<body><h2>About cfview</h2>'
            html += '<b>This is version 2.9.0</b><p>'
            html += 'cfview is a quick look data exploration tool for netCDF, Met Office PP and fields file data'
            html += '<p>'
            html += 'cfview uses:<br>'
            html += '<b>cf-python</b> - data I/O and manipulation<br>'
            html += 'https://ncas-cms.github.io/cf-python<p>'
            html += '<b>cf-plot</b> - plotting<br>'
            html += 'http://ajheaps.github.io/cf-plot<P>'
            html += '<b>PyQt5</b> - GUI toolkit<p>'
            html += 'Author: Andy Heaps andy.heaps@ncas.ac.uk<br>'
            html += '&#169; NCAS CMS January 2023'
            self.about_help = Help(html)
            self.about_help.show()

        if value.text() == 'cfview':
            html = '<body><h2>Using cfview</h2>'
            html += 'cfview is available for the Unix and Mac platforms<p>'
            html += '<h4>Starting cfview</h4>'
            html += 'On the command line type<br><b>cfview</b><br>or<br><b>cfview datafile(s)</b><p>'
            html += 'Valid input formats match those of cf-python - netCDF, Met Office PP and fields files.<p>'
            html += '<h4>The main interface</h4>'
            html += 'The main interface has a list of fields on the top left with and an index number and the '
            html += 'number of x, y, z and t values for a field.  The fields can be ordered by index or field name '
            html += 'and a search function is useful for when there are many fields in the dataset<p>'
            html += 'An output messages panel lies underneath the fields listing. '
            html += 'On the right hand side a dimension view panel has x, y, z and t radio buttons to allow showing of '
            html += 'the four dimension values'
            html += '<h4>Selecting dimension values</h4>'
            html += 'Use the left mouse to highlight a particular value or shift and left mouse to select a range. '
            html += 'On Linux control and left click will select multiple values and on a Mac this is Command and '
            html += 'left click.'
            html += '<h4>Contour plots</h4>'
            html += 'The initial setting is for cfview to make a x-y contour plot.  The axes used for the contour '
            html += 'plot can be changed using the dropdown menu above the dimension viewing panel. Contour, '
            html += 'contour level, maps and colour scales can be changed using the Setup menu item.  Once changed the '
            html += 'new settings are persistent until reset or changed again.<p>'  
            html += '<h4>Unrecognised or multiple dimensions of the same type</h4>'
            html += 'Dimensions not recogised as X, Y, Z, T dimensions are labelled as cf-python sees them - dim0, dim1 etc.'
            html += 'If several dimensions exist of the same type in the data i.e. a Z coordinate and a Z auxiliary '
            html += 'coordinate then the coordinates are labelled as dim1 etc as cf-python cannot know which Z coordinate '
            html += 'is intended.  Hovering the mouse over the field <b>Index nX nY ndim1 field name</b> box will show a tool tip '
            html += 'box with the dimensions and their associated names.  These can also be seen in more detail in the '
            html += 'View -> properties pop up window' 
            html += '<h4>Default settings</h4>'
            html += 'Default settings for fonts, font size and interface colour theme are changed in the Setup '
            html += '-> cfview defaults menu item. Contour settings are also save to the cfview defaults file with is '
            html += 'generally ~/.cfview_defaults.  To load in the new defaults start cfview again.  If the defaults file '
            html += 'is ~/.cfview_defaults then no additional command line arguments are required.  For a different defaults '
            html += 'file use<br><b>cfview -d mydefaults.file datafile</b><p>'



            self.cfview_help = Help(html)
            self.cfview_help.show()

    def file_menu(self, value):

        if value.text() == 'Open':
            self.open_file(self)

        if value.text() == 'Save':
            self.save_file(self)

        if value.text() == 'Exit':
            sys.exit(0)




class Cfview_defaults_window(QWidget):
    '''popup window for editing cfview defaults'''

    def __init__(self, parent):
        super(Cfview_defaults_window, self).__init__()

        self.parent = parent
        self.initUI()
        
        self.windows = ['trajectory_window', 'names_window', 'transform_window', 'vector_window',\
                        'data_window', 'line_window', 'contour_window', 'map_window', \
                        'colour_scale', 'contour_levels']


    def initUI(self):

        # Set up some buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)
        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)
        saveButton = QPushButton("Save defaults to file:")
        saveButton.clicked.connect(self.save)
        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        # Interface colour themes
        themeComboBox = QComboBox()
        themes = ['BlueMono', 'BluePurple', 'DarkBlue12', 'DarkGrey7',\
                  'DarkTeal11', 'GreenMono', 'LightBrown2', 'NeutralBlue',\
                  'SandyBeach', 'TealMono', 'SystemDefault', 'SystemDefault1', 'xconv']
    
        for theme in themes:
            themeComboBox.addItem(theme)
        themeComboBox.highlighted[str].connect(self.theme)

        # Font size slider
        font_Slider = QSlider(Qt.Horizontal)
        font_Slider.sliderMoved.connect(self.fontsize)
        font_Slider.setRange(2, 25)
        font_Slider.setValue(plotvars.fontsize)

        # Find available fonts
        db = QFontDatabase()
        fonts = QFontDatabase().families()

        # Main font
        fontComboBox = QComboBox()
        for font in fonts:
            fontComboBox.addItem(font)
        

        test_fonts = ['Times', 'Helvetica', 'Arial', 'DejaVu Sans', deepcopy(plotvars.font)]
        index = 0
        for font in test_fonts:
            if font in fonts:
                index = fonts.index(font)
            else:
                plotvars.font = font


        fontComboBox.setCurrentIndex(index)
        fontComboBox.highlighted[str].connect(self.font_main)


        # Mono font
        font_monoComboBox = QComboBox()
        for font in fonts:
            font_monoComboBox.addItem(font)

        test_fonts = ['Times', 'Helvetica', 'Courier', 'Menlo', 'Monaco', deepcopy(plotvars.font_mono)]
        index = 0
        for font in test_fonts:
            if font in fonts:
                index = fonts.index(font)
            else:
                plotvars.font = font

        font_monoComboBox.setCurrentIndex(index)
        font_monoComboBox.highlighted[str].connect(self.font_mono)
        
        # Code output tick box
        code_outputCheckBox = QCheckBox("Output code")
        code_outputCheckBox.setChecked(plotvars.code_output)
        code_outputCheckBox.clicked.connect(self.code_output)
        
         # Terminal output tick box
        terminal_outputCheckBox = QCheckBox("Output messages to terminal")
        terminal_outputCheckBox.setChecked(plotvars.terminal_output)
        terminal_outputCheckBox.clicked.connect(self.terminal_output)       


        # Create layout
        vbox = QVBoxLayout()

        vbox.addWidget(QLabel('Interface options'))
        
        hbox_font = QHBoxLayout()
        font_Label = QLabel('Main font:')
        hbox_font.addWidget(font_Label)
        hbox_font.addWidget(fontComboBox)
        vbox.addLayout(hbox_font)

        hbox_font_mono = QHBoxLayout()
        font_mono_Label = QLabel('Monospace font:')
        hbox_font_mono.addWidget(font_mono_Label)
        hbox_font_mono.addWidget(font_monoComboBox)
        vbox.addLayout(hbox_font_mono)

        hbox_font_size = QHBoxLayout()
        font_size_Label = QLabel('Font size: 15')
        hbox_font_size.addWidget(font_size_Label)
        hbox_font_size.addWidget(font_Slider)
        vbox.addLayout(hbox_font_size)

        hbox_theme = QHBoxLayout()
        themeLabel = QLabel('Interface colour theme:')
        hbox_theme.addWidget(themeLabel)
        hbox_theme.addWidget(themeComboBox)
        vbox.addLayout(hbox_theme)

        vbox.addWidget(HLine())

        vbox.addWidget(code_outputCheckBox)
        
        vbox.addWidget(terminal_outputCheckBox)

        vbox.addWidget(HLine())


        # Save text boxes
        saveTextBox = QLineEdit()
        saveTextBox.setText('~/.cfview_defaults')

        save_hbox = QHBoxLayout()
        vbox.addLayout(save_hbox)
        save_hbox.addWidget(saveButton)
        save_hbox.addWidget(saveTextBox)

        hbox_buttons = QHBoxLayout()
        vbox.addLayout(hbox_buttons)
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)

        self.themeComboBox = themeComboBox
        self.font_size_Label = font_size_Label
        self.saveTextBox = saveTextBox
        self.font_Slider = font_Slider
        self.font_monoComboBox = font_monoComboBox
        self.fontComboBox = fontComboBox
        self.code_outputCheckBox = code_outputCheckBox
        self.terminal_outputCheckBox = terminal_outputCheckBox
        self.setWindowTitle('cfview defaults')

        self.setLayout(vbox)

        # Set the window theme
        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        # Set font 
        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        self.defaults_help = None  # No external window yet.

        self.show()

    def theme(self, name):

        cols = ['#f5deb3', '#f5deb3', '#000000', '#bfdfff', '#000000', '#000000']

        if name == 'BlueMono':
            cols = ['#abb6d4', '#7085c4', '#ffffff', '#f1f4fd', '#000000', '#000000']

        if name == 'BluePurple':
            cols = ['#a5cadd', '#313852', '#ffffff', '#dff5ff', '#000000', '#7a2894']

        if name == 'DarkBlue12':
            cols = ['#324f7b', '#5067aa', '#ffffff', '#87a6df', '#dcf8cf', '#000000']

        if name == 'DarkGrey7':
            cols = ['#4c586e', '#434261', '#c7dfbd', '#574e6d', '#c7dfbd', '#c7dfbd']

        if name == 'DarkTeal11':
            cols = ['#40555a', '#69868c', '#ffffff', '#69868c', '#ffffff', '#ffffff']

        if name == 'GreenMono':
            cols = ['#a8c3b4', '#6e9e86', '#ffffff', '#e3e3e3', '#000000', '#000000']

        if name == 'LightBrown2':
            cols = ['#a7ad7f', '#5b8e7d', '#ffffff', '#e6d3a8', '#000000', '#000000']

        if name == 'NeutralBlue':
            cols = ['#92aa9d', '#d0dbbd', '#000000', '#fcfff6', '#000000', '#000000']

        if name == 'SandyBeach':
            cols = ['#efeccb', '#056484', '#ffffff', '#e6d3a8', '#002e30', '#00302b']

        if name == 'SystemDefault':
            cols = ['#f0f0f0', '#082567', '#ffffff', '#c8c8c8', '#000000', '#000000']

        if name == 'SystemDefault1':
            cols = ['#f0f0f0', '#f0efed', '#000000', '#c8c8c8', '#000000', '#000000']
    
        if name == 'TealMono':
            cols = ['#a9cede', '#18243f', '#ffffff', '#dfecf2', '#000000', '#000000']

        if name == 'xconv':
            cols = ['#f5deb3', '#f5deb3', '#000000', '#bfdfff', '#000000', '#000000']


        # assign colours and set them in plotvars
        background = cols[0]
        button = cols[1]
        buttontext = cols[2]
        highlight = cols[3]
        text = cols[4]
        windowtext=cols[5]

        plotvars.background_colour = background
        plotvars.button_colour = button
        plotvars.buttontext_colour = buttontext
        plotvars.highlight_colour = highlight
        plotvars.text_colour = text
        plotvars.windowtext_colour = windowtext



        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(background))
        palette.setColor(QPalette.Base, QColor(background))    
        palette.setColor(QPalette.Button, QColor(button))
        palette.setColor(QPalette.ButtonText, QColor(buttontext))
        palette.setColor(QPalette.Highlight, QColor(highlight))
        palette.setColor(QPalette.HighlightedText, QColor(text))
        palette.setColor(QPalette.Text,  QColor(text))    
        palette.setColor(QPalette.WindowText, QColor(windowtext))
        self.setPalette(palette)
        self.parent.window().setPalette(palette)
        for window in self.windows: 
            if getattr(self.parent, window):
                getattr(self.parent, window).setPalette(palette)
            


    def fontsize(self):

        value = self.font_Slider.value()
        font = QFont(plotvars.font, value)

        self.parent.window().setFont(font)
        plotvars.fontsize = value
        self.font_size_Label.setText('Font size: ' + str(value))

        font = QFont(plotvars.font_mono, plotvars.fontsize)
        self.parent.window().fieldlist.setFont(font)
        
        self.parent.window().field_titles.setFont(font)
        self.parent.window().fieldlist.setFont(font)
        

    def font_main(self, value):

        plotvars.font = value
        font = QFont(plotvars.font, plotvars.fontsize)
        self.parent.window().setFont(font)
        

    def font_mono(self, value):

        plotvars.font_mono = value
        font = QFont(plotvars.font_mono, plotvars.fontsize)

        # Set mono font for field titles and field list
        self.parent.window().field_titles.setFont(font)
        self.parent.window().fieldlist.setFont(font)


    def code_output(self):
        if self.code_outputCheckBox.isChecked():
            plotvars.code_output = True
        else:
            plotvars.code_output = False
            
            
    def terminal_output(self):
        if self.terminal_outputCheckBox.isChecked():
            plotvars.terminal_output = True
            sys.stdout = sys.__stdout__
            sys.stderr = sys.__stderr__
        else:
            plotvars.terminal_output = False        
            sys.stdout = EmittingStream(textWritten=self.parent.output_terminal_written)
            sys.stderr = EmittingStream(textWritten=self.parent.output_terminal_written)
            

    def reset(self):
        # Reset to defaults
        self.saveTextBox.setText('~/.cfview_defaults')
        plotvars.fontsize = 15
        self.font_Slider.setValue(15)
        self.font_size_Label.setText('Font size: 15')

        # Find available fonts
        db = QFontDatabase()
        fonts = QFontDatabase().families()

        # Main font
        test_fonts = ['Times', 'Helvetica', 'Arial', 'DejaVu Sans']
        for font in test_fonts:
            if font in fonts:
                index = fonts.index(font)
                self.fontComboBox.setCurrentIndex(index)
                plotvars.font = font
        font = QFont(plotvars.font, plotvars.fontsize)
        self.parent.window().setFont(font)


        # Mono font
        test_fonts = ['Times', 'Helvetica', 'Courier', 'Menlo', 'Monaco', 'DejaVu Sans Mono']
        for font in test_fonts:
            if font in fonts:
                index = fonts.index(font)
                plotvars.font_mono = font
                self.font_monoComboBox.setCurrentIndex(index)
        font = QFont(plotvars.font_mono, plotvars.fontsize)
        self.parent.window().field_titles.setFont(font)
        self.parent.window().fieldlist.setFont(font)

        # Reset the theme
        self.theme('BlueMono')
        self.themeComboBox.setCurrentIndex(0)

        # Code output is True
        self.code_outputCheckBox.setChecked(True)
        plotvars.code_output = True
        
         # Terminal output is True
        self.terminal_outputCheckBox.setChecked(False)
        plotvars.terminal_output = True       
        

    def save(self):

        # Ask to clobber existing file
        full_path = os.path.expanduser(self.saveTextBox.text())
        self.savefile = full_path
        check = os.path.isfile(full_path) 
        
        if check:
            html = ''
            html += '<body>The file ' + full_path + ' already exists'
            html += '<p>Okay to overwrite?</body>'


            self.clobber = Clobber_file(html, self.savefile)

            self.clobber.show()

        else:
            plotvars.write_defaults = True
            
        Save_defaults(self.savefile)


    def help(self):

        if self.defaults_help is None:

            html = ''
            html += '<body><h2>cfview default options</h2>'
            html += 'The defaults file for cfview is generally stored in the file ~/.cfview_defaults '
            html += 'which is read in when cfview starts up.  A different defaults file can be specified using '
            html += 'the -d parameter to the cfview command line i.e.<br>'
            html += '<b>cfview -d mydefaults.def file.nc</b><p>'
            html += 'The main font applies to all text in the cfview display apart from the field titles and '
            html += 'names where the monospace font applies.'
            html += 'Interface colour schemes can be altered by manually editing the ~/.cfview_defaults file '
            html += 'and replacing the six main colour definitions.<p>'
            html += 'Contour options for fill, blockfill, blockfill_fast, lines and line labels that are currently selected '
            html += 'are also saved into the ~/.cfview_defaults file.'

            self.defaults_help = Help(html)

        self.defaults_help.show()
        
        
    def closeEvent(self, event):
        # Close self.parent.cfview_defaults_window
        self.parent.cfview_defaults_window = None
        self.close()
        


class Clobber_file(QWidget):
    '''Popup window for clobbering a file'''

    def __init__(self, html, savefile):
        super(Clobber_file, self).__init__()
        self.html = html
        self.savefile = savefile
        self.initUI()


    def initUI(self):

        yesButton = QPushButton("Yes")
        yesButton.clicked.connect(self.clobber_yes)

        noButton = QPushButton("No")
        noButton.clicked.connect(self.clobber_no)

        helpTextbox = QTextEdit()
        helpTextbox.setReadOnly(True)


        doc = QTextDocument()

        doc.setHtml(self.html)
        helpTextbox.setDocument(doc)


        vbox = QVBoxLayout()
        vbox.addWidget(helpTextbox)

        hbox = QHBoxLayout()
        hbox.addWidget(yesButton)
        hbox.addWidget(noButton)
        vbox.addLayout(hbox)

        self.setLayout(vbox)
       

        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        # Set font 
        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        self.setFixedWidth(600)

        self.show()


    def clobber_yes(self):
        plotvars.write_defaults = True
        #self.savefile()
        Save_defaults(self.savefile)
        self.close()

    def clobber_no(self):
        plotvars.write_defaults = False
        self.close()

     

class Save_defaults():

    def __init__(self, savefile):
        super(Save_defaults, self).__init__()
        self.savefile = savefile
        self.save()

    def save(self):
        if plotvars.write_defaults:
        
            plotvars.write_defaults = False

            datafile = open(self.savefile, 'w')
            datafile.write('### cfview defaults file ###\n\n')
            datafile.write('background_colour ' + plotvars.background_colour + '\n')
            datafile.write('button_colour ' + plotvars.button_colour + '\n')
            datafile.write('buttontext_colour ' + plotvars.buttontext_colour + '\n')
            datafile.write('highlight_colour ' + plotvars.highlight_colour + '\n')
            datafile.write('text_colour ' + plotvars.text_colour + '\n')
            datafile.write('windowtext_colour ' + plotvars.windowtext_colour + '\n')
            datafile.write('font ' + plotvars.font + '\n')
            datafile.write('font_mono ' + plotvars.font_mono + '\n')
            datafile.write('fontsize ' + str(plotvars.fontsize) + '\n')
            datafile.write('fill ' + str(plotvars.fill) + '\n')
            datafile.write('code_output ' + str(plotvars.code_output) + '\n')
            datafile.write('terminal_output ' + str(plotvars.terminal_output) + '\n')
            datafile.write('blockfill ' + str(plotvars.blockfill) + '\n')
            datafile.write('blockfill_fast ' + str(plotvars.blockfill_fast) + '\n')
            datafile.write('lines ' + str(plotvars.lines) + '\n')
            datafile.write('line_labels ' + str(plotvars.line_labels) + '\n')
            datafile.write('titles ' + str(plotvars.titles) + '\n')
            datafile.write('nlevs ' + str(plotvars.nlevs) + '\n')            
            datafile.close()



class Contour_levels_window(QWidget):
    '''popup window for editing contour level defaults'''

    def __init__(self, parent):
        super(Contour_levels_window, self).__init__()

        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up some buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        # Labels
        automatic_Label = QLabel('Automatic levels')

        min_Label = QLabel('Min:')
        min_Label.setEnabled(False)
        max_Label = QLabel('Max:')
        max_Label.setEnabled(False)
        step_Label = QLabel('Step:')
        step_Label.setEnabled(False)

        ascending_Label = QLabel('Ascending values separated by spaces')
        extensions_Label = QLabel('Colourbar extensions - extend contours to cover all the data')
        lower_Label = QLabel('Lower:')
        upper_Label = QLabel('Upper:')


        # Level type selection tick boxes
        automaticCheckBox = QCheckBox("Automatic levels")
        automaticCheckBox.setChecked(True)
        automaticCheckBox.clicked.connect(lambda: self.level_type('automatic'))

        evenCheckBox = QCheckBox("Evenly spaced levels")
        evenCheckBox.setChecked(False)
        evenCheckBox.clicked.connect(lambda: self.level_type('even'))

        manualCheckBox = QCheckBox("User spaced levels")
        manualCheckBox.setChecked(False)
        manualCheckBox.clicked.connect(lambda: self.level_type('manual'))



        # Extension type selection tick boxes
        extension_lowerCheckBox = QCheckBox("Lower")
        extension_lowerCheckBox.setChecked(True)
        extension_lowerCheckBox.clicked.connect(lambda: self.extension_type('lower'))
        extension_upperCheckBox = QCheckBox("Upper")
        extension_upperCheckBox.setChecked(True)
        extension_upperCheckBox.clicked.connect(lambda: self.extension_type('upper'))



        # Levels entry Labels and Boxes
        levs_minTextbox = QLineEdit()
        levs_minTextbox.textChanged.connect(self.levs_even_changed)
        levs_minTextbox.setEnabled(False)
        levs_maxTextbox = QLineEdit()
        levs_maxTextbox.textChanged.connect(self.levs_even_changed)
        levs_maxTextbox.setEnabled(True)
        levs_stepTextbox = QLineEdit()
        levs_stepTextbox.textChanged.connect(self.levs_even_changed)
        levs_stepTextbox.setEnabled(True)

        levs_manualTextbox = QLineEdit()
        levs_manualTextbox.textChanged.connect(self.levs_manual_changed)
        levs_manualTextbox.setEnabled(True)


        vbox = QVBoxLayout()
        hbox_even = QHBoxLayout()
        hbox_manual = QHBoxLayout()
        hbox_extensions = QHBoxLayout()
        hbox_buttons = QHBoxLayout()

        vbox.addWidget(automaticCheckBox)
        vbox.addWidget(HLine())

        vbox.addWidget(evenCheckBox)
        vbox.addLayout(hbox_even)
        hbox_even.addWidget(min_Label)
        hbox_even.addWidget(levs_minTextbox)
        hbox_even.addWidget(max_Label)
        hbox_even.addWidget(levs_maxTextbox)
        hbox_even.addWidget(step_Label)
        hbox_even.addWidget(levs_stepTextbox)

        vbox.addWidget(HLine())
        vbox.addWidget(manualCheckBox)
        vbox.addLayout(hbox_manual)
        hbox_manual.addWidget(ascending_Label)
        hbox_manual.addWidget(levs_manualTextbox)

        vbox.addWidget(HLine())
        vbox.addWidget(extensions_Label)
        vbox.addLayout(hbox_extensions)
        hbox_extensions.addWidget(extension_lowerCheckBox)
        hbox_extensions.addWidget(extension_upperCheckBox)

        vbox.addLayout(hbox_buttons)
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)

   
        self.setWindowTitle('cfview contour levels')

        self.setLayout(vbox)


        self.automaticCheckBox = automaticCheckBox
        self.evenCheckBox = evenCheckBox
        self.levs_minTextbox = levs_minTextbox
        self.levs_maxTextbox = levs_maxTextbox
        self.levs_stepTextbox = levs_stepTextbox
        self.manualCheckBox = manualCheckBox
        self.levs_manualTextbox = levs_manualTextbox
        self.extension_lowerCheck = extension_lowerCheckBox
        self.extension_upperCheck = extension_upperCheckBox

        self.min_Label = min_Label
        self.max_Label = max_Label
        self.step_Label = step_Label
        self.ascending_Label = ascending_Label

        self.contour_levels_help = None  # No external window yet.


        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        # Set font 
        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)



        # Set initial widget states
        self.level_type('automatic')

        self.show()


    def level_type(self, text):
 
        types = ['automatic', 'even', 'manual']
        vars = [plotvars.levs_automatic_set,  plotvars.levs_set, plotvars.levs_manual_set]
        checkboxes = [self.automaticCheckBox, self.evenCheckBox, self.manualCheckBox]
        state = [False, False, False]
        loc = types.index(text)
        state[loc] = True

        plotvars.levs_automatic_set = state[0]
        plotvars.levs_set = state[1]
        plotvars.levs_manual_set = state[2]

        for i in np.arange(3):
            checkboxes[i].setChecked(state[i])


        if plotvars.levs_automatic_set:
            color = plotvars.text_colour_insensitive
        else:
            color = plotvars.text_colour


        objs = [self.levs_minTextbox, self.levs_maxTextbox, self.levs_stepTextbox,\
                self.levs_manualTextbox, self.min_Label , self.max_Label,\
                self.step_Label, self.ascending_Label]

        if text == 'automatic':
            flags = [False, False, False, False, False, False, False, False]

        if text == 'even':
            flags = [True, True, True, False, True, True, True, False]

        if text == 'manual':
            flags = [False, False, False, True, False, False, False, True]

        for i in np.arange(len(objs)):
            obj = objs[i]
            color = plotvars.text_colour_insensitive
            if flags[i]:
                color = plotvars.text_colour
            obj.setEnabled(flags[i])
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(color))
            obj.setPalette(pal)


    def levs_even_changed(self):
        plotvars.levs_min = self.levs_minTextbox.text()
        plotvars.levs_max = self.levs_maxTextbox.text()
        plotvars.levs_step = self.levs_stepTextbox.text()


    def levs_manual_changed(self):
        plotvars.levs_manual = self.levs_manualTextbox.text()
        

    def reset(self):
        self.level_type('automatic')
        self.levs_minTextbox.setText('')
        self.levs_maxTextbox.setText('')
        self.levs_stepTextbox.setText('')
        self.levs_manualTextbox.setText('')
        plotvars.levs_set = False
        
        
    def extension_type(self, text):

        plotvars.levs_extend_lower = self.extension_lowerCheck.isChecked()
        plotvars.levs_extend_upper = self.extension_upperCheck.isChecked()


    def help(self):
        if self.contour_levels_help is None:

            html = ''
            html += '<body><h2>Contour level options</h2>'
            html += 'Contour levels are initially set automatically based on the range of the field and '
            html += 'split into reasonable contour levels. If a region of the field is chosen for contouring '
            html += 'then the range of the full field is still used.'

            html += '<h3>Setting contour levels</h3>'
            html += 'When making plots that compare data click on the evenly or manually spaced levels tick '
            html += 'boxes and set them as appropriate. Manualy spaced levels must be separated by spaces and ascend in value.'

            html += '<h3>Colorbar extensions</h3>'
            html += 'Colorbar extensions are a way of extending the contours to cover all the data at the ends of the '
            html += 'contour range. Click on the colorbar extension tick boxes as appropriate if these are both required.</p></body>' 

            self.contour_levels_help = Help(html)

        self.contour_levels_help.show()

    def closeEvent(self, event):
        # Close self.parent.contour_levels_window
        self.parent.contour_levels_window = None
        self.close()








class Help(QWidget):
    '''Popup window showing help text'''

    def __init__(self, html):
        super(Help, self).__init__()
        self.html = html
        self.initUI()


    def initUI(self):

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.close)


        helpTextbox = QTextEdit()
        helpTextbox.setReadOnly(True)



        doc = QTextDocument()

        doc.setHtml(self.html)
        helpTextbox.setDocument(doc)





        vbox = QVBoxLayout()
        vbox.addWidget(helpTextbox)
        vbox.addWidget(quitButton)

        self.setLayout(vbox)
       

        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        # Set font 
        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        self.setMinimumSize(600, 600)

        self.show()



class HLine(QFrame):
    def __init__(self, parent=None, color=QColor(plotvars.text_colour)):

        super(HLine, self).__init__(parent)
        self.setFrameShape(QFrame.HLine)
        self.setFrameShadow(QFrame.Plain)
        self.setFixedHeight(40)
        self.setLineWidth(0)
        self.setMidLineWidth(3)
        self.setContentsMargins(0, 0, 0, 0)
        self.setColor(color)

    def setColor(self, color):
        pal = self.palette()
        pal.setColor(QPalette.WindowText, color)
        self.setPalette(pal)


class Colour_scale_window(QWidget):
    '''popup window for changing colour scale defaults'''

    def __init__(self, parent):
        super(Colour_scale_window, self).__init__()
        
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up some buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)



        # Scale selection tick boxes
        automaticCheckBox = QCheckBox("Automatic colour scale")
        automaticCheckBox.setChecked(True)
        automaticCheckBox.clicked.connect(self.automatic)

        reverseCheckBox = QCheckBox("Reverse colour scale")
        reverseCheckBox.setChecked(False)
        reverseCheckBox.clicked.connect(self.reverse)

        # Text box labels
        numberLabel = QLabel('Number of colours in the scale')
        whiteLabel = QLabel('Set these indicies to be white')
        aboveLabel = QLabel('Number of colours above the scale midpoint')
        belowLabel = QLabel('Number of colours below the scale midpoint')

        pal = QPalette(numberLabel.palette())
        pal.setColor(QPalette.WindowText, QColor('#708090'))
        numberLabel.setPalette(pal)

        # Text boxes
        numberTextBox = QLineEdit()
        numberTextBox.textChanged.connect(lambda: self.text_changed('number'))
        whiteTextBox = QLineEdit()
        whiteTextBox.textChanged.connect(lambda: self.text_changed('white'))
        aboveTextBox = QLineEdit()
        aboveTextBox.textChanged.connect(lambda: self.text_changed('above'))
        belowTextBox = QLineEdit()
        belowTextBox.textChanged.connect(lambda: self.text_changed('below'))


        # Set the layout
        vbox = QVBoxLayout()
        hbox_number = QHBoxLayout()
        hbox_white = QHBoxLayout()
        hbox_above = QHBoxLayout()
        hbox_below = QHBoxLayout()
        hbox_buttons = QHBoxLayout()

        vbox.addWidget(automaticCheckBox)
        vbox.addWidget(HLine())

        vbox.addLayout(hbox_number)
        hbox_number.addWidget(numberLabel)
        hbox_number.addWidget(numberTextBox)

        vbox.addLayout(hbox_white)
        hbox_white.addWidget(whiteLabel)
        hbox_white.addWidget(whiteTextBox)

        vbox.addLayout(hbox_above)
        hbox_above.addWidget(aboveLabel)
        hbox_above.addWidget(aboveTextBox)

        vbox.addLayout(hbox_below)
        hbox_below.addWidget(belowLabel)
        hbox_below.addWidget(belowTextBox)

        vbox.addWidget(reverseCheckBox)

        vbox.addWidget(HLine())

        # Make a scrollable area for the colour bars and check boxes
        vbox_scale = QVBoxLayout()
        vbox.addLayout(vbox_scale)

        formLayout = QFormLayout()
        groupBox = QGroupBox('Colour scales')
        checkboxlist = []
        scaleimagelist = []

        self.automaticCheckBox = automaticCheckBox
        self.reverseCheckBox = reverseCheckBox
        self.numberTextBox = numberTextBox
        self.whiteTextBox = whiteTextBox
        self.aboveTextBox = aboveTextBox
        self.belowTextBox = belowTextBox
        self.numberLabel = numberLabel
        self.aboveLabel = aboveLabel
        self.belowLabel = belowLabel
        self.whiteLabel = whiteLabel



        # Add the colours scale checkboxes and colour scales
        for i in np.arange(len(plotvars.cscales)):
            scale = plotvars.cscales[i]
            if scale == 'viridis':
                formLayout.addRow(QLabel('Perceptually neutral colour scales'))
            if scale == 'hotcold_18lev':
                formLayout.addRow(QLabel(''))
                formLayout.addRow(QLabel('NCAR Command Language - MeteoSwiss colour maps'))
            if scale == 'amwg':
                formLayout.addRow(QLabel(''))
                formLayout.addRow(QLabel('NCAR Command Language - small color maps (<50 colours)'))
            if scale == 'amwg256':
                formLayout.addRow(QLabel(''))
                formLayout.addRow(QLabel('NCAR Command Language - large color maps (>50 colours)'))
            if scale == 'StepSeq25':
                formLayout.addRow(QLabel(''))
                formLayout.addRow(QLabel('NCAR Command Language - Enhanced to help with colour blindness'))
            if scale == 'os250kmetres':
                formLayout.addRow(QLabel(''))
                formLayout.addRow(QLabel('Orography/bathymetry colour scales'))
            if scale == 'scale1':
                formLayout.addRow(QLabel(''))
                formLayout.addRow(QLabel('IDL guide scales'))

            setattr(self, scale + 'CheckBox', QCheckBox(scale))
            getattr(self, scale + 'CheckBox').setChecked(False)
            getattr(self, scale + 'CheckBox').clicked.connect(partial(self.scale_changed, plotvars.cscales[i]))


            # Make an image of the colour scale defined in cf-plot colourmaps directory
            width = 750
            height = 13

            img  = Image.new( mode = "RGB", size = (width, height), color = (255, 255, 255) )
            
            if cfp.__file__[-11:] == '__init__.py':
                rgb = cfp.__file__[:-11] + 'colourmaps/' + scale + '.rgb'
            else:
                rgb = cfp.__file__[:-9] + 'colourmaps/' + scale + '.rgb'    
                
            rgb_vals = []
            with open(rgb, newline='', encoding='utf-8') as f:
                reader = csv.reader(f)
                for row in reader:
                    vals = row[0].split()
                    rgb_vals.append([int(vals[0]), int(vals[1]), int(vals[2])])


            step = width/len(rgb_vals)

            for row in np.arange(len(rgb_vals)):
                idraw = ImageDraw.Draw(img)
                idraw.rectangle((row*step, 0, (row+1)*step, height), fill=tuple(rgb_vals[row]))

            image = QLabel()

            r, g, b = img.split()
            im = Image.merge("RGB", (b, g, r))
            
            im2 = im.convert("RGBA")
            data = im2.tobytes("raw", "RGBA")
            qim = QImage(data, im.size[0], im.size[1], QImage.Format_ARGB32)
            pixmap = QPixmap.fromImage(qim)
            image.setPixmap(pixmap)

            formLayout.addRow(getattr(self, scale + 'CheckBox'), image)


        #self.viridisCheckBox.setChecked(True)

        groupBox.setLayout(formLayout)
        #vbox_scale.setLayout(formLayout)
        scroll = QScrollArea()
        scroll.setWidget(groupBox)
        scroll.setWidgetResizable(True)
        scroll.setFixedHeight(400)
        vbox_scale.addWidget(scroll)





        vbox.addLayout(hbox_buttons)
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)


        self.colour_scale_help = None  # No external window yet.

        self.setWindowTitle('cfview colour scales')
        self.setLayout(vbox)



        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        # Initial set of widget sensitivity
        plotvars.cscale_automatic_set = self.automaticCheckBox.isChecked()
        self.set_sensitive()

        self.show()



    def text_changed(self, textbox):
        if textbox == 'number':
            plotvars.cscale_ncols = self.numberTextBox.text()
        if textbox == 'white':
            plotvars.cscale_white = self.whiteTextBox.text()
        if textbox == 'above':
            plotvars.cscale_above = self.aboveTextBox.text()
        if textbox == 'below':
            plotvars.cscale_below = self.belowTextBox.text()





    def automatic(self):
        # Toggle automatic colour scales
        if self.automaticCheckBox.isChecked():
            self.reset()
        else:
            plotvars.cscale_automatic_set = self.automaticCheckBox.isChecked()
            self.set_sensitive()


    def set_sensitive(self):
        # Set the sensitivity and color of the widgets

        if plotvars.cscale_automatic_set:
            color = plotvars.text_colour_insensitive
            enabled = False
        else:
            color = plotvars.text_colour
            enabled = True

        objs = [self.reverseCheckBox, self.numberTextBox, self.whiteTextBox, self.aboveTextBox, \
               self.belowTextBox, self.reverseCheckBox, self.numberLabel, self.aboveLabel,\
               self.belowLabel, self.whiteLabel]
        for scale in plotvars.cscales:
            objs.append(getattr(self, scale + 'CheckBox'))

        for obj in objs:
            obj.setEnabled(enabled)
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(color))
            obj.setPalette(pal)


    def reverse(self):
        plotvars.cscale_reverse_set = self.reverseCheckBox.isChecked()


    def scale_changed(self, myscale):
        for scale in plotvars.cscales:
            getattr(self, scale + 'CheckBox').setChecked(False)

        getattr(self, myscale + 'CheckBox').setChecked(True)
        plotvars.cscale = myscale


    def reset(self):

        self.automaticCheckBox.setChecked(True)
        plotvars.cscale_automatic_set = True
        self.reverseCheckBox.setChecked(False)
        self.numberTextBox.setText('')
        self.whiteTextBox.setText('')
        self.aboveTextBox.setText('')
        self.belowTextBox.setText('')
        
        for scale in plotvars.cscales:
            getattr(self, scale + 'CheckBox').setChecked(False)

        #self.viridisCheckBox.setChecked(True)
        plotvars.cscale = 'viridis'
        plotvars.cscale_reverse_set = False
        plotvars.cscale_white = ''
        plotvars.cscale_ncols = ''
        plotvars.cscale_above = ''
        plotvars.cscale_below = ''
        
        self.set_sensitive()

    def help(self):

        if self.colour_scale_help is None:

            html = ""
            html += "<body><h2>Colour scale options</h2>"
            html += "With the automatic colour scale option ticked cfview selects the colour scale depending "
            html += "on the data to be contoured:<p>"
            html += "viridis - fields that don\'t span zero - temperature in Kelvin etc.  This is a perceptually "
            html += "neutral colour scale that doesn\'t draw the eye to any part of the scale <p>"
            html += "scale1 - fields that span zero - zonal wind etc.  This is a blue - red scale suitable for "
            html += "contour plots of fields that have a zero in their contour levels.  The scale is automatically "
            html += "adjusted so that blue colours are below zero and red colours above.<p>"
            html += "<h3>Selecting a colour scale</h3>"
            html += "When a different colour scale is selected the scale will be automatically adjusted "
            html += "to fit the number of contour levels.<p>"
            html += "<h3>Selecting the number of colours</h3>"
            html += "Set the number of colours in the scale to gain more control over the colour scale. "
            html += "Once this is done then the white indicies and scale midpoint options become useful "
            html += "to allow more precise scale manipulation.  Set a number of white colour indicies by leaving "
            html += "a space between the indices required.<p>"

            self.colour_scale_help = Help(html)

        self.colour_scale_help.show()
        
    def closeEvent(self, event):
        # Close self.parent.colour_scale_window
        self.parent.colour_scale_window = None
        self.close()
        


class Map_window(QWidget):
    '''popup window for changing map defaults'''

    def __init__(self, parent):
        super(Map_window, self).__init__()
        
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up some buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)


        # Text box labels
        lonminLabel = QLabel('lonmin')
        lonmaxLabel = QLabel('lonmax')
        latminLabel = QLabel('latmin')
        latmaxLabel = QLabel('latmax')
        boundinglatLabel = QLabel('Bounding latitude')
        lon_0Label = QLabel('Longitude centre of map domain')
        other_map_typesLabel = QLabel('Other map types')
        continent_attributesLabel = QLabel('Continent Attributes')
        continent_thicknessLabel = QLabel('Thickness')
        continent_colorLabel = QLabel('Colour')

        # Text boxes
        lonminTextBox = QLineEdit()
        lonminTextBox.setText('-180')
        lonminTextBox.textChanged.connect(lambda: self.textbox_changed('lonmin'))
        lonmaxTextBox = QLineEdit()
        lonmaxTextBox.setText('180')
        lonmaxTextBox.textChanged.connect(lambda: self.textbox_changed('lonmax'))
        latminTextBox = QLineEdit()
        latminTextBox.setText('-90')
        latminTextBox.textChanged.connect(lambda: self.textbox_changed('latmin'))
        latmaxTextBox = QLineEdit()
        latmaxTextBox.setText('90')
        latmaxTextBox.textChanged.connect(lambda: self.textbox_changed('latmax'))
        boundinglatTextBox = QLineEdit()
        boundinglatTextBox.setText('0')
        boundinglatTextBox.textChanged.connect(lambda: self.textbox_changed('boundinglat'))
        lon_0TextBox = QLineEdit()
        lon_0TextBox.setText('0')
        lon_0TextBox.textChanged.connect(lambda: self.textbox_changed('lon_0'))
        continent_thicknessTextBox = QLineEdit()
        continent_thicknessTextBox.setText('1.5')
        continent_thicknessTextBox.textChanged.connect(lambda: self.textbox_changed('continent_thickness'))
        continent_colorTextBox = QLineEdit()
        continent_colorTextBox.setText('black')
        continent_colorTextBox.textChanged.connect(lambda: self.textbox_changed('continent_color'))

        # Map type check boxes
        maps = ['Cylindrical', 'EuroPP', 'LambertConformal', 'Mercator', 'Mollweide',\
                'NorthPoleStereographic', 'Orthographic', 'OSGB', 'Robinson',\
                'SouthPoleStereographic', 'UKCP']
        maps_short = ['cyl', 'EuroPP', 'lcc', 'merc', 'moll', 'npstere', 'ortho',\
                      'OSGB', 'robin', 'spstere', 'UKCP']

        for i in np.arange(len(maps)):
            setattr(self, maps[i] + 'CheckBox', QCheckBox(maps[i]))
            getattr(self, maps[i] + 'CheckBox').setChecked(False)
            getattr(self, maps[i] + 'CheckBox').clicked.connect(partial(self.map_type, maps_short[i]))

        self.CylindricalCheckBox.setChecked(True)




        self.lonminLabel = lonminLabel
        self.lonmaxLabel = lonmaxLabel
        self.latminLabel = latminLabel
        self.latmaxLabel = latmaxLabel
        self.boundinglatLabel = boundinglatLabel
        self.lon_0Label = lon_0Label
        self.other_map_typesLabel = other_map_typesLabel
        self.continent_attributesLabel = continent_attributesLabel
        self.continent_thicknessLabel = continent_thicknessLabel
        self.continent_colorLabel = continent_colorLabel

        self.lonminTextBox = lonminTextBox
        self.lonmaxTextBox = lonmaxTextBox
        self.latminTextBox = latminTextBox
        self.latmaxTextBox = latmaxTextBox
        self.boundinglatTextBox = boundinglatTextBox
        self.lon_0TextBox = lon_0TextBox
        self.continent_thicknessTextBox = continent_thicknessTextBox
        self.continent_colorTextBox = continent_colorTextBox

        # Set the layout
        vbox = QVBoxLayout()

        # Cylindrical options
        vbox.addWidget(self.CylindricalCheckBox)
        hbox_lons = QHBoxLayout()
        hbox_lons.addWidget(self.lonminLabel)
        hbox_lons.addWidget(self.lonminTextBox)
        hbox_lons.addWidget(self.lonmaxLabel)
        hbox_lons.addWidget(self.lonmaxTextBox)
        vbox.addLayout(hbox_lons)

        hbox_lats = QHBoxLayout()
        hbox_lats.addWidget(self.latminLabel)
        hbox_lats.addWidget(self.latminTextBox)
        hbox_lats.addWidget(self.latmaxLabel)
        hbox_lats.addWidget(self.latmaxTextBox)
        vbox.addLayout(hbox_lats)

        vbox.addWidget(HLine())

        # Polar options
        hbox_polar = QHBoxLayout()
        hbox_polar.addWidget(self.NorthPoleStereographicCheckBox)
        hbox_polar.addWidget(self.SouthPoleStereographicCheckBox)
        vbox.addLayout(hbox_polar)

        hbox_polar2 = QHBoxLayout()
        hbox_polar2.addWidget(self.boundinglatLabel)
        hbox_polar2.addWidget(self.boundinglatTextBox)
        vbox.addLayout(hbox_polar2)

        hbox_polar3 = QHBoxLayout()
        hbox_polar3.addWidget(self.lon_0Label)
        hbox_polar3.addWidget(self.lon_0TextBox)
        vbox.addLayout(hbox_polar3)

        vbox.addWidget(HLine())

        # Other maps
        vbox.addWidget(self.other_map_typesLabel)
        hbox_other1 = QHBoxLayout()
        hbox_other1.addWidget(self.EuroPPCheckBox)
        hbox_other1.addWidget(self.LambertConformalCheckBox)
        hbox_other1.addWidget(self.MercatorCheckBox)
        vbox.addLayout(hbox_other1)
        hbox_other2 = QHBoxLayout()
        hbox_other2.addWidget(self.OrthographicCheckBox)
        hbox_other2.addWidget(self.OSGBCheckBox)
        hbox_other2.addWidget(self.RobinsonCheckBox)
        hbox_other2.addWidget(self.UKCPCheckBox)
        vbox.addLayout(hbox_other2)
        vbox.addWidget(HLine())

        # Continent thickness and colour
        vbox.addWidget(self.continent_attributesLabel)
        hbox_thickness = QHBoxLayout()
        hbox_thickness.addWidget(self.continent_thicknessLabel)
        hbox_thickness.addWidget(self.continent_thicknessTextBox)
        hbox_thickness.addStretch(1)
        vbox.addLayout(hbox_thickness)
        hbox_color = QHBoxLayout()
        hbox_color.addWidget(self.continent_colorLabel)
        hbox_color.addWidget(self.continent_colorTextBox)
        hbox_color.addStretch(1)
        vbox.addLayout(hbox_color)


        # The buttons
        hbox_buttons = QHBoxLayout()
        vbox.addLayout(hbox_buttons)
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)


        self.map_help = None  # No external window yet.

        self.setWindowTitle('cfview map settings')
        self.setLayout(vbox)

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        # Set sensitivity of widgets
        self.set_sensitive()

        self.show()


    def map_type(self, text):

        maps = ['Cylindrical', 'EuroPP', 'LambertConformal', 'Mercator', 'Mollweide',\
                'NorthPoleStereographic', 'Orthographic', 'OSGB', 'Robinson',\
                'SouthPoleStereographic', 'UKCP']
        maps_short = ['cyl', 'EuroPP', 'lcc', 'merc', 'moll', 'npstere', 'ortho', 'OSGB', 
                      'robin', 'spstere', 'UKCP']


        for i in np.arange(len(maps)):
            getattr(self, maps[i] + 'CheckBox').setChecked(False)


        index = maps_short.index(text)
        getattr(self, maps[index] + 'CheckBox').setChecked(True)
        
        plotvars.proj = text

        self.set_sensitive()


    def textbox_changed(self, text):

        textbox = getattr(self, text + 'TextBox') 
        setattr(plotvars, text, textbox.text()) 


    def set_sensitive(self):
        # Set sensivity of plot widgets

        objs = [self.lonminLabel, self.lonminTextBox, self.lonmaxLabel,\
                self.lonmaxTextBox, self.latminLabel, self.latminTextBox,\
                self.latmaxLabel, self.latmaxTextBox,\
                self.boundinglatLabel,\
                self.boundinglatTextBox, self.lon_0Label, self.lon_0TextBox]

        colors = [plotvars.text_colour] * 15
        enabled = [False] * 15

        if plotvars.proj in ['cyl', 'EuroPP', 'lcc', 'merc', 'ortho', 'OSGB',\
                             'robin', 'UKCP']:
            enabled[0:7] = [True] * 8
            colors[8:] = [plotvars.text_colour_insensitive] * 6
        else:
            colors[0:7] = [plotvars.text_colour_insensitive] * 8
            enabled[8:] = [True] * 6

        for i in np.arange(len(objs)):
            obj = objs[i]
            obj.setEnabled(enabled[i])
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(colors[i]))
            obj.setPalette(pal)


    def reset(self):
        plotvars.proj = 'cyl'
        maps = ['Cylindrical', 'EuroPP', 'LambertConformal', 'Mercator', 'Mollweide',\
                'NorthPoleStereographic', 'Orthographic', 'OSGB', 'Robinson',\
                'SouthPoleStereographic', 'UKCP']
        for i in np.arange(len(maps)):
            getattr(self, maps[i] + 'CheckBox').setChecked(False)

        self.CylindricalCheckBox.setChecked(True)
        self.set_sensitive()

        plotvars.lonmin = -180
        plotvars.lonmax = 190
        plotvars.latmin = -90
        plotvars.latmax = 90
        self.lonminTextBox.setText('-180')
        self.lonmaxTextBox.setText('180')
        self.latminTextBox.setText('-90')
        self.latmaxTextBox.setText('90')
        


        plotvars.boundinglat = 0
        plotvars.lon_0 = 0
        self.boundinglatTextBox.setText('0')
        self.lon_0TextBox.setText('0')
        self.continent_thicknessTextBox.setText('1.5')
        self.continent_colorTextBox.setText('black')



    def help(self):

        if self.map_help is None:

            html = ""
            html += "<body><h2>Map options</h2>"
            html += "<h3>Cylindrical projection</h3>"
            html += "The default map projection is the cylindrical equidistant projection with "
            html += "the limits of -180 to 180 degrees in longitude and -90 to 90 degrees in "
            html += "latitude. Change these as appropriate to focus the plot onto the area of "
            html += "interest."
            html += "<h3>Polar stereographic plots</h3>"
            html += "Polar plots are focussed on either the north or south pole. The bounding "
            html += "latitude is the edge of the viewable latitudes and is set to the equator "
            html += "by default. The centre of the map domain is where the map is centred. By "
            html += "default this is 0 degrees which is the Greenwich meridian in the case of "
            html += "the north pole. For the South Pole plot this is usually changed to 180 "
            html += "degrees."
            html += "<h3>Map resolution</h3>"
            html += "The map resolution is set to a default of 110m. Higher resolutions such as "
            html += "50m and 10m take more time to plot.  50m means 1:50,000,000 and not 50 metre."

            html += "<h3>Continent color and thickness</h3>"
            html += "These default to 1.5 and black. Matplotlib "
            html += "named colors can be seen using the following Python code at the Python "
            html += "command prompt:<p>"

            html += "<b>import matplotlib<br>"
            html += "for name in matplotlib.colors.cnames: print(name)</b>"

            self.map_help = Help(html)

        self.map_help.show()
        
        
    def closeEvent(self, event):
        # Close self.parent.colour_scale_window
        self.parent.colour_scale_window = None
        self.close()


class Contour_window(QWidget):
    '''popup window for changing contour defaults'''

    def __init__(self, parent):
        super(Contour_window, self).__init__()
        
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up some buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        # Text box labels
        titleLabel = QLabel('title')
        zero_thickLabel = QLabel('zero_thick')
        contour_options = QLabel('contour options:')
        colorbar_options = QLabel('colorbar options:')
        title_options = QLabel('title options:')
        nlevsLabel = QLabel('Number of levels')
        
        # Text boxes
        titleTextBox = QLineEdit()
        titleTextBox.setText('')
        titleTextBox.textChanged.connect(lambda: self.textbox_changed('title'))
        zero_thickTextBox = QLineEdit()
        zero_thickTextBox.setText('')
        zero_thickTextBox.textChanged.connect(lambda: self.textbox_changed('zero_thick')) 
        nlevsTextBox = QLineEdit()
        nlevsTextBox.setText(plotvars.nlevs)
        nlevsTextBox.textChanged.connect(lambda: self.textbox_changed('nlevs'))       


        types = ['fill', 'blockfill', 'blockfill_fast', 'lines', 'line_labels', 'colorbar', 'automatic', 'horizontal', 'vertical', 'titles']
        status = [plotvars.fill, plotvars.blockfill, plotvars.blockfill_fast, plotvars.lines, plotvars.line_labels, True, True, False, False, True]
        for i in np.arange(len(types)):
            setattr(self, types[i] + 'CheckBox', QCheckBox(types[i]))
            getattr(self, types[i] + 'CheckBox').setChecked(status[i])
            getattr(self, types[i] + 'CheckBox').clicked.connect(partial(self.contour_type, types[i]))


        self.titleLabel = titleLabel
        self.zero_thickLabel = zero_thickLabel
        self.titleTextBox = titleTextBox
        self.zero_thickTextBox = zero_thickTextBox
        self.nlevsTextBox = nlevsTextBox

        # Set the layout
        vbox = QVBoxLayout()

        vbox.addWidget(contour_options)
        hbox_types = QHBoxLayout()
        hbox_types.addWidget(self.fillCheckBox)
        hbox_types.addWidget(self.blockfillCheckBox)
        hbox_types.addWidget(self.blockfill_fastCheckBox)
        hbox_types.addWidget(self.linesCheckBox)
        hbox_types.addWidget(self.line_labelsCheckBox)
        vbox.addLayout(hbox_types)
        hbox_nlevs = QHBoxLayout()
        hbox_nlevs.addWidget(nlevsLabel)
        hbox_nlevs.addWidget(nlevsTextBox)
        vbox.addLayout(hbox_nlevs)
        
        vbox.addWidget(HLine())

        vbox.addWidget(colorbar_options)
        hbox_colourbar = QHBoxLayout()
        hbox_colourbar.addWidget(self.colorbarCheckBox)
        hbox_colourbar.addWidget(self.automaticCheckBox)
        hbox_colourbar.addWidget(self.horizontalCheckBox)
        hbox_colourbar.addWidget(self.verticalCheckBox)
        vbox.addLayout(hbox_colourbar)
        
        vbox.addWidget(HLine())       

        vbox.addWidget(title_options)
        hbox_titles = QHBoxLayout()
        hbox_titles.addWidget(self.titlesCheckBox)
        hbox_titles.addWidget(self.titleLabel)
        hbox_titles.addWidget(self.titleTextBox)
        vbox.addLayout(hbox_titles)


        # The buttons
        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        vbox.addLayout(hbox_buttons)


        self.contour_help = None  # No external window yet.

        self.setWindowTitle('cfview contour settings')
        self.setLayout(vbox)

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)


        self.show()


    def contour_type(self, text):
        cbox = getattr(self, text + 'CheckBox')
        val = cbox.isChecked()

        if text == 'fill' and val:
            plotvars.blockfill = False
            plotvars.blockfill_fast = False
            self.blockfillCheckBox.setChecked(False)
            self.blockfill_fastCheckBox.setChecked(False)

        if text == 'blockfill' and val:
            plotvars.fill = False
            plotvars.blockfill_fast = False
            self.fillCheckBox.setChecked(False)
            self.blockfill_fastCheckBox.setChecked(False)
                        
        if text == 'blockfill_fast' and val:
            plotvars.fill = False
            plotvars.blockfill = False
            self.fillCheckBox.setChecked(False)           
            self.blockfillCheckBox.setChecked(False)

        if text == 'lines' and not val:
            plotvars.line_labels = False
            self.line_labelsCheckBox.setChecked(False)

        if text == 'lines' and val:
            plotvars.line_labels = True
            self.line_labelsCheckBox.setChecked(True)

        if text == 'automatic':
            plotvars.colorbar_orientation = 'None'
            self.horizontalCheckBox.setChecked(False)
            self.verticalCheckBox.setChecked(False)

        if text == 'horizontal':
            plotvars.colorbar_orientation = 'horizontal'
            self.automaticCheckBox.setChecked(False)
            self.verticalCheckBox.setChecked(False)

        if text == 'vertical':
            plotvars.colorbar_orientation = 'vertical'
            self.automaticCheckBox.setChecked(False)
            self.horizontalCheckBox.setChecked(False)

        if self.colorbarCheckBox.isChecked():
            color = plotvars.text_colour
            enabled = True
        else:
            color = plotvars.text_colour_insensitive
            enabled = False
            
        if self.titlesCheckBox.isChecked():
            plotvars.titles = True
        else:
            plotvars.titles = False
        print('plotvars.titles is ', plotvars.titles)
            
            
            

        objs = [self.automaticCheckBox, self.horizontalCheckBox, self.verticalCheckBox]
        for obj in objs:
            obj.setEnabled(enabled)
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(color))
            obj.setPalette(pal)


        setattr(plotvars, text, val) 


    def reset(self):
        # reset contour options
        self.fillCheckBox.setChecked(True)
        self.blockfillCheckBox.setChecked(False)
        self.blockfill_fastCheckBox.setChecked(False)
        self.linesCheckBox.setChecked(False)
        self.line_labelsCheckBox.setChecked(False)
        self.titleTextBox.setText('')
        self.nlevsTextBox.setText('')

        self.automaticCheckBox.setChecked(True)
        self.horizontalCheckBox.setChecked(False)
        self.verticalCheckBox.setChecked(False)
        self.titlesCheckBox.setChecked(True)
        
        plotvars.fill = True
        plotvars.blockfill = False
        plotvars.blockfill_fast = False
        plotvars.lines = False
        plotvars.line_labels = False
        plotvars.text = ''
        plotvars.colorbar_orientation = None
        plotvars.nlevs = False
        plotvars.titles = True

    def textbox_changed(self, text):

        textbox = getattr(self, text + 'TextBox') 
        setattr(plotvars, text, textbox.text())         
        
        
    def help(self):

        if self.contour_help is None:

            html = '<body><h2>Contour options</h2>'
            html += 'Contour options of fill and blockfill are mutually exclusive.  If a '
            html += 'grid with lots of points in x and y is plotted then the blockfill '
            html += 'grid will not be readily visible until the plot is zoomed in<p>'
            html += 'blockfill_fast uses the matplotlib pcolormesh blockfill method which can produce '
            html += 'different results from blackfill for low resolution data and for higher latitudes.'
            html += 'It is though much faster for higher resolution data.<p>'
            html += 'The number of levels is an integer and generally would have a value between 10 to 256)'
            html += 'Finer contour level selection is available in the Setup -> contour levels pop up window.<p>'
            self.contour_help = Help(html)

        self.contour_help.show()


    def closeEvent(self, event):
        # Close self.parent.contour_window
        self.parent.contour_window = None
        self.close()



class TableModel(QAbstractTableModel):

    def __init__(self, data, x, y, ndecs, datamin, datamax, datamin_colour, datamax_colour):
        super(TableModel, self).__init__()
        self._data = data
        self._x = x
        self._y = y
        self._ndecs = ndecs
        self._datamin = datamin
        self._datamax = datamax
        self._datamin_colour = datamin_colour
        self._datamax_colour = datamax_colour


    def data(self, index, role): 


        # Align table text to the right
        #if role == Qt.TextAlignmentRole and index.column() > 1:
        #     https://stackoverflow.com/questions/38150196/how-to-align-column-of-qtableview
        #     print('index.column to right' + str(index.column()))
        #     return Qt.AlignRight
        #return super(TableModel, self).data(index, role)


        try:
            v = float(self._datamax)
            if role == Qt.BackgroundRole and  self._data[index.row(), index.column()] >= float(self._datamax):
                return QColor(self._datamax_colour)
        except:
            pass

        try:
            v = float(self._datamin)
            if role == Qt.BackgroundRole and  self._data[index.row(), index.column()] <= float(self._datamin):
                return QColor(self._datamin_colour)
        except:
            pass


        if role == Qt.DisplayRole:
            value = self._data[index.row(), index.column()]
            
            if ma.is_masked(value):
                return ''
                
            if self._ndecs == 0:
                return str(int(value))
            else:
                return str(round(value, self._ndecs))
                 

    def rowCount(self, index):
        return self._data.shape[0]

    def columnCount(self, index):
        return self._data.shape[1]

    def headerData(self, section, orientation, role):

        if role != Qt.DisplayRole:
            return QVariant()

        if orientation == Qt.Horizontal:
            xlabels = []
            for ix in np.arange(len(self._x)):
                xlabels.append(str(self._x[ix]))
            return xlabels[section]
                
        if orientation == Qt.Vertical:
            ylabels = []
            for iy in np.arange(len(self._y)):
                ylabels.append(str(self._y[iy]))
            return ylabels[section]
    



class Data_window(QWidget):
    '''popup window for viewing data'''

    def __init__(self, parent):
        super(Data_window, self).__init__()
        self.parent = parent
        self.initUI()

    def initUI(self):

        # Set up some buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        plotButton = QPushButton("Plot selected cells")
        plotButton.clicked.connect(self.plot)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)
        
        # Plot type dropdown
        data_ComboBox = QComboBox()
        f = plotvars.fields[plotvars.index_number]
        dims = Cf_funcs.find_dim_names(f)
        
        options = []
        
        for i in np.arange(len(dims)):     
            for j in np.arange(len(dims)):    
                xtype = dims[i]
                if xtype[:19] == 'dimensioncoordinate':
                    xtype = 'dim' + xtype[19:]
                
                if len(dims) > 1:
                    ytype = dims[j]
                    if ytype[:19] == 'dimensioncoordinate':
                        ytype = 'dim' + ytype[19:]                    
                    
                    if xtype != ytype:
                        if xtype == 'Y' and ytype == 'X':
                            pass
                        else:
                            options.append(xtype + '-' + ytype)
                
                else:
                    options.append(xtype)        
                        
        if plotvars.dim_names[0] == 'ax0':
            if plotvars.dim_names[1] != 'ax1':
                print('Not enough axes to make a data view')
                return
                
            axes = []
            for i in np.arange(len(f.get_data_axes())):
                axes.append('ax' + str(i))
                    
            for i in np.arange(len(axes)):     
                for j in np.arange(len(axes)):    
                    xtype = axes[i]
                    ytype = axes[j]
                    if xtype != ytype:
                        options.append(xtype + '-' + ytype)
                        
            
            
                    
        data_ComboBox.addItems(options)
        data_ComboBox.currentTextChanged.connect(self.view_changed)
        self.data_ComboBox = data_ComboBox

        # Text box labels
        data_selectorLabel = QLabel('Data selection:')
        colouringLabel = QLabel('Data colouring:')
        ndecsLabel = QLabel('Number of decimal places: 2')
        column_widthLabel = QLabel('Column width: 10')
        text_sizeLabel = QLabel('Text size: ' + str(plotvars.fontsize))
        dataminLabel = QLabel('Minimum')
        datamin_colourLabel = QLabel('Minimum Colour')
        datamaxLabel = QLabel('Maximum')
        datamax_colourLabel = QLabel('Maximum Colour')
        titlesLabel = QLabel('')

        # Text boxes
        dataminTextBox = QLineEdit()
        dataminTextBox.setText('')
        dataminTextBox.textChanged.connect(self.value_changed)
        datamaxTextBox = QLineEdit()
        datamaxTextBox.setText('')
        datamaxTextBox.textChanged.connect(self.value_changed)
        datamin_colourTextBox = QLineEdit()
        datamin_colourTextBox.setText('blue')
        datamin_colourTextBox.textChanged.connect(self.value_changed)
        datamax_colourTextBox = QLineEdit()
        datamax_colourTextBox.setText('red')
        datamax_colourTextBox.textChanged.connect(self.value_changed)

        # ndecs and text size sliders
        ndecsSlider = QSlider(Qt.Horizontal)
        ndecsSlider.setMinimum(0)
        ndecsSlider.setMaximum(9)
        ndecsSlider.setValue(2)
        ndecsSlider.valueChanged.connect(self.value_changed)

        text_sizeSlider = QSlider(Qt.Horizontal)
        text_sizeSlider.setMinimum(1)
        text_sizeSlider.setMaximum(30)
        text_sizeSlider.setValue(plotvars.fontsize)
        text_sizeSlider.valueChanged.connect(self.value_changed)
        
        column_widthSlider = QSlider(Qt.Horizontal)
        column_widthSlider.setMinimum(1)
        column_widthSlider.setMaximum(30)
        column_widthSlider.setValue(10)        
        column_widthSlider.valueChanged.connect(self.set_column_width)
        
        self.width = 10
        self.ndecsLabel = ndecsLabel
        self.ndecsSlider = ndecsSlider

        self.text_sizeLabel = text_sizeLabel
        self.text_sizeSlider = text_sizeSlider
        
        self.column_widthLabel = column_widthLabel
        self.column_widthSlider = column_widthSlider       
        
        self.dataminLabel = dataminLabel
        self.datamaxLabel = datamaxLabel
        self.datamin_colourLabel = datamin_colourLabel
        self.datamax_colourLabel = datamax_colourLabel

        self.dataminTextBox = dataminTextBox
        self.datamaxTextBox = datamaxTextBox
        self.datamin_colourTextBox = datamin_colourTextBox
        self.datamax_colourTextBox = datamax_colourTextBox

        # Set the title label in self
        self.titlesLabel = titlesLabel
        
        # vbox for titles
        vbox_titles = QVBoxLayout()
        vbox_titles.setContentsMargins(0,0,0,0)
        vbox_titles.setSpacing(0)
        self.vbox_titles = vbox_titles


        # Add the table     
        table = QTableView()
        self.table = table
        

        Populate_table(self)
        
        # Align column labels to the left
        table.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
        

        vbox = QVBoxLayout()
        
        vbox.addLayout(vbox_titles)

        
        
        vbox.addWidget(titlesLabel)

        vbox.addWidget(table)

        data_selectors = QHBoxLayout()
        data_selectors.addWidget(data_selectorLabel)
        data_selectors.addWidget(data_ComboBox)
        data_selectors.addStretch(1)
        vbox.addLayout(data_selectors)

        sliders_hbox = QHBoxLayout()
        sliders_hbox.addWidget(ndecsLabel)
        sliders_hbox.addWidget(ndecsSlider)
        sliders_hbox.addWidget(text_sizeLabel)
        sliders_hbox.addWidget(text_sizeSlider)
        sliders_hbox.addWidget(column_widthLabel)
        sliders_hbox.addWidget(column_widthSlider)
        
        vbox.addLayout(sliders_hbox)

        datacolours_hbox = QHBoxLayout()
        datacolours_hbox.addWidget(colouringLabel)
        datacolours_hbox.addWidget(dataminLabel)
        datacolours_hbox.addWidget(dataminTextBox)
        datacolours_hbox.addWidget(datamin_colourLabel)
        datacolours_hbox.addWidget(datamin_colourTextBox)
        datacolours_hbox.addWidget(datamaxLabel)
        datacolours_hbox.addWidget(datamaxTextBox)
        datacolours_hbox.addWidget(datamax_colourLabel)
        datacolours_hbox.addWidget(datamax_colourTextBox)
        vbox.addLayout(datacolours_hbox)

        # The buttons
        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(plotButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        vbox.addLayout(hbox_buttons)


        self.data_help = None  # No external window yet.

        self.setWindowTitle('cfview view data')
        self.setLayout(vbox)

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)


        self.show()


    def view_changed(self, value):
        Populate_table(self)

    def plot(self):
        selected = self.table.selectionModel().selectedIndexes()

        if selected:
            con_args ={}
            xvals = []
            yvals = []
            for item in selected:
                 xvals.append(item.column())
                 yvals.append(item.row())

            minx = min(xvals)
            maxx = max(xvals)
            miny = min(yvals)
            maxy = max(yvals)
            
            
            mycoords = Cf_funcs.find_dim_names(self.field)
            for i in np.arange(len(mycoords)):
               if mycoords[i][:19] == 'dimensioncoordinate':
                   mycoords[i] = 'dim' + mycoords[i][19:]
            
            mytypes = self.data_ComboBox.currentText().split('-')
            if len(mytypes) == 2:
                myx = mytypes[0]
                myy = mytypes[1]
                
                if mycoords.index(myx) > mycoords.index(myy):
                    minx, maxx, miny, maxy = miny, maxy, minx, maxx
                    con_args['swap_axes'] = True 

            
            # Contour plot
            if maxx > minx and maxy > miny:

                if plotvars.fill and not plotvars.lines:
                    con_args['lines'] = False
                if not plotvars.fill and plotvars.lines:
                    con_args['fill'] = False
                    con_args['lines'] = True

                if plotvars.blockfill and not plotvars.lines:
                    con_args['blockfill'] = True
                    con_args['lines'] = False

                if plotvars.blockfill and plotvars.lines:
                    con_args['blockfill'] = True
                    con_args['lines'] = True
                    
                if plotvars.blockfill_fast and not plotvars.lines:
                    con_args['blockfill_fast'] = True
                    con_args['lines'] = False

                if plotvars.blockfill_fast and plotvars.lines:
                    con_args['blockfill_fast'] = True
                    con_args['lines'] = True                   
                    
                if plotvars.lines and not plotvars.line_labels:
                    con_args['line_labels'] = False

                if plotvars.titles:
                    con_args['titles'] = True

                if plotvars.contour_type[0] == 't':
                    con_args['swap_axes'] = True 

                cfp.con(self.field[miny:maxy, minx:maxx], **con_args)
                
                
            idims = len(self.field.get_data_axes())
                
            # Line plots
            if maxx > minx and miny == maxy:
                if idims == 2:
                    cfp.lineplot(self.field[miny, minx:maxx], titles=True)
                elif idims == 1:
                    cfp.lineplot(self.field[minx:maxx], titles=True)
                else:
                    print('view data - lineplot issue - ndims is not 1 or 2')    
                                   
            if maxx == minx and maxy > miny:
                if idims == 2:
                    cfp.lineplot(self.field[miny:maxy, minx], titles=True)
                elif idims == 1:
                    cfp.lineplot(self.field[miny:maxy], titles=True)
                else:
                    print('view data - lineplot issue - ndims is not 1 or 2')
                    
            if minx == maxx and miny == maxy:
                print('\n\nPloting error - only a single point selected\n\n')                
                

        else:
            print('\n\nError - No data selected\n\n')






    def reset(self):
        # Reset data viewing values

        self.ndecsSlider.setValue(2)
        self.column_widthSlider.setValue(10)
        self.column_widthLabel.setText('Column width: 10')
        self.text_sizeSlider.setValue(plotvars.fontsize)
        self.datamin_colourTextBox.setText('blue')
        self.datamax_colourTextBox.setText('red')
        self.dataminTextBox.setText('')
        self.datamaxTextBox.setText('')
        self.data_ComboBox.setCurrentIndex(0)



    def set_column_width(self, text):
        # Set column width
        
        width = self.column_widthSlider.value()
        self.column_widthLabel.setText('Column width: ' + str(width))
        self.width = width
        for i in np.arange(len(self.x)):
            self.table.setColumnWidth(i, width*10)


    def value_changed(self, text):
        # reset the table based on the text boxes and ndecs slider

        datamin = self.dataminTextBox.text()
        datamax = self.datamaxTextBox.text()
        datamin_colour = self.datamin_colourTextBox.text()
        datamax_colour = self.datamax_colourTextBox.text()

        ndecs = int(self.ndecsSlider.value())
        self.ndecsLabel.setText('Number of decimal places: ' + str(ndecs))
        
        fontsize = int(self.text_sizeSlider.value())
        self.text_sizeLabel.setText('Text size: ' + str(fontsize))    
        

        model = TableModel(self.data, self.x, self.y, ndecs, datamin, datamax, datamin_colour, datamax_colour)
        self.table.setModel(model)

        custom_font = QFont(plotvars.font, int(self.text_sizeSlider.value()))
        self.table.setFont(custom_font)


        


    def help(self):

        if self.data_help is None:

            html = ""
            html += "<body><h2>Data viewing</h2>"
            html += "Data viewing is based on the dimensions specied in the contour plot type with the default being x-y.  "
            html += "The field name, selected dimensions and plot type are displayed above the table of data."
            html += "Options to select the number of decimal places, text size and column width are provided to allow "
            html += "better visibilty of various data types.<p>"
            html += "Data colouring options are available for colouring cells that have data above or below a certain threshold.<p>"
            html += "To plot selected cells a line or area of data must be selected.<p>" 

            self.data_help = Help(html)

        self.data_help.show()


    def closeEvent(self, event):
        # Close self.parent.data_window
        self.parent.data_window = None
        self.close()




class Populate_table():
    '''Populate table in the data viewing widget'''

    def __init__(self, parent, initial=True):
        super(Populate_table, self).__init__()
        self.parent = parent
        self.initial = initial
        self.change_table()
        
        
    def change_table(self):
   
        # Set the field
        f = deepcopy(plotvars.fields[plotvars.index_number])

        
        mytypes = self.parent.data_ComboBox.currentText().split('-')
        if len(mytypes) == 2:
            myx = mytypes[0]
            myy = mytypes[1]
        else:
            myx = mytypes[0]
            myy = 'one dimensional data'
            
            
        if myx[:3] =='dim':
            myx = 'dimensioncoordinate' + myx[3:]
        if myy[:3] =='dim':
            myy = 'dimensioncoordinate' + myy[3:]           


        
        subspace_args = Cf_funcs.subspace_args(self.parent, plotvars.index_number, call_data=True)

        # Return if myx is unset
        if myx == '':
            return

        
        # Perform subspace
        f = f.subspace(**subspace_args)
        
        com_subspace, com_collapse = Cf_funcs.com_subspace_collapse(subspace_args)

        if len(com_collapse) > 0:
            f = f.collapse(com_collapse[1:-1])
        
        data = f.array.squeeze()
        
        
        
        if myx == 'T':
            x = f.coord(myx).dtarray
        else:
            x = f.coord(myx).array         
            
        if myy != 'one dimensional data':    
            if myy == 'T':
                y = f.coord(myy).dtarray    
            else:
                y = f.coord(myy).array    
        else:
            y = ['one dimensional data', 'one dimensional data']
        
        # Check axes and swap if necessary
        if myy != 'one dimensional data':
            mycoords = Cf_funcs.find_dim_names(f.squeeze())
            if mycoords[0][:19] == 'dimensioncoordinate':
                mycoords[0] = 'dim' + mycoords[0][19:]
            if mycoords[1][:19] == 'dimensioncoordinate':
                mycoords[1] = 'dim' + mycoords[1][19:]           
            
            if mycoords[0] == myy:
                data = np.flipud(np.rot90(data))
    
        else:
             data = np.tile(data, (2, 1))
        
        if len(x) <= 1 or len(y) <= 1:
            if len(x) < 1:
                print('\ndata viewing error - the length of the ' + myx + ' coordinate must be two or greater\n')
            if len(y) < 1:
                print('\ndata viewing error - the length of the ' + myy + ' coordinate must be two or greater\n')          
            data = np.zeros([2,2])
            x = ['no data returned', 'no data returned']
            y = ['no data returned', 'no data returned']

        
        datamin = self.parent.dataminTextBox.text()
        datamax = self.parent.datamaxTextBox.text()
        datamin_colour = self.parent.datamin_colourTextBox.text()
        datamax_colour = self.parent.datamax_colourTextBox.text()
        ndecs = int(self.parent.ndecsSlider.value())


        # Set the titles for the data
        dim_titles = cfp.generate_titles(f=f)

        # Delete all items in self.parent.vbox_titles
        if self.parent.vbox_titles.count() > 0:
            # Delete each widget in the layout one by one
            # We only have labels and hboxes
            while self.parent.vbox_titles.count():
                item = self.parent.vbox_titles.takeAt(0)

                # Remove single Qlabels
                if isinstance(item.widget(), QLabel):
                    item.widget().setParent(None)
                    self.parent.vbox_titles.removeItem(item)

                # Remove QHBoxLayouts
                if type(item) == QHBoxLayout:
                    for i in reversed(range(item.count())):
                        item_hbox = item.itemAt(i)
                        if item_hbox.widget() is not None:
                            item_hbox.widget().setParent(None)

        myfont = QFont(plotvars.font)
        myfont.setBold(True)
        
        
        titles = ['field name: ' + Cf_funcs.field_name(field=f)]
        for dim in dim_titles.split('\n'):
            titles.append(dim)
        
        
        for title in titles:
            index = title.find(':')
            #print('name' + title[0:index + 1] + 'value' + title[index + 1:])
            if title[0:index + 1] != '':
                myvar = title[0:index + 1]
                if myvar[:19] == 'dimensioncoordinate':
                    myvar = 'dim' + myvar[19:]
                var = QLabel(myvar)
                var.setFont(myfont)
                value = QLabel(title[index + 1:])
                myhbox = QHBoxLayout()
                myhbox.addWidget(var)
                myhbox.addWidget(value)
                myhbox.addStretch(1)
                self.parent.vbox_titles.addLayout(myhbox)
        

        
        model = TableModel(data, x, y, ndecs, datamin, datamax, datamin_colour, datamax_colour)


        self.parent.table.setModel(model)
        

        self.parent.field = f.squeeze()
        self.parent.data = data
        self.parent.x = x
        self.parent.y = y



class Names_window(QWidget):
    '''popup window for changing data name defaults'''

    def __init__(self, parent):
        super(Names_window, self).__init__()
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up some buttons
        saveButton = QPushButton("Save")
        saveButton.clicked.connect(self.save)

        new_propertyButton = QPushButton("New property")
        new_propertyButton.clicked.connect(self.new_property)
        
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)
        
        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)


        # Set the layout
        vbox = QVBoxLayout()
        vbox_names = QVBoxLayout()
        
        vbox.addLayout(vbox_names)

        self.vbox = vbox
        self.vbox_names = vbox_names
        
        
        # Set the field
        field = plotvars.fields[plotvars.index_number]
        self.field = field

        # Make a copy in self for the reset
        self.field = deepcopy(field)

        # Populate the window with the field names
        Populate_names(self, initial=True)
        
        
        # Make a scrollable area for the names
        groupBox = QGroupBox('')
        groupBox.setLayout(vbox)
        scroll = QScrollArea()
        scroll.setWidget(groupBox)
        scroll.setWidgetResizable(True)
        vscroll = scroll.verticalScrollBar()
        container = QVBoxLayout()
        container.addWidget(scroll)


        # The buttons
        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(saveButton)
        hbox_buttons.addWidget(new_propertyButton)
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        container.addLayout(hbox_buttons)

        # No external window yet
        self.names_help = None
        

        self.setWindowTitle('properties')
        self.scroll = scroll
        self.vscroll = vscroll
        self.setLayout(container)

        
        self.new_property_window = None  # No external window yet.

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)


        self.show()






    def save(self):
        # Save field if any changes have been made

        # Initial check for change to vars that require a data reload
        reload = False
        data_changed = False
        for var in self.vardict:
            
            vals = self.vardict[var]
            mytype = vals[0]
            name = vals[1]
            val = vals[2]
            status = vals[3]
        
            if name in  ['_FillValue', 'missing_value', 'valid_min', 'valid_max', 'valid_range']:
                if status != 'U':
                    reload = True
        
            if status != 'U':
                data_changed = True
        
        # Exit if no data has changed
        if data_changed is False:
            print('\nNo data changed\n\n')
            return
            
            
        # Set the data source to be 'Changed'
        plotvars.data_source_list[plotvars.index_number] = 'Changed'
        
        # Set initial status of com
        com = 'import cf\n'
           
        if reload:
            com += "import numpy as np\n\n"
            
        com += "f = cf.read(" + plotvars.file_read_str + ")[" + str(plotvars.index_number) + "]\n\n"
                                        
        # A list of items to be deleted
        deletion_list = []
        
        
        # Main loop to change data and write code
        for var in self.vardict:
            
            vals = self.vardict[var]
            mytype = vals[0]
            name = vals[1]
            val = Data_check.test_type(vals[2])
            status = vals[3]

            # Strings for making code
            mytype_str = "'" + mytype + "'"
            if type(val) == str:
                val_str = "'" + str(val) + "'"
            else:
                val_str = str(val)
            name_str = "'" + name + "'"
            
            # Set masking data type values to be that of the data
            if name in  ['_FillValue', 'missing_value', 'valid_min', 'valid_max']:
                if self.field.dtype == 'float32':
                   val = np.float32(val)
                   val_str = 'np.float32(' + val_str + ')'
                if self.field.dtype == 'float64':
                   val = np.float64(val)  
                   val_str = 'np.float64(' + val_str + ')'
                   
            # Check data if valid_range is set can be used
            if name == 'valid_range':
                val = vals[2]
                print('valid_range ', val)
                if val[0] != '[' or val[-1] != ']' or val.count(',') != 1:
                    print('\nvalid_range error - must be a list with two values of form [val1, val2]\n')
                    return
                vstr = val[1:-1].split(',')
                if self.field.dtype == 'float32':
                    val = [np.float32(vstr[0]), np.float32(vstr[1])]
                    val_str = '[' + 'np.float32(' + vstr[0] + '), np.float32(' + vstr[1] + ')]'
                if self.field.dtype == 'float64':
                    val = [np.float64(vstr[0]), np.float64(vstr[1])]
                    val_str = '[' + 'np.float64(' + vstr[0] + '), np.float64(' + vstr[1] + ')]'
                   
                   
            if status in ['C', 'D']:
            
            
                if mytype == 'field':
                    myvar = plotvars.fields[plotvars.index_number]
                    myvar_str = 'f'
                else:
                    myvar = plotvars.fields[plotvars.index_number].coord(mytype)    
                    myvar_str = "f.coord('" + mytype + "')"
                    
            
                if status == 'C':                           
                    if name == 'netCDF name':
                        plotvars.fields[plotvars.index_number].nc_set_variable(val)
                        com += "f.nc_set_variable(" + val_str  + ")\n"    
                    else:
                        if name_str == "'units'":
                            if cf.Units(val_str[1:-1]).isvalid is False:
                                print('\nWarning - units of ' + val_str[1:-1] + ' are not recognised as CF units')
                                
                            if myvar.Units.equivalent(cf.Units(val_str[1:-1])):
                                myvar.units = val_str[1:-1]
                                com += myvar_str + ".units = " + val_str  + "\n"                        
                            else:
                                print('\nWarning - setting units to non-equivalent units')
                                myvar.override_units(val_str[1:-1], inplace=True)
                                com += myvar_str + ".override_units(" + val_str  + ", inplace=True)\n"                        
                        else:
                            myvar.set_property(name, val)
                            com += myvar_str + ".set_property(" + name_str  + ", " + val_str  + ")\n"                   

                        
                    # Reset the status field for this var
                    self.vardict[var][3] = 'U'         
                            
                if status == 'D':                    
                    if name_str == "'units'":
                        myvar.override_units(None, inplace=True)
                        com += myvar_str + ".override_units(None, inplace=True)\n"      
                    
                    else:
                        myvar.del_property(name)
                        com += myvar_str + ".del_property(" + name_str + ")\n"

                    if mytype == 'field':
                        deletion_list.append(mytype + "." + name)
                    else:
                        deletion_list.append("coord.('" + mytype + "')." + name)

                    # Reset the status field for this var
                    self.vardict[var][3] = 'D'

                    
        # Delete any vardict entries that have been assigned 'D' for deletion
        for item in deletion_list:
            self.vardict.pop(item)
               
        
        # Reload data if one of masking properties has changed
        if reload:
            plotvars.fields[plotvars.index_number].apply_masking(inplace=True)
            com += "\n# Apply  masking as one of "
            com += "_FillValue, missing_value, valid_min, valid_max, valid_range "
            com += "has changed\n"
            com += "f.apply_masking(inplace=True)\n"
               

        
        if plotvars.code_output:
            print('### Code to change the data is ###\n', com)

        # Reset the field title
        selected = self.parent.fieldlist.selectedItems()[0]
        title = selected.text()
        new_name = Cf_funcs.field_name(plotvars.fields[plotvars.index_number])
        title_new = title.replace(title.split(' ')[-1], '') + new_name
        selected.setText(title_new)

        # Call fieldnumber to reset the field names
        # in case dim2 -> X etc
        self.parent.fieldnumber()
        
        
        
        
        

    def new_property(self):
        # Pop-up for a new property 

        if self.new_property_window is None:
            self.new_property_window = New_property_window(self)
            self.new_property_window.show()
 
        
    def reset(self):
        # Reset the main Name window based on the field
        
        # Delete all widgets in self.vbox_names
        if self.vbox_names.count() > 2:
            # Delete each widget in the layout one by one
            # We only have labels and hboxes
            while self.vbox_names.count():
                item = self.vbox_names.takeAt(0)
                
                # Remove single Qlabels
                if isinstance(item.widget(), QLabel):
                    item.widget().setParent(None)
                    self.vbox_names.removeItem(item)
                    
                # Remove QHBoxLayouts
                if type(item) == QHBoxLayout:
                    for i in reversed(range(item.count())):
                        item_hbox = item.itemAt(i)
                        item_hbox.widget().setParent(None)
        
        Populate_names(self, initial=True)

    def help(self):

        if self.names_help is None:

            html = "<body><h2>Properties</h2>"
            html += "This window shows the properties for the field and dimensions as "
            html += "editable values.  Properties can also be deleted by pressing the 'X' button "
            html += "next to the property.  Changes to the in memory data are only made when the 'Save' "
            html += "button is pressed.  When the 'Reset' button is pressed the values will revert to "
            html += "the initial data values or the saved data values whichever are the most recent.<P>"
            html += "To save the data to disk use the File -> Save option from the main cfview menu<P>" 
          

            self.names_help = Help(html)

        self.names_help.show()


    def textbox_changed(self, text):
        ''' Set value based on any text box change'''

        if text[:5] == 'field':
            var = 'field.' + text[5:]

        if text[:19] == 'dimensioncoordinate':
            var = "coord.('" + text[:20] + "')." + text[20:]
        
        if text[0] in ['X', 'Y', 'Z', 'T']:
            var = "coord.('" + text[0] + "')." + text[1:]
            
        # Change value and status to changed - 'C'
        self.vardict[var][2] = getattr(self, text).text()
        self.vardict[var][3] = 'C'    


        


    def delete(self, text):

        name = None

        if text.split(' ')[0] in ['X', 'Y', 'Z', 'T']:
           name = "coord.('" + text[0] + "')." + text[2:] 
            
        if text[: 19] == 'dimensioncoordinate':
            name = "coord.('" + text[:20] + "')." + text[21:] 
            
        if text[: 19] == 'auxiliarycoordinate':
            name = "coord.('" + text[:20] + "')." + text[21:]            
        
        if text.split(' ')[0] == 'field':
            name = 'field.' + text[6:]
            
        if name is not None:
            delete_item = 'field.' + text
            self.vardict[name][3] = 'D'
        else:
            print('Delete property - name not found - ', name)
            return
        

        
        # Delete all widgets in self.vbox_names
        if self.vbox_names.count() > 2:
            # Delete each widget in the layout one by one
            # We only have labels and hboxes
            while self.vbox_names.count():
                item = self.vbox_names.takeAt(0)
                
                # Remove single Qlabels
                if isinstance(item.widget(), QLabel):
                    item.widget().setParent(None)
                    self.vbox_names.removeItem(item)
                    
                # Remove QHBoxLayouts
                if type(item) == QHBoxLayout:
                    for i in reversed(range(item.count())):
                        item_hbox = item.itemAt(i)
                        item_hbox.widget().setParent(None)
                    

        # Populate the window with the changed field names
        Populate_names(self, initial=False)
        

        
    def closeEvent(self, event):
        # Close self.parent.names_window
        self.parent.names_window = None
        self.close()
        
        

class Populate_names():
    '''Populate names into the name viewing widget'''

    def __init__(self, parent, initial=True):
        super(Populate_names, self).__init__()
        self.parent = parent
        self.initial = initial
        self.add_names()
        
        
    def add_names(self):
   
        # Set the field
        field = plotvars.fields[plotvars.index_number]

        if self.initial:
        
            # Dictionary of names
            # vardict has the following variables:
            # field. or dimension
            # variable name
            # value
            # modification status - 'U' = unchanged, 'C' = changed, 'D' = deleted
            vardict = {}

        
            # Work out the dimension names
            # dims = of form X, Y, dimensioncoordinate2 etc
            # dim_names = of form X, Y, dim2 etc

            dims = Cf_funcs.find_all_dims()
            dnames = dims

            # Field netCDF name
            field_nc_name = field.nc_get_variable(False)
            if field_nc_name:
                vardict['field.netCDF name'] = ['field', 'netCDF name', field_nc_name, 'U']
                    
            # Field properties
            names = sorted(field.properties())
            for name in names:
                vardict["field." + name] = ['field', name, field.properties()[name], 'U']
        
            # Dimension properties
            for i in np.arange(len(dims)):
                dim = dims[i]
                dname = dnames[i]
                if field.has_construct(dim):
                    coord = field.coord(dim)
                    nc_name = coord.nc_get_variable(False)
        
                    # Add dimension netCDF name
                    if nc_name:
                        name = 'netCDF name'
                        val_label = 'coord_' + dname + '_' + name
                        label = dname + ' ' + name
                        vardict["coord.('"+ dim + "')." + name] = [dim, 'netCDF name', nc_name, 'U']
                 
                 
                    # Add dimension properties
                    dim_names = sorted(coord.properties())
                    for name in dim_names:
                        val = coord.get_property(name)
                        val_label = 'coord_' + dname + '_' + name
                        label = dname + ' ' + name
                        vardict["coord.('"+ dim + "')." + name] = [dim, name, val, 'U']
                
        else:
            # Retrieve the preformed vardict
            vardict =  self.parent.vardict

            # Rearrange vardict so that it is ordered by field and then dimensions
            # Use a set of dictionaries 
            dicts = [{}]
            
            dims = Cf_funcs.find_all_dims()
            for idict in np.arange(len(dims)):
                dicts.append({})

            # Field dict
            if 'field.netCDF name' in dicts[0]:
                dicts[0]['field.netCDF name'] = vardict['field.netCDF name']
            for var in vardict:
                vals = vardict[var]
                mytype = vals[0]
                if mytype == 'field' and var != 'field.netCDF name':
                    dicts[0][var] = vardict[var]
                
            # Dimension dicts
            for j in np.arange(len(dims)):
                for var in vardict:
                    vals = vardict[var]
                    mydim = vals[0]
                              
                    if mydim in ['X', 'Y', 'Z', 'T'] or mydim[:19] in ['dimensioncoordinate', 'auxiliarycoordinate']:
                        if mydim == dims[j]:
                            dicts[j + 1][var]  = vardict[var]
            
            # Reassemble and order so that the netCDF name is at the top of the lists of field and dimensions
            new_dict = {}
            
            for idict in np.arange(len(dims) + 1):
                dicts[idict] = dict(sorted(dicts[idict].items())) 

                # Add the netCDF name at the top of the field and dimension lists
                first_key = list(dicts[idict].keys())[0]
                
                vals = dicts[idict][first_key]
                if vals[0] == 'field':
                    netcdf_name = 'field.netCDF name'
                else:
                    netcdf_name = "coord.('" + dicts[idict][first_key][0] + "').netCDF name"
                        
                if netcdf_name in new_dict:
                    new_dict[netcdf_name]= dicts[idict][netcdf_name]
                     
                for var in dicts[idict]:
                    if dicts[idict][var][1] != 'netCDF name':                        
                        new_dict[var]= dicts[idict][var]
                                                

            # Store new vardict
            self.vardict = new_dict
            vardict = self.vardict
            
            
            # Delete all items in self.parent.vbox_names
            if self.parent.vbox_names.count() > 2:
                # Delete each widget in the layout one by one
                # We only have labels and hboxes
                while self.parent.vbox_names.count():
                    item = self.parent.vbox_names.takeAt(0)
                
                    # Remove single Qlabels
                    if isinstance(item.widget(), QLabel):
                        item.widget().setParent(None)
                        self.parent.vbox_names.removeItem(item)
                    
                    # Remove QHBoxLayouts
                    if type(item) == QHBoxLayout:
                        for i in reversed(range(item.count())):
                            item_hbox = item.itemAt(i)
                            item_hbox.widget().setParent(None)
            

        # Populate self.parent.vbox_names based on vardict
        i = 0
        dim_present = ''
        data_dim_counter = 0
        data_aux_counter = 0
        data_dim_extra_counter = 0
        data_dims = []
        
        for var in vardict:
        
            vals = vardict[var]

            mytype = vals[0]
            name = vals[1]
            val = vals[2]
            status = vals[3]
            

            val_label = vals[0] + name
            mydim = ''
            

            if mytype[:19] == 'dimensioncoordinate':
                mydim = 'dim' + mytype[19:]
            if mytype[:19] == 'auxiliarycoordinate':
                mydim = 'aux' + mytype[19:]                
            if mytype in ['X', 'Y', 'Z', 'T']:
                mydim = mytype
                

            label = name + ':'
            
            # Add headers
            if i == 0:
                title = QLabel('Field properties')
                getattr(self.parent, 'vbox_names').addWidget(title)                
                myFont=QFont(plotvars.font)
                myFont.setBold(True)
                title.setFont(myFont)                
                
            if mydim in ['X', 'Y', 'Z', 'T'] or mydim[:3] == 'dim':
                if mydim not in data_dims:
                    data_dims.append(mydim)
                data_dim_counter += 1
                if data_dim_counter == 1:
                    title = QLabel('\nField data dimension axes')
                    getattr(self.parent, 'vbox_names').addWidget(title)                
                    myFont=QFont(plotvars.font)
                    myFont.setBold(True)
                    title.setFont(myFont)
          
            # Add in code for Additional dimension axes
            if len(data_dims) > len(field.get_data_axes()):
                if data_dim_extra_counter == 0:
                    data_dim_extra_counter += 1
                    title = QLabel('\nAdditional dimension axes')
                    getattr(self.parent, 'vbox_names').addWidget(title)                
                    myFont=QFont(plotvars.font)
                    myFont.setBold(True)
                    title.setFont(myFont)
            
            
                        
            if mydim[:3] == 'aux':
                data_aux_counter += 1
                if data_aux_counter == 1:
                    title = QLabel('\nField auxiliary coordinates')
                    getattr(self.parent, 'vbox_names').addWidget(title)                
                    myFont=QFont(plotvars.font)
                    myFont.setBold(True)
                    title.setFont(myFont)            
                

            if mydim in ['X', 'Y', 'Z', 'T'] or mydim[:3] in ['dim', 'aux']:
                
                if mydim != dim_present:
                    dim_present = mydim
                    getattr(self.parent, 'vbox_names').addWidget(QLabel(''))
                    getattr(self.parent, 'vbox_names').addWidget(QLabel('Dimension ' + mydim + ' properties'))
                
                
            # Add if status is not deleted
            if status != 'D':
            
                myhbox = QHBoxLayout()
                
                # Add label
                myhbox.addWidget(QLabel(label))
                
                # Add editable value
                val_edit = QLineEdit()
                val_edit.setText(str(val))
                val_edit.setCursorPosition(0)
                val_edit.textChanged.connect(partial(self.parent.textbox_changed, val_label))
                setattr(self.parent, val_label, val_edit)
                myhbox.addWidget(val_edit)
                
                # Property delete button 
                # Do not add for netCDF name properties
                if label[-12:-1] != 'netCDF name':
                    my_delete_button = QPushButton('X')
                    my_delete_button.clicked.connect(partial(self.parent.delete, mytype + ' ' + name))
                    myhbox.addWidget(my_delete_button)
                
                # Add to box_names
                self.parent.vbox_names.addLayout(myhbox)
            
            i = i + 1
            
        # Align to the top of the properties window
        if not self.initial:
            self.parent.vscroll.setSliderPosition(0)
            
            
        # Set vardict in the parent
        self.parent.vardict = vardict






class New_property_window(QWidget):
    '''Popup window for adding a new property'''

    def __init__(self, parent):
        super(New_property_window, self).__init__()
        self.parent = parent
        self.initUI()


    def initUI(self):

        applyButton = QPushButton("Apply")
        applyButton.clicked.connect(self.apply)
        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)
        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)
        
        dims = Cf_funcs.find_all_dims()
        
        # Make radio buttons
        rbuttons = ['field']
        for dim in dims:
            if len(dim) == 1:
                rbuttons.append(dim.lower())
            else:
                rbuttons.append(dim[:3] + dim[19:])

        # Make the radiobutons
        radiobuttons = []
        for rbutton in rbuttons:
            setattr(self, rbutton + 'RadioButton', QRadioButton(rbutton))
            getattr(self, rbutton + 'RadioButton').clicked.connect(partial(self.button_changed, rbutton))
            radiobuttons.append(getattr(self, rbutton + 'RadioButton'))
        self.radiobuttons = radiobuttons

        # Set the inital selection
        self.varname = 'field'
        self.fieldRadioButton.setChecked(True)        

        nameLabel = QLabel('New property')
        nameTextbox = QLineEdit()

        valueLabel = QLabel('Value')
        valueTextbox = QLineEdit()


        vbox = QVBoxLayout()

        hbox_name = QHBoxLayout()
        hbox_name.addWidget(nameLabel)
        hbox_name.addWidget(nameTextbox)
        vbox.addLayout(hbox_name)

        hbox_value = QHBoxLayout()
        hbox_value.addWidget(valueLabel)
        hbox_value.addWidget(valueTextbox)
        vbox.addLayout(hbox_value)

        hbox_radio_buttons = QHBoxLayout()
        for radiobutton in radiobuttons:
            hbox_radio_buttons.addWidget(radiobutton)
        vbox.addLayout(hbox_radio_buttons)

        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(applyButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)

        vbox.addLayout(hbox_buttons)

        self.setLayout(vbox)
        self.nameTextbox = nameTextbox
        self.valueTextbox = valueTextbox
        self.new_property_help =  None  # No external window yet.

        palette = QPalette()    
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))    
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))    
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        # Set font 
        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        self.setFixedWidth(600)
        self.setWindowTitle('cfview - create new properties')

        self.show()

    def apply(self):

        name = self.nameTextbox.text()
        if len(name) == 1:
            name = 'dimension ' + name

        val = Data_check.test_type(self.valueTextbox.text())
        
        if name == '':
            print('Error - no property specified')
            return

        #if self.fieldRadioButton.isChecked():
        varname = self.varname
        
        if varname == 'field':
            myvar = "field." + name
            mytype = 'field'
        
        if varname in ['x', 'y', 'z', 't']:
            myvar = "coord.('" + varname.upper() + "')." + name
            mytype = varname.upper()
        if varname[:3] == 'dim':
            myvar = "coord.(dimensioncoordinate" + varname[3:] + "')." + name
            mytype = 'dimensioncoordinate' + varname[3:]
        if varname[:3] == 'aux':
            myvar = "coord.('auxiliarycoordinate" + varname[3:] + "')." + name   
            mytype = 'auxiliarycoordinate' + varname[3:]
        

        if myvar in self.parent.vardict:
            print('Error - ' + self.varname + name + ' already exists in the data')
            return

        # Set in vardict
        self.parent.vardict[myvar] = [mytype, name, val, 'C']
        

        # Set the data source to be 'Changed'
        plotvars.data_source_list[plotvars.index_number] = 'Changed'


        # Populate the window with the changed field names
        Populate_names(self.parent, initial=False)


    def button_changed(self, text):
         
        self.varname = text       



    def help(self):

        if self.new_property_help is None:

            html = '<body><h2>Add a new field property</h2>'
            html += 'Add a new property and value for the field, dimension or auxiliary coordinate'


            self.new_property_help = Help(html)

        self.new_property_help.show()


    def closeEvent(self, event):
        # Close new_property window
        self.parent.new_property_window = None
        self.close()
        




class Transform_window(QWidget):
    '''popup window for transforming data'''


    def __init__(self, parent):
        super(Transform_window, self).__init__()
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set the layout
        vbox = QVBoxLayout()
        vbox_transform = QVBoxLayout()
        vbox.addLayout(vbox_transform)
        self.vbox = vbox
        self.vbox_transform = vbox_transform
        
        # Make a scrollable area for the transforms
        groupBox = QGroupBox('')
        groupBox.setLayout(vbox)
        scroll = QScrollArea()
        scroll.setWidget(groupBox)
        scroll.setWidgetResizable(True)
        container = QVBoxLayout()
        container.addWidget(scroll)

        # Collpase dimension and area widgets
        self.collapse_Labels = []  
        self.collapse_ComboBoxes = [] 

        collapse_types = ['Off', 'mean', 'minimum', 'maximum', 'variance', 'standard_deviation']
        
        for i in np.arange(plotvars.max_axes):
            self.collapse_ComboBoxes.append(QComboBox())
            self.collapse_ComboBoxes[i].addItems(collapse_types)
            self.collapse_ComboBoxes[i].setCurrentIndex(0)
            self.collapse_ComboBoxes[i].setVisible(False)
            self.collapse_ComboBoxes[i].activated.connect(partial(self.collapse_type, i))
            self.collapse_Labels.append(QLabel(''))
                       
            
        # Area collapse label and combobox
        self.collapse_areaLabel = QLabel('Area:')
        self.collapse_areaLabel.setVisible(False)    
        self.collapse_areaComboBox = QComboBox()
        self.collapse_areaComboBox.addItems(collapse_types)
        self.collapse_areaComboBox.setCurrentIndex(0)
        self.collapse_areaComboBox.activated.connect(partial(self.collapse_type, -1))
        self.collapse_areaComboBox.setVisible(False)     
        
        
        # Labels
        warningLabel1 = QLabel('Field has not got a recogised pair of longitudes and latitudes')
        warningLabel2 = QLabel('Not all data transform functionality is available\n')
        warningLabel1.setVisible(False)
        warningLabel2.setVisible(False)    
        
        
        collapseLabel = QLabel('Dimension collapse methods')
        anchorLabel = QLabel('Anchor in longitude:')
        interpolationLabel = QLabel('Interpolation')
        interpolation_methodLabel = QLabel('Interpolation method:')
        lonminLabel = QLabel('lonmin:')
        lonmaxLabel = QLabel('lonmax:')
        lonstepLabel = QLabel('lonstep:')
        latminLabel = QLabel('latmin:')
        latmaxLabel = QLabel('latmax:')
        latstepLabel = QLabel('latstep:')
        sourceLabel = QLabel('source field index:')
        destinationLabel = QLabel('destination field index:')
        computationLabel1 = QLabel('Field computation')
        computationLabel2 = QLabel('Type in the new field calculation, for example, to difference two fields:')
        computationLabel3 = QLabel('f[4] - f[2]')
        computationLabel4 = QLabel('field index 1 is u and field index 2 is v')
        computationLabel5 = QLabel('to get the wind magnitude in a new field:')
        computationLabel6 = QLabel('(f[1]**2 + f[2]**2) ** 0.5')

        # Text boxes
        anchorTextBox = QLineEdit()
        reverse_yTextBox = QLineEdit()
        lonminTextBox = QLineEdit()
        lonmaxTextBox = QLineEdit()
        lonstepTextBox = QLineEdit()
        latminTextBox = QLineEdit()
        latmaxTextBox = QLineEdit()
        latstepTextBox = QLineEdit()
        computationTextBox = QLineEdit()


        # Check boxes
        reverse_yCheckBox = QCheckBox("Reverse latitude axis")
        reverse_yCheckBox.setChecked(False)
        interp_regularCheckBox = QCheckBox("Regular grid")
        interp_regularCheckBox.setChecked(False)
        interp_regularCheckBox.clicked.connect(lambda: self.checkbox_changed('regular'))
        interp_to_anotherCheckBox = QCheckBox("One field to another")
        interp_to_anotherCheckBox.setChecked(False)
        interp_to_anotherCheckBox.clicked.connect(lambda: self.checkbox_changed('to_another'))
        computationCheckBox = QCheckBox("New field computation")
        computationCheckBox.setChecked(False)
        computationCheckBox.clicked.connect(lambda: self.checkbox_changed('computation'))


        # Interpolation combobox
        types = ['linear', 'conservative_1st', 'conservative_2nd',\
                 'patch', 'nearest_stod', 'nearest_dtos']
        interpolation_ComboBox = QComboBox()
        for type in types:
            interpolation_ComboBox.addItem(type)


        # Source and destination comboboxes
        source_ComboBox = QComboBox()
        destination_ComboBox = QComboBox()
        for i in np.arange(len(plotvars.fields)):
            source_ComboBox.addItem(str(i))
            destination_ComboBox.addItem(str(i))
            
        # Horizontal lines
        hline1 = HLine()
        hline2 = HLine()
        hline3 = HLine()
        
        # Buttons
        applyButton = QPushButton("Apply")
        applyButton.clicked.connect(self.apply)

        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        
        self.warningLabel1 = warningLabel1
        self.warningLabel2 = warningLabel2
        self.anchorLabel = anchorLabel
        self.anchorTextBox = anchorTextBox
        self.reverse_yCheckBox = reverse_yCheckBox
        self.lonminTextBox = lonminTextBox
        self.lonmaxTextBox = lonmaxTextBox
        self.lonstepTextBox = lonstepTextBox
        self.latminTextBox = latminTextBox
        self.latmaxTextBox = latmaxTextBox
        self.latstepTextBox = latstepTextBox
        self.interpolation_ComboBox = interpolation_ComboBox
        self.source_ComboBox = source_ComboBox
        self.destination_ComboBox = destination_ComboBox
        self.lonminLabel = lonminLabel
        self.lonmaxLabel = lonmaxLabel
        self.lonstepLabel = lonstepLabel
        self.latminLabel = latminLabel
        self.latmaxLabel = latmaxLabel
        self.latstepLabel = latstepLabel
        self.sourceLabel = sourceLabel
        self.destinationLabel = destinationLabel
        self.computationTextBox = computationTextBox
        self.interp_regularCheckBox = interp_regularCheckBox
        self.interp_to_anotherCheckBox = interp_to_anotherCheckBox
        self.source_ComboBox = source_ComboBox
        self.destination_ComboBox = destination_ComboBox
        self.interpolationLabel = interpolationLabel
        self.interpolation_methodLabel = interpolation_methodLabel
        
        self.hline1 = hline1
        self.hline2 = hline2        
        self.hline3 = hline3        
        
        # Set the layout

        # Collapse methods
        vbox.addWidget(collapseLabel)
        hbox_area_collapse = QHBoxLayout()
        hbox_area_collapse.addWidget(self.collapse_areaLabel)
        hbox_area_collapse.addWidget(self.collapse_areaComboBox)
        vbox.addLayout(hbox_area_collapse)
        
        for i in np.arange(plotvars.max_axes):
            hbox_collapse = QHBoxLayout()
            hbox_collapse.addWidget(self.collapse_Labels[i])
            hbox_collapse.addWidget(self.collapse_ComboBoxes[i])
            vbox.addLayout(hbox_collapse)


        vbox.addWidget(hline1)

        vbox.addWidget(warningLabel1)
        vbox.addWidget(warningLabel2)        


        # Anchor box
        hbox_anchor = QHBoxLayout()
        hbox_anchor.addWidget(anchorLabel)
        hbox_anchor.addWidget(anchorTextBox)
        vbox.addLayout(hbox_anchor)
        vbox.addWidget(hline2)

        # Reverse y
        vbox.addWidget(reverse_yCheckBox)
        vbox.addWidget(hline3)

        # Interpolation boxes
        vbox.addWidget(interpolationLabel)
        hbox_interp = QHBoxLayout()
        hbox_interp.addWidget(interpolation_methodLabel)
        hbox_interp.addWidget(interpolation_ComboBox)
        vbox.addLayout(hbox_interp)
        vbox.addWidget(interp_regularCheckBox)
        hbox_lons = QHBoxLayout()
        hbox_lons.addWidget(lonminLabel)
        hbox_lons.addWidget(lonminTextBox)
        hbox_lons.addWidget(lonmaxLabel)
        hbox_lons.addWidget(lonmaxTextBox)
        hbox_lons.addWidget(lonstepLabel)
        hbox_lons.addWidget(lonstepTextBox)
        vbox.addLayout(hbox_lons)

        hbox_lats = QHBoxLayout()
        hbox_lats.addWidget(latminLabel)
        hbox_lats.addWidget(latminTextBox)
        hbox_lats.addWidget(latmaxLabel)
        hbox_lats.addWidget(latmaxTextBox)
        hbox_lats.addWidget(latstepLabel)
        hbox_lats.addWidget(latstepTextBox)
        vbox.addLayout(hbox_lats)

        vbox.addWidget(interp_to_anotherCheckBox)
        hbox_interp_to_another = QHBoxLayout()
        hbox_interp_to_another.addWidget(sourceLabel)
        hbox_interp_to_another.addWidget(source_ComboBox)
        hbox_interp_to_another.addWidget(destinationLabel)
        hbox_interp_to_another.addWidget(destination_ComboBox)
        hbox_interp_to_another.addStretch(1)
        vbox.addLayout(hbox_interp_to_another)
        vbox.addWidget(HLine())

        vbox.addWidget(computationLabel1)
        vbox.addWidget(computationLabel2)
        vbox.addWidget(computationLabel3)
        vbox.addWidget(computationLabel4)
        vbox.addWidget(computationLabel5)
        vbox.addWidget(computationLabel6)

        vbox.addWidget(computationTextBox)

        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(applyButton)
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        container.addLayout(hbox_buttons)

        self.transform_help = None  # No external window yet.

        self.setWindowTitle('cfview transform settings')
        self.scroll = scroll
        self.setLayout(container)



        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.setFont(custom_font)

        self.interp_regular('Off')
        self.interp_one_to_another('Off')


        self.show()
        


        # Set the collapse visibility based on the field
        self.Populate_collapses()
        

    def collapse_type(self, idim):

        '''
         Set the collapse widget states in plotvars
        '''
        
        
        f = deepcopy(plotvars.fields[plotvars.index_number])
        mystored = plotvars.stored['f' + str(plotvars.index_number)]
        myaxes = mystored['data_axes']
        myaxes_long = deepcopy(mystored['data_axes'])
        for i in np.arange(len(myaxes_long)):
            if myaxes_long[i][:3] == 'dim':
                myaxes_long[i] = 'dimensioncoordinate' + myaxes_long[i][3:]
        mycollapses = mystored['collapses'] 
        
          
        if idim >= 0:
            mycollapses[idim] = self.collapse_ComboBoxes[idim].currentText()
            collapse_indicies = [idim]
            
        if idim == -1:
            mycollapses[myaxes.index('X')] = self.collapse_areaComboBox.currentText()
            mycollapses[myaxes.index('Y')] = self.collapse_areaComboBox.currentText()
            collapse_indicies = [myaxes.index('X'), myaxes.index('Y')]
            
            
        for myindex in collapse_indicies:

        
            indicies = [i.row() for i in self.parent.lists[myindex].selectedIndexes()]
            mymin = min(indicies)
            mymax = max(indicies)

            # Code for bounds calculation
            coord = f.coord(myaxes_long[myindex])
            if coord.has_bounds() is False:
                bounds = coord.create_bounds()
                coord.set_bounds(bounds)

            if not coord.T:
                # Code for bounds calculation
                mybounds = coord.bounds.array[mymin:mymax+1, :]
                val = str((np.max(mybounds) - np.min(mybounds))/2.0 + np.min(mybounds))
            else:
                ref_time = coord.units
                ref_calendar = coord.calendar
                time_units = cf.Units(ref_time, ref_calendar)

                # Code for bounds calculation
                mybounds = coord.bounds.array[mymin:mymax+1, :]
                midpt = (np.max(mybounds) - np.min(mybounds)) / 2.0 + np.min(mybounds)
                val = str(cf.cftime.num2date(midpt, ref_time, ref_calendar))



            self.parent.lists_collapse[myindex].clear()

            QListWidgetItem(str(val), self.parent.lists_collapse[myindex]) 
            self.parent.lists_collapse[myindex].selectAll()
            
            
        # Call Populate_collapses for when area is set as both X and Y collapses will change
        self.Populate_collapses()
        
               
        
    def Populate_collapses(self):
        '''
         Set the collapse widget visibilities
        '''        
        
        if plotvars.fields is None:
            return
            
            
        f = deepcopy(plotvars.fields[plotvars.index_number])    
            
        mystored = plotvars.stored['f' + str(plotvars.index_number)]
        myaxes = mystored['data_axes']
        mycollapses = mystored['collapses']
        
        # Turn off all collapses to start
        self.collapse_areaLabel.setVisible(False)
        self.collapse_areaComboBox.setVisible(False)        
        for i in np.arange(plotvars.max_axes):
            self.collapse_ComboBoxes[i].setVisible(False)
            self.collapse_Labels[i].setVisible(False)
            
        # Set area collapse labels and visibility if present and have more than one value
        if ('X' in myaxes and len(f.coord('X').array) > 1) and ('Y' in myaxes and len(f.coord('Y').array > 1)):
            self.collapse_areaLabel.setVisible(True)
            self.collapse_areaComboBox.setVisible(True)
            myxcollapse = mycollapses[myaxes.index('X')]
            myycollapse = mycollapses[myaxes.index('Y')]
            if myxcollapse != 'Off' or myycollapse != 'Off':
                if myxcollapse == myycollapse:
                    self.collapse_areaComboBox.setCurrentText(mycollapses[myaxes.index('X')])
                else:
                    self.collapse_areaComboBox.setCurrentText('Off')
            
            
        # Set axis collapse labels and visibility if they have more than one value            
        for i in np.arange(len(myaxes)):
            if  mystored['axis_sizes'][i] > 1:
                self.collapse_Labels[i].setText(myaxes[i])
                self.collapse_Labels[i].setVisible(True)
                self.collapse_ComboBoxes[i].setVisible(True)
                self.collapse_ComboBoxes[i].setCurrentText(mycollapses[i])
        
        # Store collapses and reset the field title
        plotvars.stored['f' + str(plotvars.index_number)]['collapses'] = mycollapses
        
        
        
        
        # Set widget visibilty based on whether lon and lat pairing are present in this field
        lonlat = False
        if 'X' in myaxes and 'Y' in myaxes:
            lonlat = True
        
        objs = [self.anchorLabel, self.anchorTextBox, self.collapse_areaLabel, \
                self.collapse_areaComboBox, \
                self.reverse_yCheckBox, self.interpolationLabel, self.interpolation_methodLabel, \
                self.interp_regularCheckBox, self.lonminTextBox, \
                self.lonmaxTextBox, self.lonstepTextBox, self.latminTextBox, self.latmaxTextBox, \
                self.latstepTextBox,self.interpolation_ComboBox, self.reverse_yCheckBox, \
                self.interp_regularCheckBox, self.interp_to_anotherCheckBox, self.lonminLabel, \
                self.lonmaxLabel, self.lonstepLabel, self.latminLabel, self.latmaxLabel, \
                self.latstepLabel, self.sourceLabel, self.destinationLabel, self.source_ComboBox, \
                self.destination_ComboBox, self.hline1, self.hline2, self.hline3]

        
        if lonlat:
            self.warningLabel1.setVisible(False)
            self.warningLabel2.setVisible(False)
            for i in np.arange(len(objs)):
                objs[i].setVisible(True)
        else:
            self.warningLabel1.setVisible(True)
            self.warningLabel2.setVisible(True)
            pal = QPalette(self.warningLabel1.palette())
            pal.setColor(QPalette.WindowText, QColor(Qt.red))
            self.warningLabel1.setPalette(pal) 
            pal = QPalette(self.warningLabel2.palette())
            pal.setColor(QPalette.WindowText, QColor(Qt.red))
            self.warningLabel2.setPalette(pal)             
            
            for i in np.arange(len(objs)): 
                objs[i].setVisible(False)        
        
        
        # Reset the field title        
        self.parent.reset_field_title()
        

                

    def interp_regular(self, text):
        # Turn sensitvity of interpolation boxes on and off

        objs = [self.lonminTextBox, self.lonmaxTextBox, self.lonstepTextBox, \
                self.latminTextBox, self.latmaxTextBox, self.latstepTextBox, \
                self.lonminLabel, self.lonmaxLabel, self.lonstepLabel, \
                self.latminLabel, self.latmaxLabel, self.latstepLabel]

        if text == 'Off':
            flags = [False] * 12
        else:
            flags = [True] * 12

        for i in np.arange(len(objs)):
            obj = objs[i]
            color = plotvars.text_colour_insensitive
            if flags[i]:
                color = plotvars.text_colour
            obj.setEnabled(flags[i])
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(color))
            obj.setPalette(pal)


    def interp_one_to_another(self, text):
        # Turn sensitvity of interpolation boxes on and off

        objs = [self.source_ComboBox, self.destination_ComboBox, \
                self.sourceLabel, self.destinationLabel]

        if text == 'Off':
            flags = [False] * 4
        else:
            flags = [True] * 4

        for i in np.arange(len(objs)):
            obj = objs[i]
            color = plotvars.text_colour_insensitive
            if flags[i]:
                color = plotvars.text_colour
            obj.setEnabled(flags[i])
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(color))
            obj.setPalette(pal)



    def checkbox_changed(self, text):
 
        if text == 'regular':
            if self.interp_regularCheckBox.isChecked():
                self.interp_to_anotherCheckBox.setChecked(False)
                self.interp_regular('On')
                self.interp_one_to_another('Off')
            else:
                self.interp_regular('Off')


        if text == 'to_another':
            if self.interp_to_anotherCheckBox.isChecked():
                self.interp_regularCheckBox.setChecked(False)
                self.interp_regular('Off')
                self.interp_one_to_another('On')
            else:
                self.interp_one_to_another('Off')



    def apply(self):

        com_interp = 'import cf\n'
        com_interp += "f = cf.read(" + plotvars.file_read_str + ")"
        if self.interp_regularCheckBox.isChecked():
            com_interp += "[" + str(plotvars.index_number) + "]"
        com_interp += "\n"
        
        changes_made = False

        if self.anchorTextBox.text() != '':

            # Make a copy of the field
            f = deepcopy(plotvars.fields[plotvars.index_number])

            # Check data has an X dimension
            if f.has_construct('X'):
            
                # Check input is a number
                valstr = self.anchorTextBox.text()
                vals_okay, errstr = Data_check.test_numbers(valstr)
                if not vals_okay:
                    print('\n anchor value is not a number\n')
                    return
                    
                val = Data_check.test_type(valstr)
                
            
                if f.iscyclic('X') is False:
                    f.cyclic('X', period=360)
                    com_interp += "f.cyclic('X', period=360)\n"
                # We use a separate variable here as for some unknown reason if we do an inplace=True on f
                # it returns an NoneType object
                g = f.anchor('X', val)
                com_interp += "f.anchor('X', " + str(float(self.anchorTextBox.text())) + ", inplace=True)\n"
                plotvars.fields[plotvars.index_number] = g
                self.parent.fieldnumber()
                changes_made = True
            else:
                print('Cannot anchor as X does not exist')
                return


        if self.reverse_yCheckBox.isChecked():

            # Make a copy of the field
            f = deepcopy(plotvars.fields[plotvars.index_number])

            if f.has_construct('Y'):
                f = f.flip('Y')
                com_interp += "f = f.flip('Y')\n"
                plotvars.fields[plotvars.index_number] = f
                self.parent.fieldnumber()
                changes_made = True
            else:
                print('Latitude axis not present so reverse not possible')
                return


        method = str(self.interpolation_ComboBox.currentText())


        if self.interp_regularCheckBox.isChecked() or self.interp_to_anotherCheckBox.isChecked():
            # Check ESMF is installed
            try:
                import ESMF
            except:
                print('Interpolation package esmpy needs to be installed:')
                print('conda install -c conda-forge mpich esmpy')
                return

            # Check data has X and Y coords
            f = deepcopy(plotvars.fields[plotvars.index_number])
            data_okay = True
            data_error = 'Data error\n'
            if f.has_construct('X') is False:
                data_okay = False
                data_error += 'missing X coordinate\n'

            if f.has_construct('Y') is False:
                data_okay = False
                data_error += 'missing Y coordinate\n'

            if data_okay is False:
                print(data_error)
                return


            # If method is not linear check and add bounds if necessary
            if method != 'linear':
                if f.construct('X').has_bounds() is False:
                    f.construct('X').set_bounds(f.construct('X').create_bounds())
                    com_interp += "\n# Create longitude bounds\n"
                    com_interp += "f.construct('X').set_bounds(f.construct('X').create_bounds())\n"

                if f.construct('Y').has_bounds() is False:
                    f.construct('Y').set_bounds(f.construct('Y').create_bounds(min=-90, max=90))
                    com_interp += "\n# Create latitude bounds\n"
                    com_interp += "f.construct('Y').set_bounds(f.construct('Y')"
                    com_interp += ".create_bounds(min=-90, max=90))\n"

            # Regular user supplied grid
            if self.interp_regularCheckBox.isChecked():
                # Extract values from the interface
                lonmin = Data_check.test_type(self.lonminTextBox.text())
                lonmax = Data_check.test_type(self.lonmaxTextBox.text())
                lonstep = Data_check.test_type(self.lonstepTextBox.text()) 
                latmin = Data_check.test_type(self.latminTextBox.text())
                latmax = Data_check.test_type(self.latmaxTextBox.text())
                latstep = Data_check.test_type(self.latstepTextBox.text())

                # Check inputs are numbers
                vals_okay, errstr = Data_check.test_numbers([lonmin, lonmax, lonstep])
                if not vals_okay:
                    print('\nInterpolation input error - one of lonmin, lonmax, lonstep is not a number\n')
                    return
                
                vals_okay, errstr = Data_check.test_numbers([latmin, latmax, latstep])
                if not vals_okay:
                    print('\nInterpolation input error - one of latmin, latmax, latstep is not a number\n')
                    return
                
                # Check lonmax > lonmin and latmax > latmin
                okay, errstr = Data_check.test_val0_lt_val1([lonmin, lonmax])
                if not okay:
                    print("\nInterpolation input error - lonmin must be less than lonmax\n")
                    return
                    
                okay, errstr = Data_check.test_val0_lt_val1([latmin, latmax])
                if not okay:
                    print("\nInterpolation input error - latmin must be less than latmax\n")
                    return
                    
                
                
                lonmax = lonmax + lonstep
                latmax = latmax + latstep

                # Form the destination longitude coordinate
                lons = cf.DimensionCoordinate(data=cf.Data(np.arange(lonmin, lonmax, lonstep), 'degrees_east'))
                lons.standard_name = 'longitude'
                lon_bounds = lons.create_bounds()
                lons.set_bounds(lon_bounds)

                com_interp += '\nimport numpy as np\n'
                com_interp += "\nlons = cf.DimensionCoordinate(data=cf.Data(np.arange(" + str(lonmin) +", "
                com_interp += str(lonmax) + ", " + str(lonstep) + "), 'degrees_east'))\n"
                com_interp += "lons.standard_name = 'longitude'\n"
                com_interp += "lon_bounds = lons.create_bounds()\n"
                com_interp += "lons.set_bounds(lon_bounds)\n"
                #com_interp += "lons.cyclic('X')\n"

                # Form the latitude destination coordinate
                lats = cf.DimensionCoordinate(data=cf.Data(np.arange(latmin, latmax, latstep), 'degrees_north'))
                lats.standard_name = 'latitude'
                lat_bounds = lats.create_bounds(min=-90, max=90)
                lats.set_bounds(lat_bounds)

                com_interp += "\nlats = cf.DimensionCoordinate(data=cf.Data(np.arange(" + str(latmin) +", "
                com_interp += str(latmax) + ", " + str(latstep) + "), 'degrees_north'))\n"
                com_interp += "lats.standard_name = 'latitude'\n"
                com_interp += "lat_bounds = lats.create_bounds(min=-90, max=90)\n"
                com_interp += "lats.set_bounds(lat_bounds)\n\n"


                # Regrid data
                g = f.regrids({'longitude': lons, 'latitude': lats}, method=method)
                com_interp += "g = f.regrids({'longitude': lons, 'latitude': lats}, method='" + method + "')\n"

                changes_made = True

            # User supplied grid
            if self.interp_to_anotherCheckBox.isChecked():

                f = deepcopy(plotvars.fields)

                source_index = self.source_ComboBox.currentIndex()
                destination_index = self.destination_ComboBox.currentIndex()

                source = f[source_index]
                destination = f[destination_index]

                g =  source.regrids(destination, method=method)
                
                com_interp = 'import cf\n'
                com_interp += "f = cf.read(" + plotvars.file_read_str + ")]\n"
                com_interp += "g = f[" + str(source_index) + "].regrids(f[" + str(destination_index) + "], method=" + method + ")"
                
                changes_made = True
                
        if self.computationTextBox.text() != '':

            computation = self.computationTextBox.text()
            com_interp += "\ng = " + computation + "\n"

            # Check for valid characters otherwise reject
            valid = 'f0123456789.()[]+-*/ '


            okay = True
            invalid_str = ''
            for char in computation:
                if char not in valid:
                    okay = False 
                    invalid_str += char

            f = deepcopy(plotvars.fields)

            if okay:
                g = eval(computation)                
                changes_made = True
            else:
                print('Field computation code is restricted to the characters ' + valid)
                print('Found an invalid character in the input ',  invalid_str)
                return


        if changes_made: 
            print('### Transform code is ###', com_interp)
        else:
            print('\nNo user transform data changes have been made\n')
            return


        # Add a new field to the end of the field list
        selected = self.parent.fieldlist.selectedItems()
        index = plotvars.index_number
        next = len(plotvars.fields)
        if self.interp_regularCheckBox.isChecked() or self.computationTextBox.text() != '':
            new_name = selected[0].text()
        else:
            source_int = int(self.source_ComboBox.currentText())
            new_name = self.parent.fieldlist.item(source_int).text()

        QListWidgetItem(deepcopy(new_name), self.parent.fieldlist)
        plotvars.stored_dimensions['f' + str(next)] = deepcopy(plotvars.stored_dimensions['f' + str(index)])
        plotvars.stored_collapses['f' + str(next)] = deepcopy(plotvars.stored_collapses['f' + str(index)])
        plotvars.stored_collapse_values['f' + str(next)] = deepcopy(plotvars.stored_collapse_values['f' + str(index)])
        plotvars.fields.append(g)
        index_length = plotvars.index_length
        new_title = str(next) + (6 - len(str(next))) * ' ' + new_name[6:]
        self.parent.fieldlist.item(next).setText(new_title)
        plotvars.data_source_list.append('interpolated')
        plotvars.index_number = next
        for myselect in selected:
            myselect.setSelected(False)
        self.parent.fieldlist.item(next).setSelected(True)
         
        if self.computationTextBox.text() != '':
            self.parent.fieldlist.item(next).setText(new_title[:30] + 'computed field')
            
        plotvars.next_field_index += 1

        changes_made = True
          

    def reset(self):
        # Reset widget values
        self.lonminTextBox.setText('')
        self.lonmaxTextBox.setText('')
        self.lonstepTextBox.setText('')
        self.latminTextBox.setText('')
        self.latmaxTextBox.setText('')
        self.latstepTextBox.setText('')
        self.anchorTextBox.setText('')
        self.computationTextBox.setText('')
        self.reverse_yCheckBox.setChecked(False)
        self.interpolation_ComboBox.setCurrentIndex(0)
        self.interp_regular('Off')
        self.interp_one_to_another('Off')
        self.source_ComboBox.setCurrentIndex(0)
        self.destination_ComboBox.setCurrentIndex(0)
        self.interp_regularCheckBox.setChecked(False)
        self.interp_to_anotherCheckBox.setChecked(False)
        
        mystored = plotvars.stored['f' + str(plotvars.index_number)]
        mycollapses = mystored['collapses']
        self.collapse_areaComboBox.setCurrentText('Off')
        for i in np.arange(len(mycollapses)):
            mycollapses[i] = 'Off'
            self.collapse_ComboBoxes[i].setCurrentText('Off')
            
            
        self.parent.reset_field_title()
                   
            



    def help(self):

        if self.transform_help is None:

            html = '<body><h2>Transform help</h2>'
            html += '<h3>Collapse options</h3>'
            html += 'Collapse the field along a dimension with one of the following methods: <br>' 
            html += 'mean, minimumn maximum, variance, standard deviation'

            html += '<h3>Anchor in longitude</h3>'
            html += 'If a longitude axis exists roll the axis so that the given value lies in the first coordinate cell.'
            html += 'If the data not cyclic it will be made so'

            html += '<h3>Reverse the latitude axis</h3>'
            html += 'Reverse the latitude axis can be used to flip the latitude axis values in cases where the data starts at the north pole.'
            html += 'Either direction is plotted corrrectly in cf-plot.'

            html += '<h3>Interpolation options</h3>'
            html += '<b>linear</b> Bilinear interpolation<br>'
            html += '<b>conservative_1st</b> First order conservative interpolation.<br>'
            html += '<b>conservative_2nd</b> Second-order conservative interpolation.<br>'
            html += '<b>patch</b> Higher-order patch recovery interpolation.<br>'
            html += '<b>nearest_stod</b> Nearest neighbour interpolation for which each destination point is mapped to the closest source point. '
            html += 'Useful for extrapolation of categorical data.<br>'
            html += '<b>nearest_dtos</b> Nearest neighbour interpolation for which each source point is mapped to the destination point. '
            html += 'Useful for extrapolation of categorical data.<br>'
            html += 'Further information at https://ncas-cms.github.io/cf-python/method/cf.Field.regrids.html?highlight=regrids#cf.Field.regrids'
            html += '<p>'

            html += '<h3>Regular grid interpolation</h3>'  
            html += 'Fill in the longitude and latitude minimum, maximum and step to calculate a regridded field.<p>'

            html += '<h3>Regrid from one field to another</h3>'  
            html += 'Select the source and destination field indicies to regrid one field to another.<p>'
            
            html += 'Note: Anchor in longitude, reversing the latitude axis and interpolation is only available for grids with '
            html += 'recognizable longitude and latitude axes.<p.'

            html += '<h3>Field computation</h3>'  
            html += 'Type in the new field calculation, for example:<p>'
            html += 'difference two fields:<br>' 
            html += 'f[4] - f[2]<p>'
            html += 'field index 1 is u and field index 2 is v<br>'
            html += 'to get the wind magnitude in a new field:<br>'
            html += '(f[1]**2 + f[2]**2) ** 0.5'

          

            self.transform_help = Help(html)

        self.transform_help.show()



    def closeEvent(self, event):
        # Close self.parent.transform_window
        self.parent.transform_window = None
        self.close()
        


class Data_check():

    def __init__(self):
        pass
        
    def test_type(val):  
        # Test if input string is an integer or float
        # Need to match the data to the value

        # Return an integer if string is convertible to one
        try:
            return int(val)
        except ValueError:
            pass
            


        try:
            a = float(val)
            
            if plotvars.fields[plotvars.index_number].dtype == 'float32':
                a = np.float32(val)
            if plotvars.fields[plotvars.index_number].dtype == 'float64':
                a = np.float64(val)    
                             
            # Float section
            return a
            
        except ValueError:
            return str(val)

    def test_numbers(vals):  
        # Check inputs are numbers
        vals_okay = True
        errstr = ''
        for val in vals:
            try:
                float(val)
            except:
                vals_okay = False
                errstr += str(val) + ' is not a number\n'
        return vals_okay, errstr

    def test_integers(vals):  
        # Check if all values are integers
        vals_okay = True
        errstr = ''
        for val in vals:
            val_check = val
            if val_check[0] == '-':
                val_check = val_check[1:]
            if not val_check.isdigit():
                vals_okay = False
                errstr += str(val) + ' is not an integer\n'
        return vals_okay, errstr

    def test_ascending(vals):  
        # Check inputs are ascending
        vals_okay = True
        errstr = ''
        for j in np.arange(np.size(vals) - 1):
            if float(vals[j+1]) <= float(vals[j]):
                vals_okay = False
                errstr += 'inputs are not ascending\n'
        return vals_okay, errstr

    def test_val0_lt_val1(vals):  
        # Check min < max
        vals_okay = True
        errstr = ''
        if float(vals[0]) >= float(vals[1]):
            vals_okay = False
            errstr += 'minimum must be less than the maximum\n'
        return vals_okay, errstr

    def test_val_gt_zero(val):  
        # Check step > 0
        vals_okay = True
        errstr = ''
        if float(val) <= 0:
            vals_okay = False
            errstr += 'step must be greater than zero\n'
        return vals_okay, errstr



class Cf_funcs():

    def __init__(self, parent):
        super(Cf_funcs, self).__init__()
        self.parent = parent

        
    def field_name(field=None):
        '''Work out the field name'''

        name = 'No'
        if field is None:
            return name
        nc = field.nc_get_variable(None)
        if nc:
            name = field.nc_get_variable()
        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
        return name




    def field_axis_values(field=None):
        '''Work out the field dimensions on the original field'''

        # Make a copy so any changes are just local
        field = deepcopy(field)

        vals = []
        stored_vals = plotvars.stored['f' + str(plotvars.index_number)]
        myaxes = stored_vals['data_axes']
        
        if stored_vals['data_type'] == 'dim':
            for dim in myaxes:
                if len(dim) == 1:
                    mydim = dim
                else:
                    mydim = 'dimensioncoordinate' + dim[3:]
                    
                if field.dimension_coordinate(mydim).T:
                    vals.append(field.dimension_coordinate(mydim).dtarray)
                else:
                    vals.append(field.dimension_coordinate(mydim).array)

        if stored_vals['data_type'] == 'trajectory':
            coords = list(field.coords())
            has_right_coords = 0
            nx = 0
            ny = 0
            nz = 0
                
            for mycoord in coords:
                if field.coord(mycoord).nc_get_variable() == 'longitude':
                    nx = np.shape(field.auxiliary_coordinate('longitude').array)[0]
                    has_right_coords += 1
                if field.coord(mycoord).nc_get_variable() == 'latitude':
                    ny = np.shape(field.auxiliary_coordinate('latitude').array)[0]
                    has_right_coords += 1
                if field.coord(mycoord).nc_get_variable() == 'time':
                    nt = np.shape(field.auxiliary_coordinate('time').array)[0]
                    has_right_coords += 1
                        
                # has_right_coords must be 3 for a correctly defined trajectory field
                # We don't print an error here as this produces too much output in the message panel
                # It it obvious this is not a trajectory field though as it has zero tracks and trying
                # to plot it using cf-plot gives a reasonable error message to this effect
                        
            vals.append(np.arange(nx))
            vals.append(np.arange(ny))
            vals.append(np.arange(nt))


        if stored_vals['data_type'] == 'axis':

            # Set the data_shape
            # Just in case the units are messed up we need to do a try / except construct
            try:
                data_shape  = np.shape(field.array)
            except:
                field.del_property('units')
                field.set_property('units', 'K')
                data_shape = np.shape(field.array)

            axes = field.get_data_axes()
            ncnames = []
            
            if len(axes) == 1:
                vals.append(field.array)
                
            if len(axes) > 1:
                ncnames = [] 
                for i in np.arange(len(axes)):
                    axis = field.domain_axis(axes[i]).nc_get_dimension()
                    ncnames.append(axis)
                
                for i in np.arange(len(data_shape)):
                    # Set dim_sizes and dim_values to initial values in case the netCDF variable 
                    # is not found in plotvars.fields

                    myvals = np.arange(data_shape[i])
                    
                    
                    # Search plotvars.fields netCDF variables and set dim_sizes and dim_values if a matching axis is found
                    for j in np.arange(len(plotvars.fields)):
                        f = plotvars.fields[j]
                        myaxes = f.get_data_axes()
                        if len(myaxes) == 1:
                            myaxis = myaxes[0]
                            if ncnames[i] == f.domain_axis(myaxis).nc_get_dimension():
                                myvals = f.array
                                
                    vals.append(myvals)
        
        # Return axis values
        return vals










    def field_dims(field=None):
        '''Work out the field dimensions on the original field'''

        # Make a copy so any changes are just local
        field = deepcopy(field)
        
        x = []
        y = []
        z = []
        t = []
        
        nx = 0
        ny = 0
        nz = 0
        nt = 0

        # Allocated array to see if dimension has been allocated
        allocated = [False, False, False, False]
        
        # Find dimension names
        dim_names = Cf_funcs.find_dim_names(field)
        
        # Reset plotvars.dim_names
        plotvars.dim_names = ['x', 'y', 'z', 't']
        
        #mycoords = list(field.dimension_coordinates())
        #mycoords.reverse()
        
        # Test to see if any dimensions in the coordinates are not X, Y, Z, or T 
        dim_test = [x[:-1] for x in dim_names]


        # Extract coordinate data if a matching CF standard_name or axis is found                
        for dim in dim_names:
        
            if dim == 'X':
                x = field.construct(dim).array
                nx = np.size(x)
                allocated[0] = True
                plotvars.dim_names[0] = 'x'
                    
            if dim == 'Y':
                y = field.construct(dim).array
                ny = np.size(y)
                allocated[1] = True
                plotvars.dim_names[1] = 'y'
                
            if dim == 'Z':
                z = field.construct(dim).array
                nz = np.size(z)
                allocated[2] = True
                plotvars.dim_names[2] = 'z'
                
            if dim == 'T':
                t = field.construct(dim).dtarray
                nt = np.size(t)
                allocated[3] = True
                plotvars.dim_names[3] = 't'                
                
                
        #print('allocated are', allocated)
        #print('dim_test are ', dim_test)
        
        
        # Code for dimension coordinates not of form X, Y, Z, T
        if 'dimensioncoordinate' in dim_test:
            
            dims = [x, y, z, t]
            lengths = [nx, ny, nz, nt]
            
            
            # Find unallocated dimension coordinates indicies
            unalloc = [i for i, s in enumerate(dim_test) if 'dimensioncoordinate' in s]
            #print('unalloc is ', unalloc)
            
            # Find available slots
            avail = [i for i, s in enumerate(allocated) if not s]
            #print('avail is ', avail)
            
            if len(avail) > 0:
                for i in np.arange(len(unalloc)):
                    dims[avail[i]] = field.dimension_coordinate(dim_names[avail[i]]).array
                    lengths[avail[i]] = np.size(dims[avail[i]])
                    plotvars.dim_names[avail[i]] = 'dim' + dim_names[avail[i]][-1]
                  

            # Unpack the return values
            x, y, z, t = dims[0], dims[1], dims[2], dims[3]
            nx, ny, nz, nt = lengths[0], lengths[1], lengths[2], lengths[3]



        # Test for trajectory data
        #if hasattr(field, 'featureType'):
        if field.get_property('featureType', False) is not False:
            if field.featureType == 'trajectory':
            
                coords = list(field.coords())
                has_right_coords = 0
                nx = 0
                ny = 0
                nz = 0
                
                for mycoord in coords:
                    if field.coord(mycoord).nc_get_variable() == 'longitude':
                        nx = np.shape(field.auxiliary_coordinate('longitude').array)[0]
                        has_right_coords += 1
                    if field.coord(mycoord).nc_get_variable() == 'latitude':
                        ny = np.shape(field.auxiliary_coordinate('latitude').array)[0]
                        has_right_coords += 1
                    if field.coord(mycoord).nc_get_variable() == 'time':
                        nt = np.shape(field.auxiliary_coordinate('time').array)[0]
                        has_right_coords += 1
                        
                # has_right_coords must be 3 for a correctly defined trajectory field
                # We don't print an error here as this produces too much output in the message panel
                # It it obvious this is not a trajectory field though as it has zero tracks and trying
                # to plot it using cf-plot gives a reasonable error message to this effect
                        
                x = np.arange(nx)
                y = np.arange(ny)
                t = np.arange(nt)



        # Data that has no dimensions.  Some data has stored the axis data in one of the fields in plotvars.fields.
        # Sometimes there is no matching data so we assign a ascending array with a step of one of the correct size.
        if len(field.coords()) == 0 and len(field.get_data_axes()) != 0:
            dim_sizes = [nx, ny, nz, nt]
            dim_values = [x, y, z, t]
            plotvars.dim_names = ['--', '--', '--', '--']
            axes = field.get_data_axes()
            
            # Set the data_shape
            # Just in case the units are messed up we need to do a try / except construct
            try:
                data_shape  = np.shape(field.array)
            except:
                field.del_property('units')
                field.set_property('units', 'K')
                data_shape = np.shape(field.array)

            
            if len(axes) == 1:
                axis = axes[0]
                dim_sizes[0] = field.construct(axis).get_size()
                dim_values[0] = field.array
                plotvars.dim_names[0] = 'ax0'
                
            if len(axes) > 1:
                ncnames = [] 
                for i in np.arange(len(axes)):
                    axis = field.domain_axis(axes[i]).nc_get_dimension()
                    ncnames.append(axis)
                    plotvars.dim_names[i] = 'ax' + str(i)
                
                for i in np.arange(len(axes)):
                    # Set dim_sizes and dim_values to initial values in case the netCDF variable 
                    # is not found in plotvars.fields
                    dim_sizes[i] = data_shape[i]
                    dim_values[i] = np.arange(dim_sizes[i])
                    
                    
                    # Search plotvars.fields netCDF variables and set dim_sizes and dim_values if a matching axis is found
                    for j in np.arange(len(plotvars.fields)):
                        f = plotvars.fields[j]
                        myaxes = f.get_data_axes()
                        if len(myaxes) == 1:
                            myaxis = myaxes[0]
                            if ncnames[i] == f.domain_axis(myaxis).nc_get_dimension():
                                dim_sizes[i] = f.construct(myaxis).get_size()
                                dim_values[i] = f.array
                    
                                
                                          
            nx, ny, nz, nt = dim_sizes[0], dim_sizes[1], dim_sizes[2], dim_sizes[3]
            x, y, z, t = dim_values[0], dim_values[1], dim_values[2], dim_values[3]

        
        return (nx, ny, nz, nt, x, y, z, t)


    def cf_var_name(field=None, dim=None):
        """
         | cf_var_name - return the name from a supplied dimension
         |               in the following order
         |               ncvar
         |               short_name
         |               long_name
         |               standard_name
         |
         | field=None - field
         | dim=None - dimension required - 'dim0', 'dim1' etc.
         |
         :Returns:
          name
        """

        id = getattr(field.construct(dim), 'id', False)
        ncvar = field.construct(dim).nc_get_variable(False)
        short_name = getattr(field.construct(dim), 'short_name', False)
        long_name = getattr(field.construct(dim), 'long_name', False)
        standard_name = getattr(field.construct(dim), 'standard_name', False)

        name = 'No Name'
        if id:
            name = id
        if ncvar:
            name = ncvar
        if short_name:
            name = short_name
        if long_name:
            name = long_name
        if standard_name:
            name = standard_name

        units = getattr(field.construct(dim), 'units', '()')
        if units[0] != '(':
            units = '(' + units + ')'
        return name, units



    def fields_list(parent, fields=None, order=None, search=False, order_changed=False, call=None):
        """
         | fields_list - return the field list for populating the field list widget
         |               
         | fields = None - list of fields to examine
         | order = 'index' or 'field' - order the fields by supplied parameter
         | search = False - return named search
         | order_changed = False - order of the fields has changed.  Use the stored 
         |                 fields_list rather than recalculating.
         | call = None - flag for when calling from reset_field_title.  When this is done
         |               don't set plotvars.field_widget_names
         |
         :Returns:
          field widget title and widget_names
        """        
        

        # Check for a trajectory field
        traj = False
        f = deepcopy(plotvars.fields[plotvars.index_number])
        if f.get_property('featureType', False) is not False:
            if f[0].featureType == 'trajectory':     
                traj = True


        if order_changed:
            field_widget_names = plotvars.field_widget_names
            field_widget_title = plotvars.field_widget_title
            
        else:
  
  
            names_list = []
            index_list = []                    
            field_widget_names = []      
            

            # Work out the widget title
            field_widget_title ='index '

            axis_lengths = plotvars.maximum_axis_lengths 

            if traj:
                field_widget_title += 'tracks' + plotvars.maximum_axis_lengths[0] * ' '
                
                # ajh - to be deleted
                #field_widget_title += 'tracks' + (nx_length - 1) * ' '                
                
            else:
                f = plotvars.fields[plotvars.index_number]
                
                

                myaxes = plotvars.stored['f' + str(plotvars.index_number)]['data_axes']
                
                for i in np.arange(len(axis_lengths)):
                    axis_label = (axis_lengths[i] + 1) * ' '
                    if i + 1 < len(myaxes) :
                         axis_label = (axis_lengths[i] - len(myaxes[i])) * ' ' + 'n' + myaxes[i]
                    
                    field_widget_title += axis_label
                
  

            field_widget_title += 'field name'



            # Work out the widget names
            for i in np.arange(len(fields)):
                # ajh - new code
                field_widget_name = str(i)  + (6 - len(str(i))) * ' '
                mystored = plotvars.stored['f' + str(i)]
                axis_sizes = mystored['axis_sizes']
                field_name = mystored['field_name']
                for i in np.arange(len(plotvars.maximum_axis_lengths)):
                    axis_label = (axis_lengths[i] + 1) * ' '
                    
                    if i + 1 <= len(axis_sizes):
                         axis_label = str(axis_sizes[i]) + (axis_lengths[i] - len(str(axis_sizes[i])) + 1) * ' '
                    field_widget_name += axis_label
                    
                field_widget_name +=  field_name                    
                field_widget_names.append(field_widget_name)

        # Only set plotvars.field_widget_names if the call doesn't come from reset_field_title
        if call is None:
            plotvars.field_widget_names = field_widget_names


        if order == 'index':
            plotvars.field_widget_indicies = np.arange(len(field_widget_names))


        if order == 'field':
            # Split widget_name string into a list, remove empty strings and create 
            # a numpy ordered list
            names = []
            for i in np.arange(len(field_widget_names)):
                name_long = field_widget_names[i].split(' ')
                name_field = [x for x in name_long if x != ''][5]
                names.append(name_field)

            ordered_indicies = np.argsort(names)

            plotvars.field_widget_indicies = ordered_indicies
            field_widget_names_ordered = []
            for i in np.arange(len(field_widget_names)):
                field_widget_names_ordered.append(field_widget_names[ordered_indicies[i]])
 
            field_widget_names = field_widget_names_ordered



        if search:
            widget_names_searched = []
            ordered_indicies = []
            names = []
            for i in np.arange(len(field_widget_names)):
                name_long = field_widget_names[i].split(' ')
                name_field = [x for x in name_long if x != ''][5]
                if search in name_field:
                    widget_names_searched.append(field_widget_names[i])
                    ordered_indicies.append(i)

            plotvars.field_widget_indicies = ordered_indicies
            field_widget_names = widget_names_searched
                


        plotvars.field_widget_title = field_widget_title
        
        
        return field_widget_names, field_widget_title
    



    def selected_dims(index):
        """ 
         Calculate the selected length of the data in the axes
         based on the dimension selection and the collapse method
         Returns: axis size
        """

        # Extract the dimensions and collapses for this field
        
        # ajh - new code
        mystored = plotvars.stored['f' + str(index)]
        
        vals = mystored['axis_sizes']
        
        for i in np.arange(len(vals)):
            if mystored['axes'][i] != -1:
                vals[i] = len(mystored['axes'][i])
            if  mystored['collapses'][i] != 'Off':
                vals[i] = 1
                
                
        return vals        
        
        

    def subspace_args(parent, index, call_data=False):
        """
         Work out the subspace args for the selected field
         call_data is generally False but set to True if being called from
         the data viewing section of code.  This is to allow a switch to using 
         the data drop down widget in that popup rather than the contour dropdown
         Returns: subspace_args
        """

        # Initialise an empty args dictionary
        args = {}

        f = deepcopy(plotvars.fields[index])
        
        
        # ajh - new code
        mystored = plotvars.stored['f' + str(index)]
        
        myaxes = deepcopy(mystored['data_axes'])
        # Convert all dim0 instances to dimensioncoordinate0 etc
        for i in np.arange(len(myaxes)):
            if myaxes[i][:3] == 'dim':
                myaxes[i] = 'dimensioncoordinate' + myaxes[i][3:]
        
        if call_data is True:
            plot_dims = parent.data_ComboBox.currentText().split('-')
        else:
            if plotvars.plot_type in ['Contour', 'Vector', 'Contour and vector']:
                plot_dims = plotvars.contour_type.split('-')
            
        if plotvars.plot_type == 'Line':
            plot_dims = [plotvars.line_type]         
        
        
        # Remove any log(dim1) and leave dim1
        for i in np.arange(np.size(plot_dims)):
            if plot_dims[i][0:4] == 'log(':
                plot_dims[i] = plot_dims[i][4: -1]   


        # Convert all dim0 instances to dimensioncoordinate0 etc
        for i in np.arange(np.size(plot_dims)):
            if plot_dims[i][0:3] == 'dim':
                plot_dims[i] = 'dimensioncoordinate' + plot_dims[i][3:]        
              
                
        # Subspace if the axis is in the plot axes or if the axis has a collapse method
        for i in np.arange(len(myaxes)):
            myaxis = myaxes[i]
            if myaxis in plot_dims or mystored['collapses'][i] != 'Off':
                if myaxis[:2] != 'ax':
                    if mystored['axes'][i] != -1:
                        ind_min = np.min(mystored['axes'][i])
                        ind_max = np.max(mystored['axes'][i])                        
                        
                        # values must go from min to max in cf.wi statements
                        if f.coord(myaxis).array[ind_min] > f.coord(myaxis).array[ind_max]:
                            ind_min, ind_max = ind_max, ind_min
            
                        # Set the subspace value
                        if myaxis != 'T':
                            args[myaxis] = cf.wi(f.coord(myaxis).array[ind_min], f.coord(myaxis).array[ind_max])
                        else:
                            args[myaxis] = cf.wi(f.coord(myaxis).dtarray[ind_min], f.coord(myaxis).dtarray[ind_max])            
            
                if myaxis[:2] == 'ax':
                    min_ind = parent.lists[i].selectedIndexes()[0].row() 
                    max_ind = parent.lists[i].selectedIndexes()[-1].row()
                    list_len = len(parent.lists[i])
                    
                    if max_ind - min_ind + 1 < list_len:
                        args[dim] = [min_ind, max_ind]                
        
            else:

                    # Only need to subspace if this dimension has more than one value
                    if np.size(f.coord(myaxis).array) > 1:
                    
                        # Use first value if no collapse is specified
                        if mystored['collapses'][i] == 'Off': 
                    
                            # Calculate the first dimension index value
                            if mystored['axes'][i] == -1:
                                ind = 0
                            else:
                                ind = np.min(mystored['axes'][i]) 
                        
                            # Set the subspace value
                            if myaxis != 'T':
                                args[myaxis] = f.coord(myaxis).array[ind]
                            else:
                                args[myaxis] = f.coord(myaxis).dtarray[ind]
                                
                    if myaxis[:2] == 'ax':
                        my_ind = parent.lists[i].selectedIndexes()[0].row()
                        args[myaxis] = my_ind        
        
        
        return args
        
        
        

        

    def com_subspace_collapse(subspace_args):
        """
         Form the subspace and collapse args
        """
        
        f = deepcopy(plotvars.fields[plotvars.index_number])
        mystored = plotvars.stored['f' + str(plotvars.index_number)]
        myaxes = mystored['data_axes']
        mycollapses = mystored['collapses']
        
        # Form the subspace command
        com_subspace = '' 
            
        if len(subspace_args) > 0:
            i = 0
            for arg in subspace_args:
                if i > 0:
                    com_subspace += ', '
                i = i + 1
                if type(subspace_args[arg]) == cf.query.Query:
                    vals = subspace_args[arg].value
                    if arg != 'T':
                        com_subspace += arg + '=cf.wi(' + str(vals[0]) + ', ' + str(vals[1]) + ')'
                    else:
                        com_subspace += arg + "=cf.wi(cf.dt('" + str(vals[0]) + "'), " + "cf.dt('" + str(vals[1]) + "'))"
                else:
                    com_subspace += arg + '=' 
                    if arg != 'T':
                        com_subspace += str(subspace_args[arg])
                    else:
                        com_subspace += "cf.dt('" + str(subspace_args[arg]) + "')"


        # Form the collapse command
        com_collapse = ''
        i = 0
        area = False
        
        if 'X' in myaxes and 'Y' in myaxes:
            x_ind = myaxes.index('X')
            y_ind = myaxes.index('Y')
            x_val = mycollapses[x_ind]
            y_val = mycollapses[y_ind]            
            
            if x_val != 'Off' and x_val != 'Off':
                if x_val == y_val:
                    area = True
                    com_collapse += "'area: " + x_val
                
                
        for i in np.arange(len(myaxes)):
            if mycollapses[i] != 'Off':
                if area is False or mycollapses[i] not in ['X', 'Y']:
                    if i == 0:
                        com_collapse += "'"
                    if i > 0:
                        com_collapse += ' '

                    com_collapse += myaxes[i] + ": " + mycollapses[i]      
                    i = i + 1                    
                                         
        if len(com_collapse) > 0:
            com_collapse += "'"



        return com_subspace, com_collapse                
                



    def generate_code(com_subspace, com_collapse, index=None, var_index=None):
        """
         Generate the cf-python code to make the data
        """ 

        if var_index is None:
            var_index = 0

        if var_index == 0:
            com = 'import cf\n'
            com += 'import cfplot as cfp\n\n'
        else:
            com = ''

        if plotvars.plot_type == 'Contour and vector' and var_index == 0:
            com += 'cfp.gopen()\n'

        if index is None:
            index = plotvars.index_number
        f = deepcopy(plotvars.fields[index])

        variable_names = ['f', 'g', 'h']
        if var_index is None:
            var = variable_names[0]
        else:
            var = variable_names[var_index]
            
        if plotvars.plot_type == 'Vector':
            if var_index == 0:
                com += '# U component\n'
            if var_index == 1:
                com += '# V component\n'
                
        if plotvars.plot_type == 'Contour and vector':
            if var_index == 1:
                com += '# U component\n'
            if var_index == 2:
                com += '# V component\n'               
                
            
        com += var + " = cf.read(" + plotvars.file_read_str + ")[" + str(index) +']\n'

        # Check for bounds if a collapse is required
        # and add them if they are missing
        mystored = plotvars.stored['f' + str(plotvars.index_number)]
        myaxes = mystored['data_axes']
        
        for i in np.arange(len(myaxes)):
            if mystored['collapses'][i] != 'Off':
                coord = f.coord(myaxes[i])
                # Check axis has more than one cell
                if len(coord.array) > 1:
                    if coord.has_bounds() is False:
                        #print('creating bounds')
                        bounds = coord.create_bounds()
                        coord.set_bounds(bounds)
                        com += '\n# Adding missing bounds for ' + myaxes[i] + '\n'
                        com += "coord = " + var + ".coord('" + myaxes[i] + "')\n"
                        com += 'bounds = coord.create_bounds()\n'
                        com += 'coord.set_bounds(bounds)\n\n'        
        
        

        if com_subspace != '':
            com += var + ' = ' + var + '.subspace(' + com_subspace + ')\n'

        if com_collapse != '':
            com += var + ' = ' + var + '.collapse(' + com_collapse + ', weights=True)\n'
            
        if plotvars.plot_type in ['Vector', 'Contour and vector']:
            com += '\n'
        
        return com


    def find_dim_names(field):
        ''' Find the field dimension coordinate names
            Ignores auxiliary coordinates (for now)
            returns:
            coordinates in the order [T, X, Y, Z]       
        '''
        
        # Get the field domain axes
        daxes = list(field.get_data_axes())
        
        # Get the field coordinates
        dcoords = list(field.coords())     
        
        
        ## Calculate the number of coordinates of type X, Y, Z and T
        nx = 0
        ny = 0
        nz = 0
        nt = 0
        for i in np.arange(len(dcoords)):
            if field.coord(dcoords[i]).X:
                nx += 1
            if field.coord(dcoords[i]).Y:
                ny += 1            
            if field.coord(dcoords[i]).Z:
                nz += 1        
            if field.coord(dcoords[i]).T:
                nt += 1      

   
        # Strip out any auxiliary coordinates if the field is not a trajectory field
        remove_aux = True
        if field.get_property('featureType', False) is not False:
            if field.featureType == 'trajectory':
                remove_aux = False
                
                
        # Strip out any auxiliary coordinates if the field is not a trajectory field
        if remove_aux:
            for i in np.arange(len(dcoords)):
                if dcoords[i][:-1] == 'auxiliarycoordinate':
                    dcoords[i] = 'aux' 
            dcoords = list(filter(('aux').__ne__,dcoords))
        

        
        # Convert these into corresponding dimension coordinates
        if remove_aux:
            coords = []
            for i in np.arange(len(daxes)):
                val = daxes[i]
                coord = None
                for j in np.arange(len(dcoords)):               
                    if daxes[i] == field.get_data_axes(dcoords[j])[0]:
                        coord = dcoords[j]      
        
                if coord is not None:
                    coords.append(coord)

        else:
            coords = dcoords
       
        
        # Blank daxes if coords has no elements
        if len(coords) == 0:
            daxes = []
                
        # Make a copy of coords in mycoords
        mycoords = deepcopy(coords)
        
        # Convert to X, Y, Z, T if coordinate is one of these
        # If the number of coordinates of this type is greater than 1 then don't do this as f.coord('Z') gives an 
        # error as there are more that one coordinates to return
        #print('daxes are ', daxes)
        #print('coords are ', coords)
        
        
        for i in np.arange(len(daxes)):
            if field.coord(coords[i]).X:
                if nx == 1:
                    mycoords[i] = 'X'
            if field.coord(coords[i]).Y:
                if ny == 1:
                    mycoords[i] = 'Y'            
            if field.coord(coords[i]).Z:
                if nz == 1:
                    mycoords[i] = 'Z'  
            if field.coord(coords[i]).T:
                if nt == 1:
                    mycoords[i] = 'T'            
            
            
        # Return the reverse of the coordinates so that they are in the order [X, Y, Z, T]
        mycoords.reverse()
        
        daxes = list(field.get_data_axes())

        #if len(mycoords) == 0 and len(daxes) > 0:
        #    for i in np.arange(len(daxes)):
        #        mycoords.append('ax' + str(len(daxes)-i))
            #mycoords = ['ax2', 'ax1', 'ax0', '--']
        
        
        return mycoords

        
                    
    def find_all_dims():
            
        if plotvars.fields[plotvars.index_number] is not None:
            field = plotvars.fields[plotvars.index_number]
        else:
            return
         
        # Assign lists of data axes and field coordinates   
        daxes = list(field.get_data_axes())
        coords = list(field.coords())
            
            
        # Calculate the number of coordinates of type X, Y, Z and T
        nx = 0
        ny = 0
        nz = 0
        nt = 0
        for i in np.arange(len(coords)):
            if field.coord(coords[i]).X:
                nx += 1
            if field.coord(coords[i]).Y:
                ny += 1            
            if field.coord(coords[i]).Z:
                nz += 1        
            if field.coord(coords[i]).T:
                nt += 1          
                
        # Extract the dimension coordinates
        dcoords = []
        for i in np.arange(len(coords)):
            if coords[i][:19] == 'dimensioncoordinate':
                dcoords.append(coords[i])
                
                
        # Extract the dimension coords that are data axes
        data_coords = []
        for i in np.arange(len(daxes)):
            val = daxes[i]
            coord = None
            for j in np.arange(len(dcoords)):          
                if daxes[i] == field.get_data_axes(dcoords[j])[0]:
                    coord = dcoords[j]      
        
            if coord is not None:
                data_coords.append(coord)

               
        # Extract dimension coords that are not data axes 
        extra_coords = []
        for i in np.arange(len(dcoords)):
            if dcoords[i] not in data_coords:
                extra_coords.append(dcoords[i])
        
        
        # Translate data axes into X, Y, Z, T if possible
        for i in np.arange(len(data_coords)):
            if field.coord(data_coords[i]).X:
                if nx == 1:
                    data_coords[i] = 'X'
            if field.coord(data_coords[i]).Y:
                if ny == 1:
                    data_coords[i] = 'Y'            
            if field.coord(data_coords[i]).Z:
                if nz == 1:
                    data_coords[i] = 'Z'  
            if field.coord(data_coords[i]).T:
                if nt == 1:
                    data_coords[i] = 'T'        
                    
        # Reverse data_coords so they go in the normal X, Y, Z, T order
        data_coords.reverse()
        
                        
        # Assemble the coordinates in order
        all_coords = []        
        for coord in data_coords:
            all_coords.append(coord)
            
        for coord in extra_coords:
            all_coords.append(coord)            
        
        for i in np.arange(len(coords)):
            if coords[i][:19] == 'auxiliarycoordinate':
                all_coords.append(coords[i])
            

        return all_coords                    
                    
                    
                    
                    
                    
                    
                    
    def check_well_formed(field):
        ''' 
            Check the coordinates are all recognizably of the form X, Y, Z, T
            returns boolean
        ''' 
            
        coords = list(field.coords())
        mycoords = deepcopy(coords)
        
        for i in np.arange(len(coords)):
            c = field.coord(coords[i])
            if c.X:
                mycoords[i] = 'X'
            if c.Y:
                mycoords[i] = 'Y'
            if c.Z:
                mycoords[i] = 'Z'
            if c.T:
                mycoords[i] = 'T'
            
        
        # Check if the coordinates are all of the form X, Y, Z, T    
        well_formed = True
        dimension_coords = ['dimensioncoordinate0','dimensioncoordinate1','dimensioncoordinate2','dimensioncoordinate3']
        for i in np.arange(4):
            if dimension_coords[i] in mycoords:
                well_formed = False
                
        
        return well_formed
            
            
            

class Line_window(QWidget):
    '''popup window for changing line defaults'''

    def __init__(self, parent):
        super(Line_window, self).__init__()
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up the bottom buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        # Text box labels
        titleLabel = QLabel('title')
        xminLabel = QLabel('xmin')
        xmaxLabel = QLabel('xmax')
        yminLabel = QLabel('ymin')
        ymaxLabel = QLabel('ymax')
        styleLabel = QLabel('line style')
        colourLabel = QLabel('line colour')
        thicknessLabel = QLabel('line thickness')
        markerLabel = QLabel('Marker')
        markersizeLabel = QLabel('Marker size')
        markeredgecolorLabel = QLabel('Marker edge colour')
        markeredgewidthLabel = QLabel('Marker edge width')
        line_optionsLabel = QLabel('Line options')
        marker_optionsLabel = QLabel('Marker options')

        # Text boxes
        titleTextBox = QLineEdit()
        titleTextBox.setText('')
        titleTextBox.textChanged.connect(lambda: self.textbox_changed('title'))

        colourTextBox = QLineEdit()
        colourTextBox.setText('C0')
        colourTextBox.textChanged.connect(lambda: self.textbox_changed('colour'))

        thicknessTextBox = QLineEdit()
        thicknessTextBox.setText('1.0')
        thicknessTextBox.textChanged.connect(lambda: self.textbox_changed('thickness'))

        xminTextBox = QLineEdit()
        xminTextBox.setText('')
        xminTextBox.textChanged.connect(lambda: self.textbox_changed('xmin'))

        xmaxTextBox = QLineEdit()
        xmaxTextBox.setText('')
        xmaxTextBox.textChanged.connect(lambda: self.textbox_changed('xmax'))

        yminTextBox = QLineEdit()
        yminTextBox.setText('')
        yminTextBox.textChanged.connect(lambda: self.textbox_changed('ymin'))

        ymaxTextBox = QLineEdit()
        ymaxTextBox.setText('')
        ymaxTextBox.textChanged.connect(lambda: self.textbox_changed('ymax'))

        markerTextBox = QLineEdit()
        markerTextBox.setText('')
        markerTextBox.textChanged.connect(lambda: self.textbox_changed('marker'))

        markersizeTextBox = QLineEdit()
        markersizeTextBox.setText('')
        markersizeTextBox.textChanged.connect(lambda: self.textbox_changed('markersize'))

        markeredgecolorTextBox = QLineEdit()
        markeredgecolorTextBox.setText('')
        markeredgecolorTextBox.textChanged.connect(lambda: self.textbox_changed('markeredgecolor'))

        markeredgewidthTextBox = QLineEdit()
        markeredgewidthTextBox.setText('')
        markeredgewidthTextBox.textChanged.connect(lambda: self.textbox_changed('markeredgewidth'))


        # Combo boxes
        line_style_ComboBox = QComboBox()
        line_style_ComboBox.addItem('solid')
        line_style_ComboBox.addItem('dashed')
        line_style_ComboBox.addItem('dashdot')
        line_style_ComboBox.addItem('dotted')
        line_style_ComboBox.activated[str].connect(self.line_style)

        # Check boxes
        user_data_limitsCheckBox = QCheckBox("User defined plot limits")
        user_data_limitsCheckBox.setChecked(False)
        user_data_limitsCheckBox.clicked.connect(lambda: self.data_limits('automatic'))

        # Put widgets into self
        self.thicknessTextBox = thicknessTextBox
        self.titleTextBox = titleTextBox
        self.xminLabel = xminLabel
        self.xmaxLabel = xmaxLabel
        self.yminLabel = yminLabel
        self.ymaxLabel = ymaxLabel
        self.titleLabel = titleLabel
        self.styleLabel = styleLabel
        self.colourLabel = colourLabel
        self.thicknessLabel = thicknessLabel
        self.markerLabel = markerLabel
        self.markersizeLabel = markersizeLabel
        self.markeredgecolorLabel = markeredgecolorLabel
        self.markeredgewidthLabel = markeredgewidthLabel

        self.xminTextBox = xminTextBox
        self.xmaxTextBox = xmaxTextBox
        self.yminTextBox = yminTextBox
        self.ymaxTextBox = ymaxTextBox
        self.thicknessTextBox = thicknessTextBox
        self.colourTextBox = colourTextBox
        self.line_style_ComboBox = line_style_ComboBox
        self.markerTextBox = markerTextBox
        self.markersizeTextBox = markersizeTextBox
        self.markeredgecolorTextBox = markeredgecolorTextBox
        self.markeredgewidthTextBox = markeredgewidthTextBox

        self.user_data_limitsCheckBox = user_data_limitsCheckBox


        # Set the layout
        vbox = QVBoxLayout()

        hbox_titles = QHBoxLayout()
        hbox_titles.addWidget(self.titleLabel)
        hbox_titles.addWidget(self.titleTextBox)
        vbox.addLayout(hbox_titles)

        vbox.addWidget(HLine())
        vbox.addWidget(line_optionsLabel)

        hbox_type = QHBoxLayout()
        hbox_type.addWidget(self.styleLabel)
        hbox_type.addWidget(self.line_style_ComboBox)
        vbox.addLayout(hbox_type)

        hbox_thickness = QHBoxLayout()
        hbox_thickness.addWidget(self.thicknessLabel)
        hbox_thickness.addWidget(self.thicknessTextBox)
        vbox.addLayout(hbox_thickness)

        hbox_colour = QHBoxLayout()
        hbox_colour.addWidget(self.colourLabel)
        hbox_colour.addWidget(self.colourTextBox)
        vbox.addLayout(hbox_colour)

        vbox.addWidget(HLine())
        vbox.addWidget(marker_optionsLabel)

        hbox_marker = QHBoxLayout()
        hbox_marker.addWidget(self.markerLabel)
        hbox_marker.addWidget(self.markerTextBox)
        vbox.addLayout(hbox_marker)

        hbox_markersize = QHBoxLayout()
        hbox_markersize.addWidget(self.markersizeLabel)
        hbox_markersize.addWidget(self.markersizeTextBox)
        vbox.addLayout(hbox_markersize)

        hbox_markeredgecolor = QHBoxLayout()
        hbox_markeredgecolor.addWidget(self.markeredgecolorLabel)
        hbox_markeredgecolor.addWidget(self.markeredgecolorTextBox)
        vbox.addLayout(hbox_markeredgecolor)

        hbox_markeredgewidth = QHBoxLayout()
        hbox_markeredgewidth.addWidget(self.markeredgewidthLabel)
        hbox_markeredgewidth.addWidget(self.markeredgewidthTextBox)
        vbox.addLayout(hbox_markeredgewidth)

        vbox.addWidget(HLine())

        vbox.addWidget(self.user_data_limitsCheckBox)
        hbox_plot_x = QHBoxLayout()
        hbox_plot_x.addWidget(self.xminLabel)
        hbox_plot_x.addWidget(self.xminTextBox)
        hbox_plot_x.addWidget(self.xmaxLabel)
        hbox_plot_x.addWidget(self.xmaxTextBox)
        vbox.addLayout(hbox_plot_x)

        hbox_plot_y = QHBoxLayout()
        hbox_plot_y.addWidget(self.yminLabel)
        hbox_plot_y.addWidget(self.yminTextBox)
        hbox_plot_y.addWidget(self.ymaxLabel)
        hbox_plot_y.addWidget(self.ymaxTextBox)
        vbox.addLayout(hbox_plot_y)

        # The buttons
        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        vbox.addLayout(hbox_buttons)


        self.line_help = None  # No external window yet.

        self.setWindowTitle('line plot settings')
        self.setLayout(vbox)
        

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)

        self.data_limits('automatic')

        self.show()


    def line_style(self, text):
        # Set line type
        plotvars.line_style = text


    def reset(self):

        # Reset line options

        self.xminTextBox.setText('')
        self.xmaxTextBox.setText('')
        self.yminTextBox.setText('')
        self.ymaxTextBox.setText('')
        self.titleTextBox.setText('')
        self.markerTextBox.setText('')
        self.markersizeTextBox.setText('')
        self.markeredgecolorTextBox.setText('')
        self.markeredgewidthTextBox.setText('')
        self.thicknessTextBox.setText('1.0')
        self.colourTextBox.setText('C0')
        self.line_style_ComboBox.setCurrentIndex(0)

        self.user_data_limitsCheckBox.setChecked(False)
        self.data_limits('automatic')

        plotvars.line_style = 'solid'
        self.line_xmin = None
        self.line_xmax = None
        self.line_ymin = None
        self.line_ymax = None


    def textbox_changed(self, text):

        textbox = getattr(self, text + 'TextBox') 
        setattr(plotvars, 'line_' + text, textbox.text()) 



    def data_limits(self, text):

        if self.user_data_limitsCheckBox.isChecked():
            text = 'user'

        plotvars.line_limits = text

        objs = [self.xminTextBox, self.xmaxTextBox, self.yminTextBox, self.ymaxTextBox,\
                self.xminLabel, self.xmaxLabel, self.yminLabel, self.ymaxLabel]

        if plotvars.line_limits == 'user':
            color = plotvars.text_colour
            flags = [True] * 8
        else:
            color = plotvars.text_colour_insensitive
            flags = [False] * 8


        for i in np.arange(len(objs)):
            obj = objs[i]
            color = plotvars.text_colour_insensitive
            if flags[i]:
                color = plotvars.text_colour
            obj.setEnabled(flags[i])
            pal = QPalette(obj.palette())
            pal.setColor(QPalette.WindowText, QColor(color))
            obj.setPalette(pal)


    def help(self):

        if self.line_help is None:

            html = '<body><h2>Line plot options</h2>'
            html += '<h3>Line properties</h3>'
            html += 'The default line properties are a solid line of thickness 1.0 and colour C0\n'
            html += 'A list of default line colours is shown at\n'
            html += 'https://matplotlib.org/stable/users/dflt_style_changes.html\n'
            html += 'A list of named colours is shown at\n'
            html += 'https://matplotlib.org/stable/gallery/color/named_colors.html\n'
            html += '<h3>Marker properties</h3>'
            html += 'The default is for no markers to be drawn at the node points for the line.\n'
            html += 'A list of markers is available at\n'
            html += 'https://matplotlib.org/stable/api/markers_api.html\n'
            html += 'The quotes in this list are not needed in the cfview interface so a circle marker is\n'
            html += 'just an o in the marker text box.\n'
            html += 'Marker size, colour and edge width can also be specified\n'
            html += '<h3>Plot limits</h3>'
            html += 'Plot limits are generally taken from the supplied data but can be specied manually in\n'
            html += 'the four plot limits text boxes.Here xmin and xmax are the limits in the page x direction and \n'
            html += 'not the X direction i.e. longitude as specified in the data\n' 
            self.line_help = Help(html)

        self.line_help.show()
        
        
    def closeEvent(self, event):
        # Close self.parent.line_window
        self.parent.line_window = None
        self.close()
        
        


class Vector_window(QWidget):
    '''popup window for changing vector defaults'''

    def __init__(self, parent):
        super(Vector_window, self).__init__()
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up the bottom buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        # Text box labels
        vector_titleLabel = QLabel('title')
        vector_scaleLabel = QLabel('scale')
        vector_strideLabel = QLabel('stride')
        vector_ptsLabel = QLabel('pts')
        vector_key_lengthLabel = QLabel('key length')



        # Text boxes
        vector_titleTextBox = QLineEdit()
        vector_titleTextBox.setText('')
        vector_titleTextBox.textChanged.connect(lambda: self.textbox_changed('title'))

        vector_scaleTextBox = QLineEdit()
        vector_scaleTextBox.setText(str(plotvars.vector_scale))
        vector_scaleTextBox.textChanged.connect(lambda: self.textbox_changed('scale'))

        vector_strideTextBox = QLineEdit()
        vector_strideTextBox.setText('')
        vector_strideTextBox.textChanged.connect(lambda: self.textbox_changed('stride'))

        vector_ptsTextBox = QLineEdit()
        vector_ptsTextBox.setText('')
        vector_ptsTextBox.textChanged.connect(lambda: self.textbox_changed('pts'))

        vector_key_lengthTextBox = QLineEdit()
        vector_key_lengthTextBox.setText(str(plotvars.vector_key_length))
        vector_key_lengthTextBox.textChanged.connect(lambda: self.textbox_changed('key_length'))

        self.vector_titleLabel = vector_titleLabel
        self.vector_scaleLabel = vector_scaleLabel
        self.vector_strideLabel = vector_strideLabel
        self.vector_ptsLabel = vector_ptsLabel
        self.vector_key_lengthLabel = vector_key_lengthLabel
        self.vector_titleTextBox = vector_titleTextBox
        self.vector_scaleTextBox = vector_scaleTextBox
        self.vector_strideTextBox = vector_strideTextBox
        self.vector_ptsTextBox = vector_ptsTextBox
        self.vector_key_lengthTextBox = vector_key_lengthTextBox



        # Set the layout
        vbox = QVBoxLayout()


        hbox_title = QHBoxLayout()
        hbox_title.addWidget(self.vector_titleLabel)
        hbox_title.addWidget(self.vector_titleTextBox)
        vbox.addLayout(hbox_title)

        vbox.addWidget(HLine())

        hbox_scale = QHBoxLayout()
        hbox_scale.addWidget(self.vector_scaleLabel)
        hbox_scale.addWidget(self.vector_scaleTextBox)
        vbox.addLayout(hbox_scale)

        hbox_stride = QHBoxLayout()
        hbox_stride.addWidget(self.vector_strideLabel)
        hbox_stride.addWidget(self.vector_strideTextBox)
        vbox.addLayout(hbox_stride)

        hbox_pts = QHBoxLayout()
        hbox_pts.addWidget(self.vector_ptsLabel)
        hbox_pts.addWidget(self.vector_ptsTextBox)
        vbox.addLayout(hbox_pts)

        hbox_key_length = QHBoxLayout()
        hbox_key_length.addWidget(self.vector_key_lengthLabel)
        hbox_key_length.addWidget(self.vector_key_lengthTextBox)
        vbox.addLayout(hbox_key_length)


        # The buttons
        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        vbox.addLayout(hbox_buttons)


        self.vector_help = None  # No external window yet.

        self.setWindowTitle('vector plot settings')
        self.setLayout(vbox)

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)


        self.show()



    def textbox_changed(self, text):

        textbox = getattr(self, 'vector_' + text + 'TextBox') 
        setattr(plotvars, 'vector_' + text, textbox.text()) 


    def reset(self):

        # Reset vector options
       
        self.vector_titleTextBox.setText('')
        self.vector_scaleTextBox.setText('')
        self.vector_strideTextBox.setText('')
        self.vector_ptsTextBox.setText('')
        self.vector_key_lengthTextBox.setText('')

        plotvars.vector_title = ''
        plotvars.vector_scale = ''
        plotvars.vector_stride = ''
        plotvars.vector_pts = ''
        plotvars.vector_key_length = ''
        plotvars.vector_pivot = 'middle'


    def help(self):

        if self.vector_help is None:

            html = '<body><h2>Vector plot options</h2>'
            html += '<h3>Vector properties</h3>'
            html += '<b>title</b> If a title is unset then the title is the u and v fields<br>'
            html += '<b>scale</b> Initially set to 100.  A value of 50 gives vectors that are twice as long<br>'
            html += '<b>pts</b> Interpolate to this number of points in the plot x and y directions<br>'
            html += '<b>stride</b> Skip this number of points in the plot x and y directions<br>' 
            html += '<b>key_length</b> Use this value for the reference vector<br>'



            self.line_help = Help(html)

        self.line_help.show()


    def closeEvent(self, event):
        # Close parent.vector_window
        self.parent.vector_window = None
        self.close()





class Trajectory_window(QWidget):
    '''popup window for changing trajectory defaults'''

    def __init__(self, parent):
        super(Trajectory_window, self).__init__()
        self.parent = parent
        self.initUI()


    def initUI(self):

        # Set up the bottom buttons
        resetButton = QPushButton("Reset")
        resetButton.clicked.connect(self.reset)

        helpButton = QPushButton("Help")
        helpButton.clicked.connect(self.help)

        quitButton = QPushButton("Quit")
        quitButton.clicked.connect(self.closeEvent)

        # Text box labels
        trajectory_titleLabel = QLabel('title')
        trajectory_marker_shapeLabel = QLabel('Marker shape')
        trajectory_marker_sizeLabel = QLabel('Marker size')
        trajectory_marker_face_colourLabel = QLabel('Marker face colour')
        trajectory_marker_edge_colourLabel = QLabel('Marker edge colour')
        trajectory_line_colourLabel = QLabel('Line colour')
        trajectory_line_styleLabel = QLabel('Line style')
        trajectory_line_widthLabel = QLabel('Line width')
        date_description = 'Specify a plotting date range\n'
        date_description += 'Dates are of the form YYYY-MM-DD HH:MM:SS\n'
        date_description += 'with the HH:MM:SS being optional'
        trajectory_date_descriptionLabel = QLabel(date_description)
        trajectory_date_minLabel = QLabel('Date minimum')
        trajectory_date_maxLabel = QLabel('Date maximum')

        # Text boxes
        trajectory_titleTextBox = QLineEdit()
        trajectory_titleTextBox.setText('')
        trajectory_titleTextBox.textChanged.connect(lambda: self.textbox_changed('title'))

        trajectory_marker_shapeTextBox = QLineEdit()
        trajectory_marker_shapeTextBox.setText(plotvars.trajectory_marker_shape)
        trajectory_marker_shapeTextBox.textChanged.connect(lambda: self.textbox_changed('marker_shape'))

        trajectory_marker_sizeTextBox = QLineEdit()
        trajectory_marker_sizeTextBox.setText(plotvars.trajectory_marker_size)
        trajectory_marker_sizeTextBox.textChanged.connect(lambda: self.textbox_changed('marker_size'))

        trajectory_marker_face_colourTextBox = QLineEdit()
        trajectory_marker_face_colourTextBox.setText(plotvars.trajectory_marker_face_colour)
        trajectory_marker_face_colourTextBox.textChanged.connect(lambda: self.textbox_changed('marker_face_colour'))

        trajectory_marker_edge_colourTextBox = QLineEdit()
        trajectory_marker_edge_colourTextBox.setText(plotvars.trajectory_marker_edge_colour)
        trajectory_marker_edge_colourTextBox.textChanged.connect(lambda: self.textbox_changed('marker_edge_colour'))

        trajectory_line_colourTextBox = QLineEdit()
        trajectory_line_colourTextBox.setText(plotvars.trajectory_line_colour)
        trajectory_line_colourTextBox.textChanged.connect(lambda: self.textbox_changed('line_colour'))

        trajectory_line_widthTextBox = QLineEdit()
        trajectory_line_widthTextBox.setText(plotvars.trajectory_line_width)
        trajectory_line_widthTextBox.textChanged.connect(lambda: self.textbox_changed('line_width'))

        trajectory_date_minTextBox = QLineEdit()
        trajectory_date_minTextBox.setText('')
        trajectory_date_minTextBox.textChanged.connect(lambda: self.textbox_changed('date_min'))

        trajectory_date_maxTextBox = QLineEdit()
        trajectory_date_maxTextBox.setText('')
        trajectory_date_maxTextBox.textChanged.connect(lambda: self.textbox_changed('date_max'))



        # Combo boxes
        trajectory_line_style_ComboBox = QComboBox()
        trajectory_line_style_ComboBox.addItem('solid')
        trajectory_line_style_ComboBox.addItem('dashed')
        trajectory_line_style_ComboBox.addItem('dashdot')
        trajectory_line_style_ComboBox.addItem('dotted')
        trajectory_line_style_ComboBox.activated[str].connect(self.trajectory_line_style)


        # Tick boxes

        trajectory_vectorCheckBox = QCheckBox("Vector")
        trajectory_vectorCheckBox.setChecked(False)
        trajectory_vectorCheckBox.clicked.connect(self.item_clicked)


        trajectory_legendCheckBox = QCheckBox("Legend")
        trajectory_legendCheckBox.setChecked(False)
        trajectory_legendCheckBox.clicked.connect(self.item_clicked)


        self.trajectory_titleTextBox = trajectory_titleTextBox
        self.trajectory_marker_shapeTextBox = trajectory_marker_shapeTextBox
        self.trajectory_marker_sizeTextBox = trajectory_marker_sizeTextBox
        self.trajectory_marker_face_colourTextBox = trajectory_marker_face_colourTextBox
        self.trajectory_marker_edge_colourTextBox = trajectory_marker_edge_colourTextBox
        self.trajectory_line_colourTextBox = trajectory_line_colourTextBox
        self.trajectory_line_widthTextBox = trajectory_line_widthTextBox
        self.trajectory_line_style_ComboBox = trajectory_line_style_ComboBox
        self.trajectory_vectorCheckBox = trajectory_vectorCheckBox
        self.trajectory_legendCheckBox = trajectory_legendCheckBox
        self.trajectory_date_minTextBox = trajectory_date_minTextBox
        self.trajectory_date_maxTextBox = trajectory_date_maxTextBox


        # Set the layout
        vbox = QVBoxLayout()


        hbox_title = QHBoxLayout()
        hbox_title.addWidget(trajectory_titleLabel)
        hbox_title.addWidget(trajectory_titleTextBox)
        vbox.addLayout(hbox_title)

        vbox.addWidget(HLine())

        hbox_marker_shape = QHBoxLayout()
        hbox_marker_shape.addWidget(trajectory_marker_shapeLabel)
        hbox_marker_shape.addWidget(self.trajectory_marker_shapeTextBox)
        vbox.addLayout(hbox_marker_shape)

        hbox_marker_size = QHBoxLayout()
        hbox_marker_size.addWidget(trajectory_marker_sizeLabel)
        hbox_marker_size.addWidget(self.trajectory_marker_sizeTextBox)
        vbox.addLayout(hbox_marker_size)

        hbox_marker_face_colour = QHBoxLayout()
        hbox_marker_face_colour.addWidget(trajectory_marker_face_colourLabel)
        hbox_marker_face_colour.addWidget(self.trajectory_marker_face_colourTextBox)
        vbox.addLayout(hbox_marker_face_colour)

        hbox_marker_edge_colour = QHBoxLayout()
        hbox_marker_edge_colour.addWidget(trajectory_marker_edge_colourLabel)
        hbox_marker_edge_colour.addWidget(self.trajectory_marker_edge_colourTextBox)
        vbox.addLayout(hbox_marker_edge_colour)

        vbox.addWidget(HLine())

        hbox_line_colour = QHBoxLayout()
        hbox_line_colour.addWidget(trajectory_line_colourLabel)
        hbox_line_colour.addWidget(self.trajectory_line_colourTextBox)
        vbox.addLayout(hbox_line_colour)

        hbox_line_width = QHBoxLayout()
        hbox_line_width.addWidget(trajectory_line_widthLabel)
        hbox_line_width.addWidget(self.trajectory_line_widthTextBox)
        vbox.addLayout(hbox_line_width)

        hbox_line_style = QHBoxLayout()
        hbox_line_style.addWidget(trajectory_line_styleLabel)
        hbox_line_style.addWidget(self.trajectory_line_style_ComboBox)
        vbox.addLayout(hbox_line_style)

        vbox.addWidget(HLine())

        vbox.addWidget(trajectory_vectorCheckBox)
        vbox.addWidget(trajectory_legendCheckBox)

        vbox.addWidget(HLine())

        vbox.addWidget(trajectory_date_descriptionLabel)

        hbox_date_min = QHBoxLayout()
        hbox_date_min.addWidget(trajectory_date_minLabel)
        hbox_date_min.addWidget(trajectory_date_minTextBox)
        vbox.addLayout(hbox_date_min)

        hbox_date_max = QHBoxLayout()
        hbox_date_max.addWidget(trajectory_date_maxLabel)
        hbox_date_max.addWidget(trajectory_date_maxTextBox)
        vbox.addLayout(hbox_date_max)

        vbox.addWidget(HLine())


        # The buttons
        hbox_buttons = QHBoxLayout()
        hbox_buttons.addWidget(resetButton)
        hbox_buttons.addWidget(helpButton)
        hbox_buttons.addWidget(quitButton)
        vbox.addLayout(hbox_buttons)


        self.trajectory_help = None  # No external window yet.

        self.setWindowTitle('trajectory plot settings')
        self.setLayout(vbox)

        # Set theme and font
        palette = QPalette()
        palette.setColor(QPalette.Window, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Base, QColor(plotvars.background_colour))
        palette.setColor(QPalette.Button, QColor(plotvars.button_colour))
        palette.setColor(QPalette.ButtonText, QColor(plotvars.buttontext_colour))
        palette.setColor(QPalette.Highlight, QColor(plotvars.highlight_colour))
        palette.setColor(QPalette.HighlightedText, QColor(plotvars.text_colour))
        palette.setColor(QPalette.Text,  QColor(plotvars.text_colour))
        palette.setColor(QPalette.WindowText, QColor(plotvars.windowtext_colour))
        self.setPalette(palette)

        custom_font = QFont(plotvars.font, plotvars.fontsize)
        self.window().setFont(custom_font)


        self.show()


    def textbox_changed(self, text):

        textbox = getattr(self, 'trajectory_' + text + 'TextBox') 
        setattr(plotvars, 'trajectory_' + text, textbox.text()) 

    def trajectory_line_style(self, text):
        # Set line type
        plotvars.trajectory_line_style = text

    def item_clicked(self, item):
        plotvars.trajectory_vector = self.trajectory_vectorCheckBox.isChecked()
        plotvars.trajectory_legend = self.trajectory_legendCheckBox.isChecked()

    def reset(self):

        # Reset vector options
       
        self.trajectory_titleTextBox.setText('')
        self.trajectory_marker_shapeTextBox.setText('o')
        self.trajectory_marker_sizeTextBox.setText('5')
        self.trajectory_marker_face_colourTextBox.setText('red')
        self.trajectory_marker_edge_colourTextBox.setText('green')
        self.trajectory_line_colourTextBox.setText('blue')
        self.trajectory_line_widthTextBox.setText('1.0')
        self.trajectory_line_style_ComboBox.setCurrentIndex(0)
        self.trajectory_legendCheckBox.setChecked(False)
        self.trajectory_date_minTextBox.setText('')
        self.trajectory_date_maxTextBox.setText('')

        plotvars.trajectory_title = ''
        plotvars.trajectory_marker_shape = 'o'
        plotvars.trajectory_marker_size = '5'
        plotvars.trajectory_marker_face_colour = 'red'
        plotvars.trajectory_marker_edge_colour = 'green'
        plotvars.trajectory_line_colour = 'blue'
        plotvars.trajectory_line_width = '1.0'
        plotvars.trajectory_line_style ='-'
        plotvars.trajectory_legend = False
        plotvars.trajectory_date_min = ''
        plotvars.trajectory_date_max = ''


    def help(self):

        if self.trajectory_help is None:

            html = '<body><h2>Trajectory plot options</h2>'
            html += '<h3>Trajectory properties</h3>'
            html += '<b>title</b> The title of the plot<br>'
            html += '<b>marker shape</b> Marker shape - default is a circle<br>'
            html += '<b>marker size</b> Marker size<br>'
            html += '<b>face colour</b> Marker face colour<br>'
            html += '<b>edge colour</b> Marker edge colour<br>'
            html += '<b>line colour</b> Line colour<br>'
            html += '<b>line style</b> Line style - default is solid<br>'
            html += '<b>line width</b> Line width<br>'
            html += '<b>Vector direction</b> direction of the vector<br>'
            html += '<b>Legend</b> include a legend<br>'

            html += '<br>Marker options - https://matplotlib.org/stable/api/markers_api.html<br>'
            html += '<br>Line colours  - https://matplotlib.org/stable/gallery/color/named_colors.html<br>'

            self.line_help = Help(html)

        self.line_help.show()

    def closeEvent(self, event):
        # Close parent.trajectory_window
        self.parent.trajectory_window = None
        self.close()




def main(filename, defaults='~/.cfview_defaults'):
    app = QApplication(sys.argv)
    app.setStyle('Fusion')

    ex = Cfview(filename, defaults)
    sys.exit(app.exec_())

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='cfview - climate data GUI using cf-python and cf-plot')
    parser.add_argument('-d', '--defaults', default='~/.cfview_defaults', help='defaults file')
    parser.add_argument("file", nargs="*", help='netCDF, Met Office PP or fields file')
    args = parser.parse_args()

    if len(args.file) == 0:
        main(None, defaults=args.defaults)
    else:
        main(args.file, defaults=args.defaults)













