#!/usr/bin/python3
'''
Fit-o-mat - a versatile program for nonlinear least-squares fitting
Copyright (C) 2017-2025  Andreas Moeglich, University of Bayreuth, Germany
contact me at andreas.moeglich@uni-bayreuth.de

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
'''
VERSION = '0.976'

# check for modules via importlib
try:
  from importlib.util import find_spec
  importItems = 'datetime,PyQt6,sys,pathlib,functools,copy,ast,time,os,webbrowser,zipfile,io'.split(',')
  importItems += 'matplotlib,numpy,scipy,warnings'.split(',')
  FAILED_IMPORTS = []
except ImportError:
  try:
    import ctypes
    ctypes.windll.user32.MessageBoxW(None, 'Fit-o-mat cannot run because importlib is missing.\n\nTry installing missing module importlib via:\n\tpython3 -m pip install importlib\n', 'Fit-o-mat -- missing module', 0)
  except:
    print('Fit-o-mat cannot run because importlib is missing.\n')
    print('\nTry installing missing module importlib via:\n\tpython3 -m pip install importlib\n')
  exit()
  
for item in importItems:
  if(find_spec(item) is None):
    FAILED_IMPORTS.append(item)

if(len(FAILED_IMPORTS)):
  try:
    from PyQt6 import QtWidgets, QtCore
  except:
    pass
  if('QtWidgets' in globals()):
    # create application
    app = QtWidgets.QApplication([])
    # display error dialogue
    msgBox = QtWidgets.QMessageBox()
    msgBox.setWindowTitle('Fit-o-mat -- missing module')
    text = 'Fit-o-mat cannot run because the following modules are missing:<br/>'
    for item in FAILED_IMPORTS:
      text += '\n' + '&nbsp;' * 3 + '<b>' + item + '</b><br/>'
    text += '\n<br/>Try installing missing module <b>xxx</b> via:\n<br/>' + '&nbsp;' * 3 + 'python3 -m pip install <b>xxx</b>'
    msgBox.setText(text)
    msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText)
    msgBox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Abort)
    msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
    exit(msgBox.exec())
  else:
    try:
      import ctypes
      ctypes.windll.user32.MessageBoxW(None, 'Fit-o-mat cannot run because the following modules are missing\n\t' + '\n\t'.join(FAILED_IMPORTS) + '\n\nTry installing missing module xxx via:\n\tpython3 -m pip install xxx\n', 'Fit-o-mat -- missing module', 0)
    except:
      print('Fit-o-mat cannot run because the following modules are missing\n\t' + '\n\t'.join(FAILED_IMPORTS) + '\n')
      print('\nTry installing missing module xxx via:\n\tpython3 -m pip install xxx\n')
    exit()

# start proper
import datetime    # need this in rare instances when a datetime slipped through openpyxl import
from PyQt6 import QtCore, QtGui, QtWidgets
from sys import argv, exit, platform, exc_info, version_info
from pathlib import Path
from functools import partial, cmp_to_key
from copy import deepcopy
from ast import literal_eval
from time import asctime, time
from os.path import expanduser, isfile, exists, dirname, abspath
from os import getenv, makedirs
import webbrowser
import zipfile, io
try:
  from pycorn import pc_res3
  PYCORN_PRESENT = True
except:
  PYCORN_PRESENT = False

import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
import matplotlib.legend as Legend
from matplotlib.widgets import AxesWidget
from matplotlib.widgets import Lasso
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib import patheffects as PathEffects

try:
  import xlsxwriter
  XLSXWRITER_PRESENT = True
except:
  XLSXWRITER_PRESENT = False
try:
  import xlrd
  XLRD_PRESENT = True
except:
  XLRD_PRESENT = False
try:
  import openpyxl
  OPENPYXL_PRESENT = True
except:
  OPENPYXL_PRESENT = False
try:
  from pyexcel_ods3 import get_data
  ODS3_PRESENT = True
except:
  ODS3_PRESENT = False

import warnings
warnings.simplefilter('ignore')
import numpy as np
# this is to make numpy __repr__ work as it used to
# it is quite the ugly fudge but alternative would be a major revamp of state files and style sheets :(
np.set_printoptions(legacy='1.25')
import scipy.optimize as optim
import scipy.odr as odr
import scipy.stats as stats
import scipy.interpolate as interpolate


# reimplement FigureCanvas to optimize graphics refresh  
class MyFigureCanvas(FigureCanvas):
  def __init__(self, parent=None, matplotlibCanvas=None, name='superunknown'):
    super(MyFigureCanvas, self).__init__(parent)
    self.parent = parent
    self.matplotlibCanvas = matplotlibCanvas
    self.refreshCount = 0
    self.destructCounter = 0
    self.name = name
    
  def myRefresh(self):
    # wrapper function to globally set how draw updates are done
    self.refreshCount += 1
    
    # ready to destruct?
    if(self.destructCounter):
      self.destructCounter -= 1
      if(not self.destructCounter):
        self.matplotlibCanvas.destructAboutLogo()
    
    # do we have arrows to take care of?
    for entry in ['x', 'y', 'y2']:
      if(self.name == 'plot'):
        if((self.matplotlibCanvas.handleArrow[entry] != None) or (self.matplotlibCanvas.handleArrow2[entry] != None)):
          self.matplotlibCanvas.drawAxisArrow(axis=entry, redraw=False, target='plot')
      elif((self.name == 'resid') and (entry != 'y2')):
        if((self.matplotlibCanvas.handleArrowResid[entry] != None) or (self.matplotlibCanvas.handleArrowResid2[entry] != None)):
          self.matplotlibCanvas.drawAxisArrow(axis=entry, redraw=False, target='resid')
        
    # do we have split axis divider lines to take are of?
    # (the function call will also delete left-over previous divider lines)
    self.matplotlibCanvas.drawSplitAxisDivider(redraw=False, target=self.name)
    self.matplotlibCanvas.drawSplitYAxisDivider(redraw=False)
    
    # manually draw second x axis
    self.matplotlibCanvas.draw2ndXAxisTicks(axis='x', target=self.name)
    self.matplotlibCanvas.draw2ndXAxisTicks(axis='x2', target=self.name)

    # the actual draw command
    self.draw()
    
  def setDestructionCounter(self, counter=0):
    # sets up destruction of self.handlesAbout
    self.destructCounter = counter

  def getDestructionCounter(self):
    # returns destruction counter
    return self.destructCounter

# custom QTabWidget (to enable dynamic tooltips)
class myQTabWidget(QtWidgets.QTabWidget):
  def __init__(self, *args, **kwargs):
    super(myQTabWidget, self).__init__(*args, **kwargs)
    self.storedToolTips = []
    
  def toggleToolTips(self, state=True):
    # dynamically sets and unsets tab tool tips
    for tab in range(self.count()):
      if(state):
        if(len(self.storedToolTips) > tab):
          self.setTabToolTip(tab, self.storedToolTips[tab])
      else:
        if(len(self.storedToolTips) <= tab):
          self.storedToolTips.append(self.tabToolTip(tab))
        else:
          self.storedToolTips[tab] = self.tabToolTip(tab)
        self.setTabToolTip(tab, '')

# custom QTableWidgetItem for interactive display of tooltips
class QTableWidgetItemToolTip(QtWidgets.QTableWidgetItem):
  def __init__(self, *args, **kwargs):
    super(QTableWidgetItemToolTip, self).__init__(*args, **kwargs)

  # duh, QTableWidgetItem does not allow event filtering
  # have to hijack data()
  def data(self, role):
    if((role == QtCore.Qt.ItemDataRole.ToolTipRole) and (not myapp.toolTipsShow)):
      return ''
    else:
      return QtWidgets.QTableWidgetItem.data(self, role)

# custom window for picture preview
class PreviewWindow(QtWidgets.QMainWindow):
  def __init__(self, *args, **kwargs):
    super(PreviewWindow, self).__init__(*args, **kwargs)
    ###self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint)
    self.NUMBER_TILES = 15
    self.displayPreview = None

  def paintEvent(self, event):
    # draw rounded corners
    s = self.size()
    winWidth, winHeight = s.width(), s.height()
    offsetX, offsetY = 0, 0
    targetWidth, targetHeight = winWidth, winHeight
    # adjust this to fit the draw area
    if((self.displayPreview != None) and (hasattr(self.displayPreview, 'size'))):
      targetSize = self.displayPreview.size()
      targetWidth, targetHeight = targetSize.width(), targetSize.height()
      offsetX, offsetY = winWidth - targetWidth, winHeight - targetHeight
    # do the actual painting
    qp = QtGui.QPainter()
    if(qp is not None):
      qp.begin(self)
      qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
      qp.setPen(QtCore.Qt.PenStyle.NoPen)
      # draw background rectangle
      qp.setBrush(QtGui.QColor(*UI_BASE_COLOR))
      qp.drawRect(offsetX, offsetY, targetWidth, targetHeight)
      # draw checkered boxes
      qp.setBrush(QtGui.QColor(*UI_BRIGHT_COLOR))
      deltaX, deltaY = int(targetWidth / self.NUMBER_TILES), int(targetHeight / self.NUMBER_TILES)
      for dx in range(self.NUMBER_TILES):
        for dy in range(self.NUMBER_TILES):
          if((dx + dy) % 2):
            qp.drawRect(offsetX + dx * deltaX, offsetY + dy * deltaY, deltaX, deltaY)
      # fini
      qp.end()

  def setDisplayPreview(self, displayPreview):
    # tells the preview window where to paint the checker marks
    self.displayPreview = displayPreview
    if(hasattr(self.displayPreview, 'setObjectName')):
      self.displayPreview.setObjectName('preview')
    
  def keyPressEvent(self, event):
    # catches escape key
    if(event.key() == QtCore.Qt.Key.Key_Escape):
      self.close()
    else:
      # activate normal event handling
      QtWidgets.QMainWindow.keyPressEvent(self, event)

# custom QComboBox to fix Qt layout bug on Mac :(
# exploit Mac stupidity to also implement pass-through of Ctrl key presses
class QComboBoxMac(QtWidgets.QComboBox):
  def __init__(self, *args, **kwargs):
    super(QComboBoxMac, self).__init__(*args, **kwargs)
    self.setAttribute(QtCore.Qt.WidgetAttribute.WA_LayoutUsesWidgetRect)
    self.installEventFilter(self)

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def keyPressEvent(self, event):
    # pass through events with Ctrl modifier
    if(event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier):
      event.ignore()
      return
    else:
      # normal event processing
      QtWidgets.QComboBox.keyPressEvent(self, event)

# subclass delegate to format FontoBox
class FontoDelegate(QtWidgets.QStyledItemDelegate):
  def __init__(self, *args, **kwargs):
    super(FontoDelegate, self).__init__(*args, **kwargs)

  def paint(self, painter, option, index):
    if(index.row() == 0):
      painter.save()
      font = painter.font()
      font.setBold(True)
      painter.setFont(font)
      if(len(FontoBox.recentFonts)):
        painter.drawText(QtCore.QRectF(option.rect), 'Recently used fonts')
      else:
        painter.drawText(QtCore.QRectF(option.rect), 'All fonts')
      painter.restore()
    elif(len(FontoBox.recentFonts) and (index.row() == len(FontoBox.recentFonts) + 1)):
      painter.save()
      font = painter.font()
      font.setBold(True)
      painter.setFont(font)
      painter.drawText(QtCore.QRectF(option.rect), 'All fonts')
      painter.restore()
    else:
      if(index.row() < len(FontoBox.recentFonts) + 1):
        painter.save()
        if(DARK_MODE):
          brushColor = QtGui.QColor('#9999bb')
        else:
          brushColor = QtGui.QColor('#aaaaee')
        painter.setBrush(QtGui.QBrush(brushColor))
        painter.setPen(QtCore.Qt.PenStyle.NoPen)
        r = QtCore.QRect(option.rect)
        painter.drawRect(r)
        painter.restore()
    
      QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
    
# custom QComboBox for font selection
class FontoBox(QComboBoxMac):
  # set up class variable to allow communication between Font selectors
  recentFonts = []
  MAX_RECENT_FONTS = 5
  
  def __init__(self, *args, **kwargs):
    super(FontoBox, self).__init__(*args, **kwargs)
    self.fontoDelegate = FontoDelegate()
    self.setItemDelegate(self.fontoDelegate)
    self.recentItems, self.allItems, self.combinedItems = [], [], []
    self.activated.connect(self.catchActivation)
    self.doActivate = True
    
  def toggleActivate(self, state=True):
    # controls whether updating of QComboBox triggers update of recent font list
    self.doActivate = state
  
  def addItems(self, items=[]):
    # overrides normal addItem routine
    self.allItems = items
    
    # prepare combined list
    self.prepareCombinedItems()
      
    # call original function
    QtWidgets.QComboBox.addItems(self, self.combinedItems)
    
    # now disable certain items
    model = self.model()
    model.item(0).setEnabled(False)
    if(len(self.recentItems)):
      model.item(len(self.recentItems) + 1).setEnabled(False)
      
    self.setModel(model)
      
  def setMe(self, item=None):
    # sets QComboBox to correct entry
    if(item != None):
      defaultFont = 'DejaVu Sans'
      if(item in self.combinedItems):
        if(not (item in FontoBox.recentFonts)):
          self.updateRecentFonts(item=item)
        self.setCurrentText(item)
        return
      elif(defaultFont in self.combinedItems):
        self.setCurrentText(defaultFont)
        return

    # set to first entry by default
    if(len(self.combinedItems)):
      self.setCurrentIndex(1)

  def showPopup(self):
    # hijack showPopup() to update recent font list on the fly
    model = self.model()
    
    # first determine how many recent fonts we have at present
    if('All fonts' in self.combinedItems):
      index = self.combinedItems.index('All fonts')
      # strip out all recent fonts from combinedItems
      self.combinedItems = self.combinedItems[index:]
      
    # now loop through model and strip out all entries before 'All fonts'
    modelData = [model.data(model.index(i, 0)) for i in range(model.rowCount())]
    if('All fonts' in modelData):
      number = 0
      index = modelData.index('All fonts')
      while number < index:
        model.removeRow(0)
        number += 1
      
    if(len(FontoBox.recentFonts)):
      for item in sorted(FontoBox.recentFonts)[::-1]:
        self.combinedItems = [item] + self.combinedItems
        model.insertRow(0, QtGui.QStandardItem(item))
      
      self.combinedItems = ['Recently used fonts'] + self.combinedItems
      model.insertRow(0, QtGui.QStandardItem('Recently used fonts'))
      model.item(0).setEnabled(False)

      # set combo box to entry in recent fonts if it exists there
      if(self.currentText() in self.combinedItems):
        index = self.combinedItems.index(self.currentText())
        self.setCurrentIndex(index)

    self.setModel(model)

    # execute normal action
    QComboBoxMac.showPopup(self)

  def prepareCombinedItems(self):
    # helper function that updates combined font list
    if(len(FontoBox.recentFonts)):
      self.combinedItems = ['Recently used fonts'] + sorted(FontoBox.recentFonts)
    else:
      self.combinedItems = []
    self.combinedItems += ['All fonts'] + self.allItems
    
  def catchActivation(self):
    # tracks activation to update fonts accordingly
    if(self.doActivate):
      # check whether font exists at all
      if(self.currentText() in self.allItems):
        self.updateRecentFonts(self.currentText())
      else:
        pass
    
  def updateRecentFonts(self, item=None):
    # updates recent font list
    if(item != None):
      if(item in FontoBox.recentFonts):
        FontoBox.recentFonts = [item] + [i for i in FontoBox.recentFonts if (i != item)]
      else:
        FontoBox.recentFonts = [item] + FontoBox.recentFonts

      # truncate recent font list to desired length
      FontoBox.recentFonts = FontoBox.recentFonts[:FontoBox.MAX_RECENT_FONTS]
    
      # update combined list accordingly
      self.prepareCombinedItems()

# custom QRadioButton for interactive display of tooltips
class QRadioButtonToolTip(QtWidgets.QRadioButton):
  def __init__(self, *args, **kwargs):
    super(QRadioButtonToolTip, self).__init__(*args, **kwargs)
    self.installEventFilter(self)

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def paintEvent(self, event):
    if((UI_STYLE != 'default') and DARK_MODE):
      # trick Fusion style into changing outline color of radio indicator
      pal = self.palette()
      pal.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor(*UI_TEXT_COLOR))
      self.setPalette(pal)
      
    # original draw event
    QtWidgets.QRadioButton.paintEvent(self, event)

# custom QLabel for interactive display of tooltips
class QLabelToolTip(QtWidgets.QLabel):
  def __init__(self, *args, **kwargs):
    super(QLabelToolTip, self).__init__(*args, **kwargs)
    self.installEventFilter(self)

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

# classes for drawing consistent lines
class HLine(QtWidgets.QFrame):
  # draws a horizontal line
  def __init__(self, *args, **kwargs):
    super(HLine, self).__init__(*args, **kwargs)
    self.setFrameShape(QtWidgets.QFrame.Shape.HLine)
    self.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
    self.setFrameShadow(QtWidgets.QFrame.Shadow.Raised)
    self.setObjectName('hdivide')

class VLine(QtWidgets.QFrame):
  # draws a vertical line
  def __init__(self, *args, **kwargs):
    super(VLine, self).__init__(*args, **kwargs)
    self.setFrameShape(QtWidgets.QFrame.Shape.VLine)
    self.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)
    self.setObjectName('vdivide')

# a custom checkbox to deal with ui styles
class QCheckBoxToolTip(QtWidgets.QCheckBox):
  def __init__(self, *args, **kwargs):
    super(QCheckBoxToolTip, self).__init__(*args, **kwargs)
    self.installEventFilter(self)
    
  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def paintEvent(self, event):
    # original draw event
    QtWidgets.QCheckBox.paintEvent(self, event)

    if((UI_STYLE != 'default') and DARK_MODE):
      s = self.size()
      qp = QtGui.QPainter()
      if(qp is not None):
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
    
        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
        pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
        pen.setColor(QtGui.QColor(*UI_TEXT_COLOR))
        qp.setPen(pen)
    
        # overlay gradient
        if(self.isChecked()):
          grad = QtGui.QLinearGradient(0, s.height(), 0, 0)
        else:
          grad = QtGui.QLinearGradient(0, 0, 0, s.height())
        col1, col2 = [255, 255, 255], [180, 180, 180]
        grad.setColorAt(0, QtGui.QColor(*col1, 150))
        grad.setColorAt(0.2, QtGui.QColor(*col1, 0))
        grad.setColorAt(0.88, QtGui.QColor(*col2, 0))
        grad.setColorAt(1, QtGui.QColor(*col2, 120))
        qp.setBrush(QtGui.QBrush(grad))
    
        qp.drawRoundedRect(0, 3, s.height() - 6, s.height() - 6, 2, 2)
        qp.end()

# custom QPushButton to fix Qt layout bug on Mac :(
class QPushButtonMac(QtWidgets.QPushButton):
  def __init__(self, *args, **kwargs):
    super(QPushButtonMac, self).__init__(*args, **kwargs)
    self.setAttribute(QtCore.Qt.WidgetAttribute.WA_LayoutUsesWidgetRect)
    self.installEventFilter(self)

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

# subclass color button to have it work both with Fusion style and not mess up tool tips
class QPushButtonColor(QPushButtonMac):
  def __init__(self, *args, **kwargs):
    super(QPushButtonColor, self).__init__(*args, **kwargs)
    self.myColor = None
    
  def setMyColor(self, red=0, green=0, blue=0, alpha=255):
    # sets button color
    self.myColor = [red, green, blue, alpha]
    # issue update to ensure that color is displayed
    self.update()
    
  def getMyColor(self):
    # returns the current color value
    if(self.myColor == None):
      return [0, 0, 0, 1]
    
    return self.myColor

  def paintEvent(self, event):
    # original draw event
    #QPushButtonMac.paintEvent(self, event)
  
    # draws colored area in button
    if(self.myColor != None):
      s = self.size()
      qp = QtGui.QPainter()
      if(qp is not None):
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
        qp.setPen(QtCore.Qt.PenStyle.NoPen)
        qp.setBrush(QtGui.QBrush(QtGui.QColor(*self.myColor)))
        qp.drawRoundedRect(0, 0, s.width(), s.height(), 3, 3)
  
        # overlay gradient
        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
        pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
        pen.setColor(QtGui.QColor(171, 171, 171, 255))
        pen.setWidth(1)
        pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
        qp.setPen(pen)
    
        grad = QtGui.QLinearGradient(0, 0, 0, s.height())
        grad.setColorAt(0, QtGui.QColor(255, 255, 255, 150))
        grad.setColorAt(0.2, QtGui.QColor(255, 255, 255, 0))
        grad.setColorAt(0.8, QtGui.QColor(0, 0, 0, 0))
        grad.setColorAt(1, QtGui.QColor(0, 0, 0, 80))
        qp.setBrush(QtGui.QBrush(grad))
    
        qp.drawRoundedRect(0, 0, s.width(), s.height(), 3, 3)
        qp.end()

# a funky new push button
class QPushButtonCheckable(QPushButtonMac):
  def __init__(self, *args, **kwargs):
    super(QPushButtonCheckable, self).__init__(*args, **kwargs)
    self.setCheckable(True)
    self.checkMe = False
    self.padMe = 0
    self.highlightMe = False
    self.installEventFilter(self)
    self.penColor = QtGui.QColor(0, 0, 0, 255)
    self.baseStyle = 'QPushButton:hover {color: #6258b8;}\n\
                    QPushButton:checked {background-color: $1$; color: black;}\n\
                    QPushButton:checked:hover {background-color: $2$; color: white;}\n\
                    QPushButton:checked:!enabled {background-color: $3$; color: #787878;}\n'
    
  def setCheckMe(self, state):
    # controls whether to draw check mark when checked
    self.checkMe = state
    
  def setHighlightMe(self, state):
    # controls whether to highlight (checked) pushbutton
    self.highlightMe = state
    if(state):
      styleString = self.baseStyle.replace('$1$', '#70ff66')
      styleString = styleString.replace('$2$', '#90ff88')
      styleString = styleString.replace('$3$', '#c0ffbb')
    else:
      styleString = self.baseStyle.replace('$1$', '#7066ff')
      styleString = styleString.replace('$2$', '#9088ff')
      styleString = styleString.replace('$3$', '#c0bbff')
    self.setStyleSheet(styleString)

  def setPadMe(self, value):
    # controls whether to pad button
    self.padMe = value
    # activate specific style sheet
    if(self.padMe):
      self.setObjectName('listos')
    else:
      self.setObjectName('')

  def paintEvent(self, event):
    # original draw event
    QPushButtonMac.paintEvent(self, event)
    
    s = self.size()
    qp = QtGui.QPainter()
    if(qp is not None):
      qp.begin(self)
      qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
  
      pen = QtGui.QPen()
      pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
      pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
  
      # draw check mark?
      if(self.checkMe and self.isChecked()):
        pen.setColor(self.penColor)
        pen.setWidth(2)
        pen.setCapStyle(QtCore.Qt.PenCapStyle.RoundCap)
        qp.setPen(pen)
        qp.setBrush(QtCore.Qt.BrushStyle.NoBrush)
        
        path = QtGui.QPainterPath()
        path.moveTo(self.padMe + 5, self.padMe + 5)
        path.lineTo(s.width() - 5, s.height() - 5)
        path.moveTo(self.padMe + 5, s.height() - 5)
        path.lineTo(s.width() - 5, self.padMe + 5)
        qp.drawPath(path)
      
      # overlay gradient
      qp.setPen(QtCore.Qt.PenStyle.NoPen)
  
      if(self.isChecked()):
        grad = QtGui.QLinearGradient(self.padMe, s.height(), self.padMe, self.padMe)
      else:
        grad = QtGui.QLinearGradient(self.padMe, self.padMe, self.padMe, s.height())
      col1, col2 = [255, 255, 255], [180, 180, 180]
      grad.setColorAt(0, QtGui.QColor(*col1, 150))
      grad.setColorAt(0.2, QtGui.QColor(*col1, 0))
      grad.setColorAt(0.88, QtGui.QColor(*col2, 0))
      grad.setColorAt(1, QtGui.QColor(*col2, 120))
      qp.setBrush(QtGui.QBrush(grad))
  
      qp.drawRoundedRect(self.padMe, self.padMe, s.width(), s.height(), 2, 2)
      qp.end()
    
  def eventFilter(self, source, event):
    if(source == self):
      if(event.type() == QtCore.QEvent.Type.HoverEnter):
        self.hoverMe(True)
      elif(event.type() == QtCore.QEvent.Type.HoverLeave):
        self.hoverMe(False)
      elif(self.toolTip()):
        # this button has a tooltip alright
        if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
          # ignore this one
          return True
    return QtWidgets.QWidget.eventFilter(self, source, event)

  def hoverMe(self, toggle=True):
    if(toggle):
      self.penColor = QtGui.QColor(255, 255, 255, 255)
    else:
      self.penColor = QtGui.QColor(0, 0, 0, 255)
    
# a funky new push button with smaller label
class QPushButtonCheckableMini(QPushButtonCheckable):
  def __init__(self, *args, **kwargs):
    for item in ['parent', 'kind']:
      if(item in kwargs):
        self.__dict__[item] = kwargs[item]
        del kwargs[item]
      else:
        self.__dict__[item] = None
    super(QPushButtonCheckableMini, self).__init__(*args, **kwargs)
    
    # set size
    self.setMaximumSize(QtCore.QSize(int(11 * SCALEFONT + 1), int(15 * SCALEFONT + 1)))
    self.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), int(15 * SCALEFONT + 1)))
    
    # keep track of highlight
    self.isHigh = False
    
    # setPointSize is ignored, have to go via QSS :(
    self.setObjectName('mini')
    
    # install event filtering for right clicks
    self.removeEventFilter(self)
    self.installEventFilter(self)

  def setHigh(self, state=True):
    # function to change highlight status
    if(state != self.isHigh):
      self.isHigh = state
      useFont = self.font()
      useFont.setBold(self.isHigh)
      self.setFont(useFont)

  def paintEvent(self, event):
    # original draw event
    QtWidgets.QPushButton.paintEvent(self, event)

    # mark as active
    if(self.isHigh):
      s = self.size()
      penWidth = 1
      
      qp = QtGui.QPainter()
      if(qp is not None):
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
        pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
        pen.setColor(QtGui.QColor(255, 0, 0, 255))
        pen.setWidth(penWidth)
        pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
        qp.setPen(pen)
        qp.setBrush(QtCore.Qt.BrushStyle.NoBrush)
        qp.drawRoundedRect(int(penWidth / 2), int(penWidth / 2), s.width() - penWidth, s.height() - penWidth, 2, 2)
        qp.end()
    
  def eventFilter(self, source, event):
    if(source == self):
      if((event.type() == QtCore.QEvent.Type.MouseButtonPress) and (event.button() in [QtCore.Qt.MouseButton.RightButton, QtCore.Qt.MouseButton.MiddleButton])):
        if(self.parent != None):
          if(self.kind.startswith('master_')):
            if(event.button() == QtCore.Qt.MouseButton.RightButton):
              if(self.isChecked()):
                self.setChecked(False)
              splittor = self.kind.split('_')
              kind = splittor[-1]
              # activate button callback
              self.parent.toggloCallbackMaster(kind, self)
            elif(self.kind != 'master_extra'):
              kind2 = self.kind.split('_')[-1]
              self.parent.configureMeHereMyMaster(kind=kind2, callButton=self)
          else:
            try:
              if(self.kind == 'resid'):
                index = int(self.text()) - 1
              else:
                index = int(self.text())
            except:
              index = 0
            if(event.button() == QtCore.Qt.MouseButton.RightButton):
              self.parent.makeMeActive(kind=self.kind, index=index - 1)
            else:
              self.parent.configureMeHere(kind=self.kind, index=index - 1, callButton=self)
      elif(self.toolTip()):
        # this button has a tooltip alright
        if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
          # ignore this one
          return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

# custom QToolButton to fix Qt layout bug on Mac :(
class QToolButtonMac(QtWidgets.QToolButton):
  def __init__(self, *args, **kwargs):
    super(QToolButtonMac, self).__init__(*args, **kwargs)
    self.setAttribute(QtCore.Qt.WidgetAttribute.WA_LayoutUsesWidgetRect)
    self.installEventFilter(self)

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

# custom QWidget to fix Qt layout bug on Mac :(
class QWidgetMac(QtWidgets.QWidget):
  def __init__(self, *args, **kwargs):
    super(QWidgetMac, self).__init__(*args, **kwargs)
    self.setAttribute(QtCore.Qt.WidgetAttribute.WA_LayoutUsesWidgetRect)

  def paintEvent(self, event):
    # hijack paint event for customized background
    if(self.objectName() in ['out1', 'out2', 'out3']):
      # draw rounded corners
      s = self.size()
      qp = QtGui.QPainter()
      if(qp is not None):
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
        if(self.objectName() == 'out1'):
          qp.setBrush(QtCore.Qt.BrushStyle.NoBrush)
        elif(self.objectName() == 'out2'):
          if(myapp.ui.plotArea.themeUI == 'default'):
            qp.setBrush(QtGui.QColor(*UI_BASE_COLOR))
          else:
            qp.setBrush(QtGui.QColor(*UI_ALTERNATE_BASE_COLOR))
        else:
          # just for testing at present
          qp.setBrush(QtGui.QColor(255,0,0,255))
        qp.drawRoundedRect(0, 0, s.width(), s.height(), 2, 2)
        qp.end()
    else:
      QtWidgets.QWidget.paintEvent(self, event)
      
# custom QMenu for pass-through of Ctrl-key
class MuhMenu(QtWidgets.QMenu):
  def __init__(self, *args, **kwargs):
    super(MuhMenu, self).__init__(*args, **kwargs)

  def keyPressEvent(self, event):
    # pass through events with Ctrl modifier
    if(event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier):
      # somehow event pass-through not working
      # explicitly call keypressevent of main gui as pass through from QMenu fails for some reason
      myapp.keyPressEvent(event)
      event.ignore()
    else:
      # normal event processing
      QtWidgets.QMenu.keyPressEvent(self, event)

  # enable normal tab stops
  def focusNextPrevChild(self, next):
    return QtWidgets.QWidget.focusNextPrevChild(self, next)

# custom-styled QMenu
class KuhMenu(MuhMenu):
  def __init__(self, *args, **kwargs):
    super(KuhMenu, self).__init__(*args, **kwargs)
    self.borderRadius = 5
    
  def paintEvent(self, event):
    # draw rounded corners
    s = self.size()
    qp = QtGui.QPainter()
    if(qp is not None):
      qp.begin(self)
      qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
      qp.setBrush(QtGui.QColor(*UI_BASE_COLOR))
      qp.drawRoundedRect(0, 0, s.width(), s.height(), self.borderRadius, self.borderRadius)
      qp.end()

# custom QStatusbar for colored error messages
class KuhStatusBar(QtWidgets.QStatusBar):
  def __init__(self, *args, **kwargs):
    super(KuhStatusBar, self).__init__(*args, **kwargs)
    self.timer = None
    
  def showMessage(self, message='', timer=10000, color='red'):
    # parse color
    if(color == 'red'):
      color = 'rgba' + repr(UI_ALERT_COLOR)
    elif(color == 'blue'):
      color = 'rgba' + repr(UI_NOTIFICATION_COLOR)
    
    # set color
    self.setStyleSheet('QStatusBar{color:' + color + '; font-weight:bold;}')
    QtWidgets.QStatusBar.showMessage(self, message, timer)
    # set up timer to revert style sheet change (probably not needed b/c all messages should appear in same color)
    if(self.timer == None):
      self.timer = QtCore.QTimer()
      self.timer.timeout.connect(self.myTimeOut)
      self.timer.setSingleShot(True)
      self.timer.start(timer)
    else:
      # if timer already exists, reset timer
      self.timer.start(timer)
    
  def myTimeOut(self):
    # resets time sheet
    self.setStyleSheet('QStatusBar{color:black; font-weight:normal;}')
    self.timer = None
        
# a delegate for custom display of float numbers
class FloatFormatDelegate(QtWidgets.QStyledItemDelegate):
  def __init__(self):
    super(FloatFormatDelegate, self).__init__()

  def displayText(self, value, locale):
    if(type(value) == float):
      return QtWidgets.QStyledItemDelegate.displayText(self, value, locale).replace(',', '.')
    else:
      return QtWidgets.QStyledItemDelegate.displayText(self, value, locale)

  def paint(self, painter, option, index):
    # this part needed to be able to control tabel bg. independently
    if(index.row() % 2):
      # use the alternate color
      brushColor = QtGui.QColor(*UI_ALTERNATE_BASE_COLOR)
    else:
      # use the regular color
      brushColor = QtGui.QColor(*UI_BASE_COLOR)
    painter.save()
    painter.setBrush(QtGui.QBrush(brushColor))
    painter.setPen(QtCore.Qt.PenStyle.NoPen)
    r = QtCore.QRect(option.rect)
    painter.drawRect(r)
    painter.restore()

    QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)

# subclass delegate to highlight certain cells
class FloatFormatDelegateColor(FloatFormatDelegate):
  def paint(self, painter, option, index):
    # this part needed to be able to control tabel bg. independently
    if(index.row() % 2):
      # use the alternate color
      brushColor = QtGui.QColor(*UI_ALTERNATE_BASE_COLOR)
    else:
      # use the regular color
      brushColor = QtGui.QColor(*UI_BASE_COLOR)
    painter.save()
    painter.setBrush(QtGui.QBrush(brushColor))
    painter.setPen(QtCore.Qt.PenStyle.NoPen)
    r = QtCore.QRect(option.rect)
    painter.drawRect(r)
    painter.restore()

    QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
    
    retv = index.data(QtCore.Qt.ItemDataRole.UserRole)
    if (retv):
      painter.save()
      if(retv == 1):
        # good rows
        brushColor = QtCore.Qt.GlobalColor.green
      else:
        # bad rows
        brushColor = QtCore.Qt.GlobalColor.red
      painter.setBrush(QtGui.QBrush(brushColor))
      painter.setOpacity(0.4)
      painter.setPen(QtCore.Qt.PenStyle.NoPen)
      r = QtCore.QRect(option.rect)
      painter.drawRect(r)
      painter.restore()

# custom table model for speeding up data loading (QTableView vs. QTableWidget)
class TableModel(QtCore.QAbstractTableModel):
  def __init__(self, data, parent=None):
    super(TableModel, self).__init__(parent)
    self.parent = parent
    if(hasattr(data, 'tolist')):
      # deal with numpy data
      self._data = data.tolist()
    else:
      self._data = data
    self.headers = [str(i + 1) for i in range(self.columnCount())]
    self.parent.setAlternatingRowColors(True)
    self.font = QtGui.QFont()
    self.columnNumbers, self.columnAverages, self.columnMinVal, self.columnMaxVal, self.columnSum =\
      [0] * self.columnCount(), [0] * self.columnCount(), ['n/a'] * self.columnCount(), ['n/a'] * self.columnCount(), ['n/a'] * self.columnCount()

  def getFont(self):
    return self.font

  def rowCount(self, parent=None):
    return len(self._data)

  def columnCount(self, parent=None):
    return len(self._data[0]) if self.rowCount() else 0

  def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
    if role == QtCore.Qt.ItemDataRole.DisplayRole:
      row = index.row()
      if 0 <= row < self.rowCount():
        column = index.column()
        if 0 <= column < self.columnCount():
          return self._data[row][column]
    elif(role == QtCore.Qt.ItemDataRole.FontRole):
      return self.font
    elif(role == QtCore.Qt.ItemDataRole.TextAlignmentRole):
      row, column = index.row(), index.column()
      if(type(self._data[row][column]) in [float, int]):
        return QtCore.Qt.AlignmentFlag.AlignRight
      else:
        return QtCore.Qt.AlignmentFlag.AlignLeft
    elif(role == QtCore.Qt.ItemDataRole.ToolTipRole):
      column = index.column()
      if(self.columnNumbers[column] and (self.headers[column] != 'labels') and (self.headers[column] != 'labels2')):
        outstr = '<u>Stats ' + self.headers[column] + ' column</u><br/>\n'
        outstr += '&nbsp;' * 2 + 'n ' + str(self.columnNumbers[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'sum ' + self.formatNumber(self.columnSum[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'avg ' + self.formatNumber(self.columnAverages[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'min ' + self.formatNumber(self.columnMinVal[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'max ' + self.formatNumber(self.columnMaxVal[column])
        return outstr
      else:
        return
        
  def setColumnStats(self, index, number=0, csum=0, mean='n/a', minval='n/a', maxval='n/a'):
    # sets statistics information on columns
    if(index < self.columnCount()):
      self.columnNumbers[index] = number
      self.columnSum[index] = csum
      self.columnAverages[index] = mean
      self.columnMinVal[index] = minval
      self.columnMaxVal[index] = maxval
    
  def dataByIndices(self, row=0, column=0):
    if 0 <= row < self.rowCount():
      if 0 <= column < self.columnCount():
        return self._data[row][column]
        
  def headerData(self, section, orientation, role=QtCore.Qt.ItemDataRole.DisplayRole):
    if role == QtCore.Qt.ItemDataRole.DisplayRole and orientation == QtCore.Qt.Orientation.Horizontal:
      if(section < len(self.headers)):
        return self.headers[section]
      else:
        # needed for UI calls while table empty
        return ''
    return QtCore.QAbstractTableModel.headerData(self, section, orientation, role)        

  def setAllHeaders(self, headerData):
    maxIndex = min(self.columnCount(), len(headerData))
    self.headers[:maxIndex] = headerData[:maxIndex]

  def setSingleHeader(self, index, label):
    if(index < self.columnCount()):
      self.headers[index] = label
      
  def getDataRows(self, indices):
    retv = [self._data[index] for index in indices]
    return retv

  def getDataColumn(self, index):
    retv = [self._data[i][index] for i in range(self.rowCount())]
    return retv

  def getHeaders(self):
    return self.headers

  def getAllData(self):
    return self._data
  
  def setData(self, value, row, column):
    if((0 <= row < self.rowCount()) and (0 <= column < self.columnCount())):
      self._data[row][column] = value

  def pasteDataBlock(self, data, offsetRow=0, offsetColumn=0):
    for row in data:
      if(offsetRow < self.rowCount()):
        rowItems = len(row)
        self._data[offsetRow][offsetColumn:offsetColumn + rowItems] = row
      offsetRow += 1

  def formatNumber(self, number):
    # formats number for output
    NUMBER_SWITCH = 1e3
    FORMAT_DECIMAL = '{:.3f}'
    FORMAT_SCIENTIFIC = '{:.3e}'
    # determine return string
    if((self.isNumber(number)) and (np.isfinite(float(number)))):
      if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0 / NUMBER_SWITCH)):
        numberstr = FORMAT_SCIENTIFIC.format(number)
        zerostr = FORMAT_SCIENTIFIC.format(0.0)
      else:
        numberstr = FORMAT_DECIMAL.format(number)
        zerostr = FORMAT_DECIMAL.format(0.0)
      if(numberstr == zerostr):
        numberstr = '0'
    else:
      numberstr = str(number)
    
    return numberstr

  def isNumber(self, s):
    # checks whether string is a number
    try:
      float(s)
      return True
    except ValueError:
      pass
   
    try:
      import unicodedata
      unicodedata.numeric(s)
      return True
    except (TypeError, ValueError):
      pass
    
    return False

# subclass custom table model for highlighting cells
class DataTableModel(TableModel):
  def __init__(self, data, parent=None):
    super(DataTableModel, self).__init__(data, parent)
    # cellhighlighting
    self.goodRows, self.badRows = [], []
    self.selCols = []
    self.timer = None
    self.defaultTimeOut = 5000
    self.parent.setObjectName('data')
    self.columnNumbers, self.columnAverages, self.columnMinVal, self.columnMaxVal, self.columnSum =\
      [0] * self.columnCount(), [0] * self.columnCount(), ['n/a'] * self.columnCount(), ['n/a'] * self.columnCount(), ['n/a'] * self.columnCount()

  def setFontProperty(self, prop, value):
    try:
      method = getattr(self.font, 'set' + prop)
      method(value)
    except:
      pass
    
  def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
    if role == QtCore.Qt.ItemDataRole.DisplayRole:
      row = index.row()
      if 0 <= row < self.rowCount():
        column = index.column()
        if 0 <= column < self.columnCount():
          return self._data[row][column]
    elif(role == QtCore.Qt.ItemDataRole.UserRole):
      if(index.column() in self.selCols):
        if(index.row() in self.goodRows):
          return 1
        elif(index.row() in self.badRows):
          return 2
      return 0
    elif(role == QtCore.Qt.ItemDataRole.TextAlignmentRole):
      row, column = index.row(), index.column()
      if(type(self._data[row][column]) in [float, int]):
        return QtCore.Qt.AlignmentFlag.AlignRight
      else:
        return QtCore.Qt.AlignmentFlag.AlignLeft
    elif(role == QtCore.Qt.ItemDataRole.FontRole):
      return self.font
    elif(role == QtCore.Qt.ItemDataRole.ToolTipRole):
      column = index.column()
      if(self.columnNumbers[column]):
        outstr = '<u>Stats column ' + str(column + 1) + '</u><br/>\n'
        outstr += '&nbsp;' * 2 + 'n ' + str(self.columnNumbers[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'sum ' + self.formatNumber(self.columnSum[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'avg ' + self.formatNumber(self.columnAverages[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'min ' + self.formatNumber(self.columnMinVal[column]) + '<br/>\n'
        outstr += '&nbsp;' * 2 + 'max ' + self.formatNumber(self.columnMaxVal[column])
        return outstr
      else:
        return

  def setColumnStats(self, index, number=0, csum=0, mean='n/a', minval='n/a', maxval='n/a'):
    # sets statistics information on columns
    if(index < self.columnCount()):
      self.columnNumbers[index] = number
      self.columnSum[index] = csum
      self.columnAverages[index] = mean
      self.columnMinVal[index] = minval
      self.columnMaxVal[index] = maxval
    
  def setGoodBad(self, goodRows=[], badRows=[], selCols=[]):
    # updates good and bad rows
    self.goodRows, self.badRows, self.selCols = goodRows, badRows, selCols

    # set up timer to revert style sheet change (probably not needed b/c all messages should appear in same color)
    if(self.timer == None):
      self.timer = QtCore.QTimer()
      self.timer.timeout.connect(self.myTimeOut)
      self.timer.setSingleShot(True)
      self.timer.start(self.defaultTimeOut)
    else:
      # if timer already exists, reset timer
      self.timer.start(self.defaultTimeOut)
      
    # trigger update
    self.layoutChanged.emit()
    self.parent.setFocus()
    
  def myTimeOut(self):
    # resets coloring of cells
    self.goodRows, self.badRows, self.selCols = [], [], []
    self.timer = None
    # trigger update
    self.layoutChanged.emit()

# custom legend class
class MyLegend(Legend.Legend):
  def __init__(self, *args, **kwargs):
    super(MyLegend, self).__init__(*args, **kwargs)
    self.shadowDeltaX, self.shadowDeltaY = 2, -2
    self.shadowFaceColor = [0.5, 0.8, 0.9, 1.0]
    self.legendHatch, self.legendHatchMultiply, self.legendHatchLinewidth = '', 1, 1.
    self.legendEdgeFallbackColor = [0.5, 0.5, 0.5, 1.0]
    self.legendRounding = 0.2
    # ensure that _drawFrame is initialized
    self._drawFrame = True
    
  def additionalConfig(self, shadowDeltaX=None, shadowDeltaY=None, shadowFaceColor=None, legendHatch=None, legendHatchMultiply=None, legendHatchLinewidth=None, legendEdgeFallbackColor=None, legendRounding=None):
    # allows to set parameters for drop shadow
    if(shadowDeltaX != None):
      self.shadowDeltaX = shadowDeltaX
    if(shadowDeltaY != None):
      self.shadowDeltaY = shadowDeltaY
    if(shadowFaceColor != None):
      self.shadowFaceColor = shadowFaceColor
    if(legendHatch != None):
      self.legendHatch = legendHatch
    if(legendHatchMultiply != None):
      self.legendHatchMultiply = legendHatchMultiply
    if(legendHatchLinewidth != None):
      self.legendHatchLinewidth = legendHatchLinewidth
    if(legendEdgeFallbackColor != None):
      self.legendEdgeFallbackColor = legendEdgeFallbackColor
    if(legendRounding != None):
      self.legendRounding = legendRounding

  def draw(self, renderer):
    # the following is copied from matplotlib/legend.py and modified to allow customization of shadow patch
    "Draw everything that belongs to the legend."
    if not self.get_visible():
      return

    renderer.open_group('legend')
    fontsize = renderer.points_to_pixels(self._fontsize)

    # if mode == fill, set the width of the legend_box to the
    # width of the parent (minus pads)
    if self._mode in ["expand"]:
      pad = 2 * (self.borderaxespad + self.borderpad) * fontsize
      self._legend_box.set_width(self.get_bbox_to_anchor().width - pad)

    # update the location and size of the legend. This needs to
    # be done in any case to clip the figure right.
    bbox = self._legend_box.get_window_extent(renderer)
    self.legendPatch.set_bounds(bbox.x0, bbox.y0, bbox.width, bbox.height)
    self.legendPatch.set_mutation_scale(fontsize)

    if self._drawFrame:
      # this bit is modified
      useHatch = self.legendHatch * self.legendHatchMultiply
      self.legendPatch.set_hatch(useHatch)
      if(hasattr(self.legendPatch, 'set_hatch_linewidth')):
        self.legendPatch.set_hatch_linewidth(self.legendHatchLinewidth)
      if(np.isclose(sum(self.legendPatch.get_edgecolor()), 0.0)):
        # hatching is linked to edge color which is set to 0 when no edge
        # => need to trick legend here which is not a problem since we constantly generate new legend objects
        self.legendPatch.set_edgecolor(self.legendEdgeFallbackColor)
        self.legendPatch.set_linewidth(0)
      self.legendPatch.set_boxstyle("round", pad=0, rounding_size=self.legendRounding)
      # up to here
      
      if self.shadow:
        # this bit is modified
        shadow = matplotlib.patches.Shadow(self.legendPatch, self.shadowDeltaX, self.shadowDeltaY)
        shadow.set_facecolor(self.shadowFaceColor)
        if(len(self.shadowFaceColor) > 3):
          shadow.set_alpha(self.shadowFaceColor[3])
        # set edge color of shadow equal to facecolor
        shadow.set_edgecolor(self.shadowFaceColor)
        # until here
        shadow.draw(renderer)

      self.legendPatch.draw(renderer)

    self._legend_box.draw(renderer)

    renderer.close_group('legend')
    self.stale = False
   
# a custom cursor
class MyCursor(matplotlib.widgets.Cursor):
  def __init__(self, *args, **kwargs):
    super(MyCursor, self).__init__(*args, **kwargs)
    self.label = self.ax.text(1, 1, '', animated=True)
    # determine scale factor for matplotlib
    MPL_SCALEFACTOR = myapp.devicePixelRatio()
    self.label.set_fontsize(int(11 * SCALEFONT * MPL_SCALEFACTOR))
    # does not heed clip_on, mayhpas due to blitting
    self.label.set_clip_on(False)
    # need to store background for initial creation of cursor
    self.background = self.canvas.copy_from_bbox(self.ax.bbox)
    # initialize parent and ax2
    self.parent = None
    self.ax2 = None
    # initialize twin and slave
    self.twin = None
    self.twinVertical = None
    
  def setParent(self, parent=None):
    self.parent = parent
    
  def setAx2(self, ax2=None):
    self.ax2 = ax2

  def setTwin(self, twin=None):
    self.twin = twin

  def setTwinVertical(self, twinVertical=None):
    self.twinVertical = twinVertical

  def onmove(self, event):
    """on mouse motion draw the cursor if visible"""
    if self.ignore(event):
      return
    if not self.canvas.widgetlock.available(self):
      return
    if event.inaxes != self.ax:
      self.linev.set_visible(False)
      self.lineh.set_visible(False)
      self.label.set_visible(False)

      if self.needclear:
        self.canvas.draw()
        self.needclear = False
      return
    self.needclear = True
    if not self.visible:
      return
    
    self.refreshCrossHair(event)
    # check whether we are on canvas
    if ((event.xdata != None) and (event.ydata != None)):
      self._update()
      if(self.twin != None):
        self.twin._update()
      if(self.twinVertical != None):
        self.twinVertical._update()

  def refreshCrossHair(self, event):
    # update cross hair
    self.linev.set_xdata((event.xdata, event.xdata))
    self.lineh.set_ydata((event.ydata, event.ydata))
    self.linev.set_visible(self.visible and self.vertOn)
    self.lineh.set_visible(self.visible and self.horizOn)
    # update label size
    if(self.parent != None):
      currZoomLevel = self.parent.matplot.get_dpi()
      self.linev.set_linewidth(1.0 * 100.0 / currZoomLevel)
      self.lineh.set_linewidth(1.0 * 100.0 / currZoomLevel)
    # update label size
    if(self.parent != None):
      currZoomLevel = self.parent.matplot.get_dpi()
      # determine scale factor for matplotlib
      MPL_SCALEFACTOR = myapp.devicePixelRatio()
      self.label.set_fontsize(int(11 * SCALEFONT * MPL_SCALEFACTOR) * 100.0 / currZoomLevel)
    # check whether we are on canvas
    if ((event.xdata != None) and (event.ydata != None)):
      labelText = ' x: ' + self.formatNumber(event.xdata) + ' \n y: ' + self.formatNumber(event.ydata) + ' '
      if((self.parent != None) and (self.parent.isSecondAxesActive()) and (not self.parent.splitY)):
        secondX, secondY = self.parent.ax2.transData.inverted().transform((event.x, event.y))
        labelText += '\n y2: ' + self.formatNumber(secondY) + ' '
      self.label.set_text(labelText)
    # update color
    color = 'black'
    if(self.ax2 != None):
      canvasColor = self.ax2.patch.get_facecolor()
      # we need this check to deal with canvas having 'none' facecolor
      if(np.isclose(sum(canvasColor), 0.0)):
        figureColor = self.parent.matplot.get_facecolor()
        if(np.isclose(sum(figureColor), 0.0)):
          color = 'black'
        elif(sum(figureColor[0:3]) < 1.5):
          color = 'white'
      elif(sum(canvasColor[0:3]) < 1.5):
        color = 'white'
    self.label.set_color(color)
    self.lineh.set_color(color)
    self.linev.set_color(color)
    # check quadrant of plot
    if(event.x > ((self.ax.bbox.xmin + self.ax.bbox.xmax) / 2.0)):
      self.label.set_horizontalalignment('right')
    else:
      self.label.set_horizontalalignment('left')
    if(event.y > ((self.ax.bbox.ymin + self.ax.bbox.ymax) / 2.0)):
      self.label.set_verticalalignment('top')
    else:
      self.label.set_verticalalignment('bottom')
    # update label position
    self.label.set_x(event.xdata)
    self.label.set_y(event.ydata)
    self.label.set_visible(self.visible)
    # check twin
    if(self.twin != None):
      self.twin.lineh.set_visible(self.visible and self.twin.horizOn)
      self.twin.lineh.set_ydata((event.ydata, event.ydata))
      self.twin.lineh.set_color(color)
    # check twin vertical
    if(self.twinVertical != None):
      self.twinVertical.linev.set_visible(self.visible and self.twinVertical.vertOn)
      self.twinVertical.linev.set_xdata((event.xdata, event.xdata))
      self.twinVertical.linev.set_color(color)

  def getHandles(self):
    # returns handles to graphics elements
    handles = [self.linev, self.lineh, self.label]
    return handles

  def _update(self):
    if self.useblit:
      if self.background is not None:
        self.canvas.restore_region(self.background)
      # check whether axis object is visible (required for split x axis)
      if(self.ax.get_visible()):
        # include the drawing of the rectangle and lasso selectors here to get around vanishing selection box when blitting
        if(self.parent.rectangleSelector != None):
          self.parent.rectangleSelector.drawMe()
        if(self.parent.lassoSelector != None):
          self.parent.lassoSelector.drawMe()

        # draw crosshair and label last, ie in front
        self.ax.draw_artist(self.linev)
        self.ax.draw_artist(self.lineh)
        self.ax.draw_artist(self.label)
        self.canvas.blit(self.ax.bbox)
    else:
      self.canvas.draw_idle()

    return False

  def toggleVisibility(self, state=False, event=None):
    self.visible = state
    if(event != None):
      if(self.visible):
        self.refreshCrossHair(event)
        # cross hair was toggled on split axis -- should not display the cross hairs
        if(event.inaxes != self.ax):
          self.linev.set_visible(False)
          self.lineh.set_visible(False)
          self.label.set_visible(False)
      else:
        self.linev.set_visible(self.visible and self.vertOn)
        self.lineh.set_visible(self.visible and self.horizOn)
        self.label.set_visible(self.visible)
      # check whether we are on canvas
      if ((event.xdata != None) and (event.ydata != None)):
        self._update()
        if(self.twin != None):
          self.twin._update()
        if((self.twinVertical != None) and self.parent.splitY):
          self.twinVertical._update()

  def formatNumber(self, number):
    # formats number for output
    precision = 2
    NUMBER_SWITCH = 10 ** (precision + 1)
    FORMAT_DECIMAL = '{:.' + str(precision + 1) + 'f}'
    # determine return string
    if((self.isNumber(number)) and (np.isfinite(float(number)))):
      if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0 / NUMBER_SWITCH)):
        # do this in mathtext
        # calculate exponent
        if(number != 0.0):
          try:
            exponent = int(np.floor(np.log10(np.abs(number))))
          except:
            exponent = 0
          # calculate preexponent
          try:
            pre = number / (10 ** exponent)
          except:
            pre, exponent = number, 0
        # assemble string
        if(number == 0.0):
          retstr = '{:.' + str(precision) + 'f}'
          retstr = retstr.format(0.0)
          numberstr = '$\\mathdefault{' + retstr + '}$' 
        elif(np.isclose(pre, 1.0)):
          numberstr = '$\\mathdefault{10^{' + str(exponent) + '}}$'
        elif(np.isclose(pre, -1.0)):
          numberstr = '$\\mathdefault{-10^{' + str(exponent) + '}}$'
        else:
          retstr = '{:.' + str(precision) + 'f}'
          retstr = retstr.format(pre)
          # cannot use \times here b/c of xkcd font which lacks the symbol
          numberstr = '$\\mathdefault{' + retstr + '\\ x\\ 10^{' + str(exponent) + '}}$'         
      else:
        numberstr = FORMAT_DECIMAL.format(number)
    else:
      numberstr = str(number)
    
    return numberstr

  def isNumber(self, s):
    # checks whether string is a number
    try:
      float(s)
      return True
    except ValueError:
      pass
   
    try:
      import unicodedata
      unicodedata.numeric(s)
      return True
    except (TypeError, ValueError):
      pass
    
    return False

# validators that take care of commata once and for all
class MyValidInt(QtGui.QIntValidator):
  def __init__(self, *args, **kwargs):
    super(MyValidInt, self).__init__(*args, **kwargs)
    
  def validate(self, s, pos):
    if(',' in s):
      # plainly ignores commata
      s = s.replace(',', '')
      # correct pos for the character we deleted
      if(pos):
        pos -= 1
      
    # call the validator with US locale to get decimal points right
    valid = QtGui.QIntValidator()
    valid.setLocale(QtCore.QLocale(QtCore.QLocale.Language.English))
    return valid.validate(s, pos)

class MyValidFloat(QtGui.QDoubleValidator):
  def __init__(self, *args, **kwargs):
    super(MyValidFloat, self).__init__(*args, **kwargs)

  def validate(self, s, pos):
    if(',' in s):
      # plainly ignores commata
      s = s.replace(',', '.')
      
    # call the validator with US locale to get decimal points right
    valid = QtGui.QDoubleValidator()
    valid.setLocale(QtCore.QLocale(QtCore.QLocale.Language.English))
    return valid.validate(s, pos)

# a QSpinBox that finally behaves as I want
class QSpinClick(QtWidgets.QSpinBox):
  def __init__(self, argument=None):
    super(QSpinClick, self).__init__(argument)
    self._gainedFocus = False
    self._originalMousePressEvent = QtWidgets.QSpinBox.mousePressEvent
    self._originalFocusInEvent = QtWidgets.QSpinBox.focusInEvent
    self.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
    self.installEventFilter(self)
    self.setAccelerated(True)
    # implement your own adaptive increment as this only becomes standard in later PyQt versions
    self.adaptive = True
    # set up event filter
    self.thisLineEdit = self.lineEdit()
    self.thisLineEdit.installEventFilter(self)
    
  def setAdaptive(self, value=True):
    # toggles on/off adaptive size adjustment of increments
    self.adaptive = value

  def eventFilter(self, source, event):
    # filter events of child widget
    if(source == self.thisLineEdit):
      # hijack mouse button press on line edit
      if(event.type() == QtCore.QEvent.Type.MouseButtonPress):
        try:
          # this call throws error under older Python versions due to accessing protected member
          self.thisLineEdit.mousePressEvent(event)
        except:
          self.thisLineEdit.deselect()
        if(self._gainedFocus):
          self.selectAll()
          self._gainedFocus = False
        return True

    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def paintEvent(self, event):
    # original draw event
    QtWidgets.QSpinBox.paintEvent(self, event)
  
    # draws colored area in button
    if(UI_STYLE != 'default'):
      s = self.size()
      qp = QtGui.QPainter()
      if(qp is not None):
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
        qp.setBrush(QtCore.Qt.BrushStyle.NoBrush)
        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
        pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
        pen.setColor(QtGui.QColor(*UI_TEXT_COLOR))
        pen.setWidth(1)
        pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
        qp.setPen(pen)
        qp.drawRoundedRect(0, 0, s.width(), s.height(), 3, 3)
        qp.end()

  def focusInEvent(self, event):
    self._originalFocusInEvent(self, event)
    
    # determine how we got focus
    if(event.reason() == QtCore.Qt.FocusReason.MouseFocusReason):
      self._gainedFocus = True
      
  # reimplement stepBy() to fire event upon activation of arrow buttons
  def stepBy(self, step):
    # adjust increment if needed
    if(self.adaptive):
      increment = self.singleStep()
      value = self.value()
      # don't change anything if value is zero
      if(value):
        if(step > 0):
          increment = 10 ** np.floor(np.log10(np.abs(value) / 2.0))
          if(increment * 14 <= np.abs(value)):
            increment *= 2
          elif(increment * 3.0 > np.abs(value)):
            increment /= 2
        else:
          increment = 10 ** (np.ceil(np.log10(np.abs(value) / 2.00001)) - 1.0)
          if(increment * 14 < np.abs(value)):
            increment *= 2
          elif(increment * 3.0 >= np.abs(value)):
            increment /= 2
      increment = max(increment, 1)
      increment = int(increment)
      self.setSingleStep(increment)
    # normal event first
    QtWidgets.QSpinBox.stepBy(self, step)
    # then fire editing finished event
    self.editingFinished.emit()

# a QSpinBox that finally behaves as I want
class QDoubleSpinClick(QtWidgets.QDoubleSpinBox):
  def __init__(self, argument=None):
    super(QDoubleSpinClick, self).__init__(argument)
    self._gainedFocus = False
    self._originalMousePressEvent = QtWidgets.QDoubleSpinBox.mousePressEvent
    self._originalFocusInEvent = QtWidgets.QDoubleSpinBox.focusInEvent
    self.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
    self.installEventFilter(self)
    self.setDecimals(2)
    self.setAccelerated(True)
    # implement your own adaptive increment as this only becomes standard in later PyQt versions
    self.adaptive = True
    # set up event filter
    self.thisLineEdit = self.lineEdit()
    self.thisLineEdit.installEventFilter(self)
    self.setLocale(QtCore.QLocale(QtCore.QLocale.Language.English))
    
  def setAdaptive(self, value=True):
    # toggles on/off adaptive size adjustment of increments
    self.adaptive = value

  def eventFilter(self, source, event):
    # filter events of child widget
    if(source == self.thisLineEdit):
      # hijack mouse button press on line edit
      if(event.type() == QtCore.QEvent.Type.MouseButtonPress):
        try:
          # this call throws error under older Python versions due to accessing protected member
          self.thisLineEdit.mousePressEvent(event)
        except:
          self.thisLineEdit.deselect()
        if(self._gainedFocus):
          self.selectAll()
          self._gainedFocus = False
        return True
    elif(event.type() == QtCore.QEvent.Type.KeyPress):
      # capture the pesky comma to accept it as valid decimal delimiter
      if(event.text() == ','):
        nuEvent = QtGui.QKeyEvent(QtCore.QEvent.Type.KeyPress, 46, event.modifiers(), 0, 0, 0, text='.')
        try:
          # this call throws error under older Python versions due to accessing protected member
          self.thisLineEdit.keyPressEvent(nuEvent)
        except:
          pass
        return True

    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def paintEvent(self, event):
    # original draw event
    QtWidgets.QDoubleSpinBox.paintEvent(self, event)
  
    # draws colored area in button
    if(UI_STYLE != 'default'):
      s = self.size()
      qp = QtGui.QPainter()
      if(qp is not None):
        qp.begin(self)
        qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
        qp.setBrush(QtCore.Qt.BrushStyle.NoBrush)
        pen = QtGui.QPen()
        pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
        pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
        pen.setColor(QtGui.QColor(*UI_TEXT_COLOR))
        pen.setWidth(1)
        pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
        qp.setPen(pen)
        qp.drawRoundedRect(0, 0, s.width(), s.height(), 3, 3)
        qp.end()

  def focusInEvent(self, event):
    self._originalFocusInEvent(self, event)
    
    # determine how we got focus
    if(event.reason() == QtCore.Qt.FocusReason.MouseFocusReason):
      self._gainedFocus = True

  # reimplement stepBy() to fire event upon activation of arrow buttons
  def stepBy(self, step):
    # adjust increment if needed
    if(self.adaptive):
      increment = self.singleStep()
      value = self.value()
      # don't change anything if value is zero
      if(value):
        if(step > 0):
          increment = 10 ** np.floor(np.log10(np.abs(value) / 2.0))
          if(increment * 14 <= np.abs(value)):
            increment *= 2
          elif(increment * 3.0 > np.abs(value)):
            increment /= 2
        else:
          increment = 10 ** (np.ceil(np.log10(np.abs(value) / 2.00001)) - 1.0)
          if(increment * 14 < np.abs(value)):
            increment *= 2
          elif(increment * 3.0 >= np.abs(value)):
            increment /= 2
      increment = max(increment, 0.1)
      self.setSingleStep(increment)
    # normal event first
    QtWidgets.QDoubleSpinBox.stepBy(self, step)
    # then fire editing finished event
    self.editingFinished.emit()

# a QLineEdit that finally behaves as I want
class QLineEditClick(QtWidgets.QLineEdit):
  def __init__(self, argument=None):
    super(QLineEditClick, self).__init__(argument)
    self._gainedFocus = False
    self._originalMousePressEvent = QtWidgets.QLineEdit.mousePressEvent
    self._originalFocusInEvent = QtWidgets.QLineEdit.focusInEvent
    self.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
    self.installEventFilter(self)
    # force English formatting ... but it is not quite as easy, also need to modify validator and setText()
    self.setLocale(QtCore.QLocale(QtCore.QLocale.Language.English))

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def focusInEvent(self, event):
    self._originalFocusInEvent(self, event)
    self.selectAll()
    
    # determine how we got focus
    if(event.reason() == QtCore.Qt.FocusReason.MouseFocusReason):
      self._gainedFocus = True

  def setText(self, text):
    # kill the comma encore
    if(self.validator() != None):
      text = text.replace(',', '.')
    QtWidgets.QLineEdit.setText(self, text)
    # ensure that long entries are scrolled to beginning
    self.setCursorPosition(0)
    
  def focusOutEvent(self, event):
    # ensure that long entries are scrolled to beginning
    QtWidgets.QLineEdit.focusOutEvent(self, event)
    self.setCursorPosition(0)

  def mousePressEvent(self, event):
    self._originalMousePressEvent(self, event)
    if(self._gainedFocus):
      self.selectAll()
      self._gainedFocus = False

# a QLineEdit that rounds numbers for display purposes
class QLineEditClickExtra(QLineEditClick):
  def __init__(self, argument=None):
    super(QLineEditClickExtra, self).__init__(argument)
    # additionally define formatter function
    self.formatString = None
    # and variable that holds the actual numerical value of the entry
    self.value = None
    
  def setFormatString(self, s):
    self.formatString = s
    
  def setValue(self, value):
    self.value = value
    self.setText(self.formatValue())

  # override focusIn event
  def focusInEvent(self, event):
    QtWidgets.QLineEdit.setText(self, str(self.value))
    self._originalFocusInEvent(self, event)
    self.selectAll()
    
    # determine how we got focus
    if(event.reason() == QtCore.Qt.FocusReason.MouseFocusReason):
      self._gainedFocus = True

  # override focusOut event
  def focusOutEvent(self, event):
    QtWidgets.QLineEdit.focusOutEvent(self, event)
    # assign current value
    try:
      self.value = float(self.text())
    except:
      pass
    if(self.formatString == None):
      QtWidgets.QLineEdit.setText(self, str(self.value))
    else:
      QtWidgets.QLineEdit.setText(self, self.formatValue())
    # ensure that long entries are scrolled to beginning
    self.setCursorPosition(0)
    
  def formatValue(self):
    # formats value according to format string
    # also strips away trailing zeros
    if(self.formatString == None):
      valueString = str(self.value)
    else:
      valueString = self.formatString.format(self.value)
      # check for scientific format
      if(('e' in valueString) or ('E' in valueString)):
        if('e' in valueString):
          pre, exp = valueString.split('e')
        else:
          pre, exp = valueString.split('e')
  
        while((len(pre) > 1) and (pre.endswith('0') or pre.endswith('.'))):
          pre = pre[:-1]
          
        valueString = pre + 'e' + exp
      else:
        while((len(valueString) > 1) and ('.' in valueString) and (valueString.endswith('0') or valueString.endswith('.'))):
          valueString = valueString[:-1]
        
    return valueString

# menu for transforming columns in the data table
class TransformerMenu(MuhMenu):
  def __init__(self, parent=None, col=1, formula=''):
    super(TransformerMenu, self).__init__()
    self.parent = parent
    self.col = col
    self.formula = formula
      
    # set up GUI
    self.buildRessource()

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      MuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.transformHeadingLabel = QtWidgets.QLabel('Transform column')
    useFont = self.transformHeadingLabel.font()
    useFont.setBold(True)
    self.transformHeadingLabel.setFont(useFont)
    self.vLayout.addWidget(self.transformHeadingLabel)
    
    # formula entry
    self.transformGroup = QtWidgets.QWidget()
    self.vLayout.addWidget(self.transformGroup)
    
    self.transformLayout = QtWidgets.QHBoxLayout(self.transformGroup)
    self.transformLayout.setContentsMargins(*[2]*4)
    self.transformLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.transformLabel = QtWidgets.QLabel('C' + str(self.col + 1) + ' = ')
    self.transformLabel.setMaximumHeight(int(BASE_SIZE))
    self.transformLabel.setMinimumHeight(int(BASE_SIZE))
    self.transformLayout.addWidget(self.transformLabel)

    self.transformEntry = QLineEditClick()
    self.transformEntry.setToolTip('Formula for column transformation.\nUse C1, C2, .. to denote 1st, 2nd, .. column in the data sheet.\nUse ROW to denote row index.')
    self.transformEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.transformEntry.setMaximumSize(QtCore.QSize(int(150 * SCALEFONT), int(BASE_SIZE)))
    if(self.formula == ''):
      self.transformEntry.setText('C' + str(self.col + 1) + ' + ROW')
    else:
      self.transformEntry.setText(self.formula)
    self.transformLayout.addWidget(self.transformEntry)
    
    # button
    self.transformButton = QPushButtonMac()
    self.transformButton.setText('Transform!')
    self.transformButton.setToolTip('Perform column transformation\n(previous cell contents will be overwritten)')
    self.transformButton.setMaximumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
    self.transformButton.setMinimumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
    self.transformButton.clicked.connect(self.doTransform)
    self.vLayout.addWidget(self.transformButton)

    self.setFocus()    
    self.focusNextChild()

  def doTransform(self):
    # starts transformation in parent object
    formula = self.transformEntry.text()
    self.parent.doTransformer(self.col, formula)
    self.close()

# menu for tabulating current function
class TabulaRasaMenu(MuhMenu):
  def __init__(self, parent=None, col=1, factor=0):
    super(TabulaRasaMenu, self).__init__()
    self.parent = parent
    self.col = col
    self.factor = factor
    self.validFloat = MyValidFloat()
      
    # set up GUI
    self.buildRessource()

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      MuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.tabulateHeadingLabel = QtWidgets.QLabel('Tabulate function')
    useFont = self.tabulateHeadingLabel.font()
    useFont.setBold(True)
    self.tabulateHeadingLabel.setFont(useFont)
    self.vLayout.addWidget(self.tabulateHeadingLabel)
    
    # formula entry
    self.tabulateGroup = QtWidgets.QWidget()
    self.vLayout.addWidget(self.tabulateGroup)
    
    self.tabulateLayout = QtWidgets.QHBoxLayout(self.tabulateGroup)
    self.tabulateLayout.setContentsMargins(*[2]*4)
    self.tabulateLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.tabulateLabel = QtWidgets.QLabel('Noise')
    self.tabulateLabel.setMaximumHeight(int(BASE_SIZE))
    self.tabulateLabel.setMinimumHeight(int(BASE_SIZE))
    self.tabulateLayout.addWidget(self.tabulateLabel)

    self.tabulateRandomEntry = QLineEditClick()
    self.tabulateRandomEntry.setToolTip('Amplitude of normally distributed noise added to data.')
    self.tabulateRandomEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.tabulateRandomEntry.setMaximumSize(QtCore.QSize(int(150 * SCALEFONT), int(BASE_SIZE)))
    self.tabulateRandomEntry.setText(str(self.factor))
    self.tabulateRandomEntry.setValidator(self.validFloat)
    self.tabulateRandomEntry.editingFinished.connect(self.sanityCheckEntry)
    self.tabulateLayout.addWidget(self.tabulateRandomEntry)
    
    # button
    self.tabulateButton = QPushButtonMac()
    self.tabulateButton.setText('Tabulate!')
    self.tabulateButton.setToolTip('Tabulate currently active function\n(previous cell contents will be overwritten)')
    self.tabulateButton.setMaximumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
    self.tabulateButton.setMinimumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
    self.tabulateButton.clicked.connect(self.doTabulate)
    self.vLayout.addWidget(self.tabulateButton)

    self.setFocus()    
    self.focusNextChild()
    
  def sanityCheckEntry(self):
    # ensures that only float numbers are entered
    value = self.tabulateRandomEntry.text()
    try:
      factor = float(value)
    except:
      factor = 0
      
    if(factor <= 0):
      self.tabulateRandomEntry.setText('0.0')

  def doTabulate(self):
    # starts transformation in parent object
    try:
      factor = float(self.tabulateRandomEntry.text())
    except:
      factor = 0
    self.parent.doTabulate(self.col, factor)
    self.close()

# the data table widget
class DataTable(QtWidgets.QTableView):
  def __init__(self, parent=None):
    super(DataTable, self).__init__(parent)
    self.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
    self.parent = parent
    self.tableModel = None
    self.currentRow, self.currentCol = 0, 0
    self.pageStep = 20
    self.minColWidth = 35
    self.storeFontMetrics = False
    self.selectedRows = []

    # set up and connect table header
    hheader = self.horizontalHeader()
    self.storedToolTip = '- Click left to assign role of column\n- Click right to sort table by column values\n- Ctrl key and click left to transform column\n- Ctrl key and click right to tabulate function for chosen x values'
    hheader.setToolTip(self.storedToolTip)
    hheader.sectionClicked.connect(self.changeRole)
    hheader.setContextMenuPolicy(QtCore.Qt.ContextMenuPolicy.CustomContextMenu)
    hheader.customContextMenuRequested.connect(self.sortitsch)

    btn = self.findChild(QtWidgets.QAbstractButton)
    self.storedCornerTip = 'Click to select entire table'
    btn.setToolTip(self.storedCornerTip)

    # set delegate
    self.setItemDelegate(FloatFormatDelegateColor())

    # set up namespace
    # import numpy again
    import numpy as np
    # import common functions from numpy for ease of access
    from numpy import abs, arccos, arcsin, arctan, exp, cos, cosh, log, log2, log10, power, sin, sinh, sqrt, tan, tanh
    self.mySpace = locals()
    self.storeFormula, self.storeTabulateFactor = '', 0.0
    
    # settings for zoom of data table
    self.zoomLevels = [3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, 80, 90, 100]
    self.currZoom = -1

  def selectionChanged(self, selected=None, deselected=None):
    # override selectionChanged() to streamline tracking selected rows
    # reset selectedRows
    self.selectedRows = []
    # pass through event
    QtWidgets.QTableView.selectionChanged(self, selected, deselected)

  def toggleToolTips(self, state=True):
    # dynamically sets and unsets tool tips
    hheader = self.horizontalHeader()
    btn = self.findChild(QtWidgets.QAbstractButton)
    if(state):
      hheader.setToolTip(self.storedToolTip)
      btn.setToolTip(self.storedCornerTip)
    else:
      hheader.setToolTip('')
      btn.setToolTip('')

  def mySizeHintForColumn(self, index):
    # custom size hint for column sizing to eke out some speed
    # INIT_SEARCH ensures that initially visible cells are used
    INIT_SEARCH, MAX_SEARCH, SPACER = 20, 50, 10
    # for size calculation, truncate float numbers after 6 significant places
    PRECISION_STRING = '%.6g'
    if(not self.storeFontMetrics):
      self.storeFontMetrics = self.fontMetrics()
    
    # analyze column contents
    data = self.tableModel.getDataColumn(index)
    if(len(data) < MAX_SEARCH):
      # search through entire table
      sizes = [self.storeFontMetrics.horizontalAdvance(PRECISION_STRING % i) if type(i) == type(2.3) else self.storeFontMetrics.horizontalAdvance(str(i)) for i in data]
      if(len(sizes)):
        return max(sizes) + SPACER
      else:
        return SPACER
    else:
      # search through MAX_SEARCH randomly chosen cells
      sizes = data[:INIT_SEARCH] + np.random.choice(data[INIT_SEARCH:], size=MAX_SEARCH - INIT_SEARCH, replace=False).tolist()
      if(len(sizes)):
        maxelement = max(sizes, key = lambda i: len(PRECISION_STRING % i) if type(i) == type(2.3) else len(str(i)))
        if(type(maxelement) == type(2.3)):
          maxelement = PRECISION_STRING % maxelement
        return self.storeFontMetrics.horizontalAdvance(str(maxelement)) + SPACER
      else:
        return SPACER

  def readjustSize(self):
    # fudge function to prevent PyQt5 from uncontrollably resizing row height
    vheader = self.verticalHeader()
    vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    vheader.setDefaultSectionSize(self.rowHeight)

  def updateColumnStatistics(self, column=-1):
    # helper function to calculate column statistics
    self.columnAverages = ['n/a'] * self.tableModel.columnCount()
    if(self.tableModel.rowCount()):
      if(column + 1):
        userange = range(column, column + 1)
      else:
        userange = range(self.tableModel.columnCount())
      for index in userange:
        temp = self.tableModel.getDataColumn(index)
        temp = [i for i in temp if type(i) in [int, float]]
        if(len(temp)):
          self.tableModel.setColumnStats(index, number=len(temp), csum=np.sum(temp), mean=np.mean(temp), minval=np.min(temp), maxval=np.max(temp))
        else:
          self.tableModel.setColumnStats(index, number=0, csum=0, mean='n/a', minval='n/a', maxval='n/a')
    
  def configTable(self, dimx, dimy, retainRoles=False, retainSelection=False, init=False):
    # helper function called by different file importers
    # set row height and prevent from resizing
    self.rowHeight = int(self.fontMetrics().height() + 2)
    if(init):
      self.rowHeight = int(BASE_SIZE - 5)
    vheader = self.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    for entry in range(dimy):
      vheader.setSectionResizeMode(entry, QtWidgets.QHeaderView.ResizeMode.Fixed)
      
    # set col width
    self.colWidth = int(self.size().width() / 4.5)
    self.colWidth = min(120, self.colWidth)
    hheader = self.horizontalHeader()
    if(not init):
      maxSize = int(0.5 * self.size().width())
      # limit resizing to first 15 columns as it becomes mortally slow otherwise
      maxDimX = min(dimx, 15)
      for entry in range(maxDimX):
        hheader.setSectionResizeMode(entry, QtWidgets.QHeaderView.ResizeMode.Interactive)
        useSize = max(self.mySizeHintForColumn(entry) + 2, self.minColWidth)
        # restrict column size to max. 50% of visible area (to allow easy resizing)
        useSize = min(useSize, maxSize)
        hheader.resizeSection(entry, useSize)
    
    # set font size
    if(self.currZoom >= 0):
      font = self.tableModel.getFont()
      font.setPointSize(self.zoomLevels[self.currZoom])
    
    # set selection mode
    self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
    
    # select all
    if(not retainSelection):
      self.selectAll()
    
    # connect event for selection update
    self.selectionModel().selectionChanged.connect(partial(self.parent.updateData, True))
    self.selectionModel().currentChanged.connect(self.currentCellChanged)
    
    # calculate statistics on columns
    self.updateColumnStatistics()
    
    # assign roles to columns (-1 means role is undefined)
    if(retainRoles):
      for item in ['x', 'y', 'xerr', 'yerr', 'labels', 'labels2']:
        if(item in self.roles):
          # check whether target column is available
          if(self.roles[item] >= dimx):
            self.roles[item] = -1
        else:
          self.roles[item] = -1
    else:
      self.roles = {'x': -1, 'y': -1, 'xerr': -1, 'yerr': -1, 'labels': -1, 'labels2': -1}
      if (dimx):
        self.roles['x'] = 0
        if (dimx == 2):
          self.roles['y'] = 1
        elif(dimx >= 3):
          self.roles['y'] = 1
    self.rolestr = {'x':'x', 'y':'y', 'xerr':u'\N{GREEK CAPITAL LETTER DELTA}x', 'yerr':u'\N{GREEK CAPITAL LETTER DELTA}y', 'labels':'labels', 'labels2': 'labels2'}
    # asisgn numbered column headers
    headerData = [str(i + 1) for i in range(dimx)]
    # update headers for roles
    for key in self.roles:
      if(self.roles[key] + 1):
        headerData[self.roles[key]] = str(self.roles[key] + 1) + ' (' + self.rolestr[key] + ')'
    self.tableModel.setAllHeaders(headerData)
    self.setModel(self.tableModel)
    self.setFocus()

  def killTheComma(self):
    # processes sheet data and replaces all commata by period
    if(len(self.sheetData)):
      dimx, dimy = len(self.sheetData[0]), len(self.sheetData)
      nuData = []
      for row in self.sheetData:
        nuData.append([self.killHelper(i) for i in row])
        
      self.sheetData = nuData
      self.tableModel = DataTableModel(self.sheetData, self)
      self.configTable(dimx, dimy, retainRoles=True, retainSelection=False)
      self.selectAll()

      nuIndex = self.model().index(self.currentRow, self.currentCol)
      self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
      self.tableModel.layoutChanged.emit()
      
      # for some strange reason we have to connect the event again, grrrr! (should've been done in self.configTable())
      self.selectionModel().currentChanged.connect(self.currentCellChanged)
      
  def killHelper(self, item):
    if(type(item) in [int, float]):
      return item
    elif((type(item) == str) and (',' in item)):
      convItem = item.replace(',', '.')
      try:
        convItem = float(convItem)
        return convItem
      except:
        return item
    else:
      return item

  def generateEmptyTable(self, columnCount=4, rowCount=20):
    # intializes blank table
    blankData = [[''] * columnCount for i in range(rowCount)]
    self.sheetData = blankData
    self.tableModel = DataTableModel(self.sheetData, self)
    self.setModel(self.tableModel)
    self.configTable(columnCount, rowCount, init=True)

    self.clearSelection()
    if(rowCount):
      self.selectRow(0)
    nuIndex = self.model().index(self.currentRow, self.currentCol)
    self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    self.tableModel.layoutChanged.emit()

    # turn off multiple sheet option
    self.parent.resetSheetSpinBox(currVal=1, maxVal=1, currName='')

  def resizeTable(self, columnCount=4, rowCount=20):
    # grows/shrinks data table as needed
    if(self.tableModel != None):
      currData = self.tableModel.getAllData()
      
      # prepare new data, initialize empty
      nuData = [[''] * columnCount for i in range(rowCount)]
      cycleRow = min(len(currData), rowCount)
      if(len(currData)):
        cycleColumn = min(len(currData[0]), columnCount)
        
        for row in range(cycleRow):
          nuData[row][:cycleColumn] = currData[row][:cycleColumn]
      
        self.tableModel = DataTableModel(nuData, self)
        self.setModel(self.tableModel)
        self.sheetData = nuData
        
        # configure table
        self.configTable(columnCount, rowCount, retainRoles=True)

        # turn off multiple sheet option
        self.parent.resetSheetSpinBox(currVal=1, maxVal=1, currName='')

  def getDimension(self):
    return self.tableModel.rowCount(), self.tableModel.columnCount()

  def restoreTable(self, tableData=[]):
    # used by loadState fxn
    if(len(tableData)):
      self.currentRow, self.currentCol = 0, 0
      self.sheetData = tableData
      self.tableModel = DataTableModel(self.sheetData, self)
      self.setModel(self.tableModel)
      
      dimx, dimy = len(tableData[0]), len(tableData)
      self.configTable(dimx, dimy)
      
      nuIndex = self.model().index(self.currentRow, self.currentCol)
      self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
      self.tableModel.layoutChanged.emit()    

  def convertDatetime(self, item=None):
    # helper function to weed out obnoxious datetime objects
    try:
      if(type(item) == datetime.date):
        return '%02d/%02d/%4s' % (int(item.month), int(item.day), item.year)
      elif(type(item) == datetime.time):
        return '%02d:%02d:%02d' % (int(item.hour), int(item.minute), int(item.second))
      else:
        outstr = ''
        if(int(item.year) + int(item.month) + int(item.day)):
          # date information is present
          outstr = '%02d/%02d/%4s' % (int(item.month), int(item.day), item.year)
        if(int(item.hour) + int(item.minute) + int(item.second)):
          if(len(outstr)):
            outstr += ' '
          outstr += '%02d:%02d:%02d' % (int(item.hour), int(item.minute), int(item.second))
        return outstr
    except:
      return ''

  def loadODS(self, filename, transpose=False):
    # open ODS sheet
    QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
    QtCore.QCoreApplication.processEvents()
    
    # check for presence of pyexcel_ods3
    if(not ODS3_PRESENT):
      self.parent.parent.statusbar.showMessage('Try installing pyexcel_ods3 module to load data file ' + filename, self.parent.parent.STATUS_TIME)
    else:
      # read data into ordered dict
      continueFlag = False
      try:
        # use read_only to dramatically speed up loading of sheet
        self.wb = get_data(filename)
      except:
        self.parent.parent.statusbar.showMessage('Cannot load data file ' + filename, self.parent.parent.STATUS_TIME)
      else:
        continueFlag = True
        
      if(continueFlag):
        # update number of available sheets and current sheet
        self.sheetNames = list(self.wb.keys())
        self.parent.resetSheetSpinBox(currVal=1, maxVal=len(self.sheetNames), currName=self.sheetNames[0], silent=True)
        
        # initally assume data is on first sheet
        self.sheetData = []
        self.sheet = self.wb[self.sheetNames[0]]
  
        # ensure minimum size of table
        if(not len(self.sheet)):
          self.sheetData = [['']]
          dimx, dimy = 1, 1
        else:
          (dimx, dimy) = (len(self.sheet[0]), len(self.sheet))
        
          # populate the table
          maxLength = 0
          for entry in range(dimy):
            self.sheetData.append(self.sheet[entry])
            maxLength = max(maxLength, len(self.sheet[entry]))
            
          # adjust rows to same length
          for entry in range(dimy):
            addItems = (maxLength - len(self.sheetData[entry])) * ['']
            self.sheetData[entry].extend(addItems)
            
          # transpose data?
          if((transpose) and (len(self.sheetData))):
            transposedData = []
            for entry1 in range(len(self.sheetData[0])):
              row = []
              for entry2 in range(len(self.sheetData)):
                row.append(self.sheetData[entry2][entry1])
              transposedData.append(row)
            self.sheetData = transposedData
            dimx, dimy = dimy, dimx
        
        self.tableModel = DataTableModel(self.sheetData, self)
        self.setModel(self.tableModel)
        self.odsFlag, self.xlsxFlag, self.xlsxFilename = True, False, None
        
        # configure table
        self.configTable(dimx, dimy)

    QtWidgets.QApplication.restoreOverrideCursor()

  def loadXLS(self, filename, transpose=False):
    # open XLS sheet
    QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
    QtCore.QCoreApplication.processEvents()
    # first figure out whether this is a classic xls or a xml-based xlsx file
    splitName = filename.split('.')
    continueFlag, self.xlsxFlag, self.xlsxFilename = False, False, None
    if((len(splitName) > 1) and (splitName[-1].lower() == 'xlsx')):
      # use openpyxl
      if(not OPENPYXL_PRESENT):
        # can try to use good old xlrd for people whose Python version still supports it
        try:
          self.wb = xlrd.open_workbook(filename)
        except:
          # this exception will also catch errors due to missing xlrd (which is intended here)
          # better refer people to installing openpyxl
          self.parent.parent.statusbar.showMessage('Try installing openpyxl module to load data file ' + filename, self.parent.parent.STATUS_TIME)
        else:
          self.sheetNames = self.wb.sheet_names()
          continueFlag = True
      else:
        self.xlsxFlag = True
        try:
          # use read_only to dramatically speed up loading of sheet
          self.wb = openpyxl.load_workbook(filename, data_only=True, read_only=True)
        except:
          self.parent.parent.statusbar.showMessage('Cannot load data file ' + filename, self.parent.parent.STATUS_TIME)
        else:
          self.sheetNames = self.wb.sheetnames
          continueFlag = True
          self.xlsxFilename = filename
    else:
      if(XLRD_PRESENT):
        try:
          self.wb = xlrd.open_workbook(filename)
        except:
          self.parent.parent.statusbar.showMessage('Cannot load data file ' + filename, self.parent.parent.STATUS_TIME)
        else:
          self.sheetNames = self.wb.sheet_names()
          continueFlag = True
      else:
        self.parent.parent.statusbar.showMessage('To open old-style .xls files, install xlrd. Cannot read ' + filename, self.parent.parent.STATUS_TIME)
    
    if(continueFlag):
      # update number of available sheets and current sheet
      self.parent.resetSheetSpinBox(currVal=1, maxVal=len(self.sheetNames), currName=self.sheetNames[0], silent=True)
      
      # initally assume data is on first sheet
      self.sheetData = []
      if(self.xlsxFlag):
        # need to escape the following call in case xlsx file is not open -- unclear?
        try:
          self.sheet = self.wb.worksheets[0]
          (dimx, dimy) = (self.sheet.max_column, self.sheet.max_row)
          
          # populate the table
          for rowRead in self.sheet.values:
            row = [self.convertDatetime(i) if (type(i) in [datetime.datetime, datetime.date, datetime.time]) else i if (i != None) else '' for i in rowRead]
            self.sheetData.append(row)
        except:
          self.parent.parent.statusbar.showMessage('Experienced some problem with access to ' + filename, self.parent.parent.STATUS_TIME)
      else:
        self.sheet = self.wb.sheet_by_index(0)
        (dimx, dimy) = (self.sheet.ncols, self.sheet.nrows)
      
        # populate the table
        for entry in range(dimy):
          row = self.sheet.row_values(entry)
          self.sheetData.append(row)
        
      # transpose data?
      if((transpose) and (len(self.sheetData))):
        transposedData = []
        for entry1 in range(len(self.sheetData[0])):
          row = []
          for entry2 in range(len(self.sheetData)):
            row.append(self.sheetData[entry2][entry1])
          transposedData.append(row)
        self.sheetData = transposedData
        dimx, dimy = dimy, dimx
      
      # ensure minimum size of table
      if(not len(self.sheetData)):
        self.sheetData = [['']]
        dimx, dimy = 1, 1
  
      self.tableModel = DataTableModel(self.sheetData, self)
      self.setModel(self.tableModel)
      self.odsFlag = False
      
      # configure table
      self.configTable(dimx, dimy)
      
      # close workbook in case of openpyxl
      if(hasattr(self.wb, 'close')):
        self.wb.close()
        self.wb, self.sheet = None, None

    QtWidgets.QApplication.restoreOverrideCursor()
  
  def changeSheet(self, currVal=1, transpose=False):
    # update number of available sheets and current sheet
    self.parent.resetSheetSpinBox(currVal=currVal, maxVal=len(self.sheetNames), currName=self.sheetNames[currVal - 1])

    # changes sheet in multi-sheet Excel file
    continueFlag = False
    if(self.odsFlag):
      # initally assume data is on first sheet
      self.sheetData = []
      self.sheet = self.wb[self.sheetNames[currVal - 1]]

      # ensure minimum size of table
      if(not len(self.sheet)):
        self.sheetData = [['']]
        dimx, dimy = 1, 1
      else:
        (dimx, dimy) = (len(self.sheet[0]), len(self.sheet))
      
        # populate the table
        maxLength = 0
        for entry in range(dimy):
          self.sheetData.append(self.sheet[entry])
          maxLength = max(maxLength, len(self.sheet[entry]))
          
        # adjust rows to same length
        for entry in range(dimy):
          addItems = (maxLength - len(self.sheetData[entry])) * ['']
          self.sheetData[entry].extend(addItems)
          
        continueFlag = True
    elif(self.xlsxFlag):
      # beware that we closed the file to prevent openpyxl from locking it for other applications
      # so, we need to open it again (or, die trying)
      try:
        self.wb = openpyxl.load_workbook(self.xlsxFilename, data_only=True, read_only=True)
        self.sheet = self.wb.worksheets[currVal - 1]
        (dimx, dimy) = (self.sheet.max_column, self.sheet.max_row)
      except:
        self.parent.parent.statusbar.showMessage('Cannot load data file ' + self.xlsxFilename, self.parent.parent.STATUS_TIME)
      else:
        # populate the table
        self.sheetData, continueFlag = [], True
        for rowRead in self.sheet.values:
          row = [self.convertDatetime(i) if (type(i) in [datetime.datetime, datetime.date, datetime.time]) else i if (i != None) else '' for i in rowRead]
          self.sheetData.append(row)
        self.wb.close()
        self.wb, self.sheet = None, None
    else:
      self.sheetData, continueFlag = [], True
      self.sheet = self.wb.sheet_by_index(currVal - 1)
      (dimx, dimy) = (self.sheet.ncols, self.sheet.nrows)
      
      # populate the table
      for entry in range(dimy):
        row = self.sheet.row_values(entry)
        self.sheetData.append(row)
    
    if(continueFlag):
      # transpose data?
      if((transpose) and (len(self.sheetData))):
        transposedData = []
        for entry1 in range(len(self.sheetData[0])):
          row = []
          for entry2 in range(len(self.sheetData)):
            row.append(self.sheetData[entry2][entry1])
          transposedData.append(row)
        self.sheetData = transposedData
        dimx, dimy = dimy, dimx
        
      # ensure minimum size of table
      if(not len(self.sheetData)):
        self.sheetData = [['']]
        dimx, dimy = 1, 1
  
      self.tableModel = DataTableModel(self.sheetData, self)
      self.setModel(self.tableModel)
      
      # configure table
      self.configTable(dimx, dimy, retainRoles=True)

  def transposeTable(self):
    # takes current data table and transposes contents
    if(self.tableModel != None):
      currData = self.tableModel.getAllData()
      
      # transpose data
      if(len(currData)):
        transposedData = []
        for entry1 in range(len(currData[0])):
          row = []
          for entry2 in range(len(currData)):
            row.append(currData[entry2][entry1])
          transposedData.append(row)
        self.sheetData = transposedData
        dimx, dimy = len(self.sheetData[0]), len(self.sheetData)
        
      self.tableModel = DataTableModel(self.sheetData, self)
      self.setModel(self.tableModel)
  
      # configure table
      self.configTable(dimx, dimy, retainRoles=True)

  def loadUnicornFile(self, filename, transpose=False):
    # opens a Unicorn file
    QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
    QtCore.QCoreApplication.processEvents()
    try:
      filecontent = pc_res3(filename)
      filecontent.load()
    except:
      self.parent.parent.statusbar.showMessage('Cannot load data file ' + filename, self.parent.parent.STATUS_TIME)
    else:
      tempSheetData = []
      dimx, dimy = 0, 0
      for entry in ['UV', 'Cond', 'pH', 'Pressure', 'Temp', 'Conc']:
        if((entry in filecontent) and ('data' in filecontent[entry])):
          if(len(filecontent[entry]['data'])):
            if(len(tempSheetData)):
              # write header
              tempSheetData[0].extend([''] * (len(filecontent[entry]['data'][0]) + 1))
              tempSheetData[0][-len(filecontent[entry]['data'][0])] = entry
              # write data
              curry = len(filecontent[entry]['data'])
              # is new entry larger?
              if(curry > dimy):
                for tilt in range(curry - dimy):
                  tempSheetData.append([''] * dimx)
                dimy = curry
              # now append new columns
              for row in range(dimy - 1):
                if(row < curry):
                  tempSheetData[row + 1].extend([''] + list(filecontent[entry]['data'][row]))
                else:
                  tempSheetData[row + 1].extend([''] * (len(filecontent[entry]['data'][0]) + 1))
              # adjust dimensions
              dimx += 1 + len(filecontent[entry]['data'][0])
            else:
              # write header
              tempSheetData = [[''] * len(filecontent[entry]['data'][0])]
              tempSheetData[0][0] = entry
              # write data
              tempSheetData.extend([list(i) for i in filecontent[entry]['data']])
              dimx, dimy = len(filecontent[entry]['data'][0]), len(filecontent[entry]['data']) + 1

      # did we find any data to import?
      if(len(tempSheetData)):
        self.sheetData = tempSheetData
  
        # transpose data?
        if((transpose) and (len(self.sheetData))):
          transposedData = []
          for entry1 in range(len(self.sheetData[0])):
            row = []
            for entry2 in range(len(self.sheetData)):
              row.append(self.sheetData[entry2][entry1])
            transposedData.append(row)
          self.sheetData = transposedData
          dimx, dimy = dimy, dimx
  
        # ensure minimum size of table
        if(not len(self.sheetData)):
          self.sheetData = [['']]
          dimx, dimy = 1, 1

        self.tableModel = DataTableModel(self.sheetData, self)
        self.setModel(self.tableModel)
  
        # configure table
        self.configTable(dimx, dimy)            

    QtWidgets.QApplication.restoreOverrideCursor()

  def loadTextFile(self, filename, delimiter='\t', transpose=False):
    # open a text file
    QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
    QtCore.QCoreApplication.processEvents()
    try:
      try:
        # try opening text file, first w/ UTF-16 (use the exception to discriminate)
        with open(filename, 'r', encoding='utf-16') as readhandle:
          filecontent = readhandle.readlines()
      except:
        # only if this fails, use regular UTF-8 but ignore any errors
        with open(filename, 'r', encoding='utf-8', errors='ignore') as readhandle:
          filecontent = readhandle.readlines()
    except:
      self.parent.parent.statusbar.showMessage('Cannot load data file ' + filename, self.parent.parent.STATUS_TIME)
    else:
      # determine row and col count
      filecontent = [i.rstrip() for i in filecontent]
      dimy = len(filecontent)
      #dimx = max([i.count(delimiter) for i in filecontent]) + 1
      dimx = max([len(i.split(delimiter)) for i in filecontent])

      # turn off multiple sheet option
      self.parent.resetSheetSpinBox(currVal=1, maxVal=1, currName='')
      
      # populate the table
      maxNumberItems = 0
      self.sheetData = []
      for row in filecontent:
        splitline = row.split(delimiter)
        splitline = [float(i) if self.isNumber(i) else i for i in splitline]
        self.sheetData.append(splitline)
        maxNumberItems = np.max((maxNumberItems, len(splitline)))
      
      # fill sheetData with empty cells to make square (otherwise will have problems in TableModel)
      for entry in self.sheetData:
        while(len(entry) < maxNumberItems):
          entry.append('')

      # transpose data?
      if((transpose) and (len(self.sheetData))):
        transposedData = []
        for entry1 in range(len(self.sheetData[0])):
          row = []
          for entry2 in range(len(self.sheetData)):
            row.append(self.sheetData[entry2][entry1])
          transposedData.append(row)
        self.sheetData = transposedData
        dimx, dimy = dimy, dimx

      # ensure minimum size of table
      if(not len(self.sheetData)):
        self.sheetData = [['']]
        dimx, dimy = 1, 1

      self.tableModel = DataTableModel(self.sheetData, self)
      self.setModel(self.tableModel)

      # configure table
      self.configTable(dimx, dimy)

    QtWidgets.QApplication.restoreOverrideCursor()

  def sortitsch(self, position):
    # first check whether control key pressed -- if so, redirect to column transformations
    # allows sorting by column
    col = self.horizontalHeader().logicalIndexAt(position)
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier):
      self.tabulateFunction(col)
    else:
      # open context menu
      self.menu = MuhMenu(self)
      self.menu.setTitle('Sort by column ' + str(col + 1))
      
      for index, entry in enumerate(['sort ascending', 'sort descending']):
        action = QtGui.QAction(entry, self)
        action.triggered.connect(partial(self.sortitschHelper, col, index==1))
        self.menu.addAction(action)
        
      # apply styles to popup window
      if(QSTYLE != None):
        self.menu.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.menu.setStyleSheet(QSTYLESHEET)
        
      # display menu at current column
      menuX, menuY = self.horizontalHeader().sectionViewportPosition(col) + self.verticalHeader().width(), self.horizontalHeader().height()
      menuPos = self.mapToGlobal(QtCore.QPoint(int(menuX), int(menuY)))
      self.menu.popup(menuPos)

  def sortitschHelper(self, col, mode):
    # helper function for sort routine
    def mixedSort(col, alist, blist):
      # first analyze types
      a, b = alist[col], blist[col]
      #a, b = alist, blist
      if((type(a) == type(b)) or (isinstance(a, (int, float, complex)) and isinstance(b, (int, float, complex)))):
        return (a > b) - (a < b)
        #return cmp(a, b)
      else:
        if(isinstance(a, (int, float, complex))):
          return -1
        elif(isinstance(b, (int, float, complex))):
          return 1
        else:
          return 0

    # code continues -- generate sorted data
    currData = self.tableModel.getAllData()
    self.sheetData = sorted(currData, key=cmp_to_key(partial(mixedSort, col)))
    if(mode):
      self.sheetData = list(reversed(self.sheetData))
      
    # update table
    self.tableModel = DataTableModel(self.sheetData, self)
    self.setModel(self.tableModel)

    # configure table
    dimx, dimy = len(self.sheetData[0]), len(self.sheetData)
    self.configTable(dimx, dimy, retainRoles=True)

  def changeRole(self, col):
    # first check whether control key pressed -- if so, redirect to column transformations
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier):
      self.transformColumn(col)
    else:
      # open context menu to assign roles
      options = ['x', 'xerr', 'y', 'yerr', 'labels', 'labels2']
      self.menu = MuhMenu(self)
      self.menu.setTitle('Assign role')
      
      for entry in options:
        action = QtGui.QAction(self.rolestr[entry], self)
        action.triggered.connect(partial(self.changeRoleHelper, col, entry))
        self.menu.addAction(action)
        
      action = QtGui.QAction('none', self)
      action.triggered.connect(partial(self.clear_role, col))
      self.menu.addAction(action)
  
      # apply styles to popup window
      if(QSTYLE != None):
        self.menu.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.menu.setStyleSheet(QSTYLESHEET)
        
      # display menu at current column
      menuX, menuY = self.horizontalHeader().sectionViewportPosition(col) + self.verticalHeader().width(), self.horizontalHeader().height()
      menuPos = self.mapToGlobal(QtCore.QPoint(int(menuX), int(menuY)))
      self.menu.popup(menuPos)
  
  def changeRoleHelper(self, col, role):
    # actually assigns the new role
    # did this column have any role assigned?
    for key in self.roles:
      if(self.roles[key] == col):
        self.roles[key] = -1
    
    # is the new role already taken?
    if (self.roles[role] + 1):
      # reset old label
      self.tableModel.setSingleHeader(self.roles[role], str(self.roles[role] + 1))
      
    # assign new role
    self.roles[role] = col
    self.tableModel.setSingleHeader(col, str(col + 1) + ' (' + self.rolestr[role] + ')')
    self.tableModel.layoutChanged.emit()
    
    # trigger data update
    self.parent.updateData(docheck=True)

  def clear_role(self, col):
    # unassigns role of column
    # did this column have any role assigned?
    for key in self.roles:
      if(self.roles[key] == col):
        self.roles[key] = -1
        
    # reset label
    self.tableModel.setSingleHeader(col, str(col + 1))
    self.tableModel.layoutChanged.emit()

    # trigger data update
    self.parent.updateData(docheck=True)

  def tabulateFunction(self, col):
    # opens context menu to allow transformation of column contents
    self.menu = TabulaRasaMenu(self, col, self.storeTabulateFactor)
    self.menu.setTitle('Tabulate function')
    
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)
      
    # display menu at current column
    menuX, menuY = self.horizontalHeader().sectionViewportPosition(col) + self.verticalHeader().width(), self.horizontalHeader().height()
    menuPos = self.mapToGlobal(QtCore.QPoint(int(menuX), int(menuY)))
    self.menu.popup(menuPos)
    
  def doTabulate(self, col, factor):
    # performs the actual function tabulation
    self.storeTabulateFactor = factor
    targetCol, useCol = col, self.roles['x']
    if(not (self.roles['x'] + 1)):
      self.parent.parent.statusbar.showMessage('Cannot tabulate function as no x column is assigned in data table.', self.parent.parent.STATUS_TIME)
    else:
      # do some reporting
      goodRows, badRows = [], []
        
      # determine selected rows
      if(len(self.selectedRows)):
        selind = self.selectedRows
      else:
        selind = self.selectionModel().selectedRows()
        selind = sorted([i.row() for i in selind])
        self.selectedRows = selind
      if (len(selind)):
        # get all selected data rows
        selectedData = self.tableModel.getDataRows(selind)
        
        # reduce data to x column
        xValues = [i[useCol] for i in selectedData]
        
        # process data to weed out problematic rows
        prunedValues, prunedIndices = [], []
        for index, value in enumerate(xValues):
          # only process rows where x value is numeric and finite
          if((type(value) in [float, int]) and (not np.isnan(value)) and (not np.isinf(value))):
            prunedValues.append(value)
            prunedIndices.append(selind[index])
          else:
            badRows.append(selind[index])
        
        # prepare data tabulation
        if(len(prunedValues)):
          prunedValues = np.array(prunedValues)
          
          # do the concrete tabulation
          x, fVal = self.parent.parent.fit[self.parent.parent.activeFit].evaluateFunc(x=prunedValues)
          if(factor):
            fVal += np.random.normal(0, factor, len(fVal))
  
          # everything has worked, so assign transformed values to data table
          for index, value in enumerate(fVal):
            if((not np.isnan(value)) and (not np.isinf(value))):
              self.tableModel.setData(float(value), prunedIndices[index], targetCol)
              goodRows.append(prunedIndices[index])
            else:
              self.tableModel.setData('', prunedIndices[index], targetCol)
              badRows.append(prunedIndices[index])
            # refresh table view
            cellIndex = self.tableModel.index(prunedIndices[index], targetCol)
            self.tableModel.dataChanged.emit(cellIndex, cellIndex, [QtCore.Qt.ItemDataRole.DisplayRole])
            
          # calculate statistics on columns
          self.updateColumnStatistics(column=targetCol)
  
        self.tableModel.setGoodBad(goodRows=goodRows, badRows=badRows, selCols=[targetCol, useCol])

  def transformColumn(self, col):
    # opens context menu to allow transformation of column contents
    self.menu = TransformerMenu(self, col, self.storeFormula)
    self.menu.setTitle('Transform column')
    
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)
      
    # display menu at current column
    menuX, menuY = self.horizontalHeader().sectionViewportPosition(col) + self.verticalHeader().width(), self.horizontalHeader().height()
    menuPos = self.mapToGlobal(QtCore.QPoint(int(menuX), int(menuY)))
    self.menu.popup(menuPos)
    
  def doTransformer(self, col, formula):
    # performs the actual column transformation
    targetCol = col
    if(formula == 'C' + str(col)):
      self.storeFormula = ''
    else:
      self.storeFormula = formula
    
    # analyze formula to determine which columns to use
    useCol = []
    for col in range(self.tableModel.columnCount()):
      probeCol = 'C' + str(col + 1)
      # check for presence of probeCol in formula
      if(probeCol in formula):
        flag = False
        splitty = formula.split(probeCol)
        for entry in splitty[1:]:
          # next character in formula string is not a number
          if((entry == '') or (not entry[0].isdigit())):
            flag = True
        if(flag):
          useCol.append(col)
          
    # analyze whether we should use row
    useRow = False
    if('ROW' in formula):
      useRow = True
    
    # define transformer function
    try:
      # check whether we have at least one input column
      if(len(useCol)):
        funcstr = 'def transformThis(self, inputs'
        if(useRow):
          funcstr += ', ROW):'
        else:
          funcstr += '):'
        for index, entry in enumerate(useCol):
          funcstr += '\n\tC' + str(entry + 1) + ' = inputs[:, ' + str(index) + ']'
        funcstr += '\n\toutput = ' + formula + '\n\treturn output'
      else:
        funcstr = 'def transformThis(self, zeroInput'
        if(useRow):
          funcstr += ', ROW):'
        else:
          funcstr += '):'
        funcstr += '\n\toutput = zeroInput + ' + formula + '\n\treturn output'
        
      # generate ffunc in local namespace (this is needed for Python3 vs. Python2, bummer)
      namespace = self.mySpace
      exec(funcstr, namespace)
      # now define the new function in the object scope
      setattr(DataTable, 'transformThis', namespace['transformThis'])
    except:
      self.parent.parent.statusbar.showMessage('Error when setting transformation for column ' + str(targetCol), self.parent.parent.STATUS_TIME)
    else:
      # do the actual transform
      # do some reporting
      goodRows, badRows = [], []
        
      # determine selected rows
      if(len(self.selectedRows)):
        selind = self.selectedRows
      else:
        selind = self.selectionModel().selectedRows()
        selind = sorted([i.row() for i in selind])
        self.selectedRows = selind
      if (len(selind)):
        # get all selected data rows
        selectedData = self.tableModel.getDataRows(selind)
        
        # reduce data to columns we are interested in
        for index, entry in enumerate(selectedData):
          selectedData[index] = [entry[i] for i in useCol]
        
        # process data to weed out problematic rows
        prunedData, prunedIndices = [], []
        for index, row in enumerate(selectedData):
          # only process rows where all required cells are numeric
          checkRow = [0 if(type(i) in [float, int]) else 1 for i in row]
          if(sum(checkRow) == 0):
            prunedData.append(row)
            prunedIndices.append(selind[index])
          else:
            badRows.append(selind[index])
        
        # do the concrete transform
        if(len(prunedData)):
          prunedData = np.array(prunedData)
          try:
            # check whether we have at least one input column
            if(len(useCol)):
              if(useRow):
                ROW = np.array(prunedIndices) + 1
                transCol = self.transformThis(prunedData, ROW=ROW)
              else:
                transCol = self.transformThis(prunedData)
            else:
              zeroInput = np.array([0] * len(prunedIndices))
              if(useRow):
                ROW = np.array(prunedIndices) + 1
                transCol = self.transformThis(zeroInput, ROW=ROW)
              else:
                transCol = self.transformThis(zeroInput)

            # everything has worked, so assign transformed values to data table
            for index, value in enumerate(transCol):
              if((not np.isnan(value)) and (not np.isinf(value))):
                self.tableModel.setData(float(value), prunedIndices[index], targetCol)
                goodRows.append(prunedIndices[index])
              else:
                self.tableModel.setData('', prunedIndices[index], targetCol)
                badRows.append(prunedIndices[index])
              # refresh table view
              cellIndex = self.tableModel.index(prunedIndices[index], targetCol)
              self.tableModel.dataChanged.emit(cellIndex, cellIndex, [QtCore.Qt.ItemDataRole.DisplayRole])
          except:
            self.parent.parent.statusbar.showMessage('Error when applying transformation for column ' + str(targetCol), self.parent.parent.STATUS_TIME)

          # calculate statistics on columns
          self.updateColumnStatistics(column=targetCol)
  
        self.tableModel.setGoodBad(goodRows=goodRows, badRows=badRows, selCols=[targetCol])

  def hasComma(self):
    # cycles through selected cells and checks for presence of comma
    selind = self.selectionModel().selectedIndexes()
    retv = False
    index = 0
    while((not retv) and (index < len(selind))):
      if((type(selind[index].data()) == str) and (',' in selind[index].data())):
        retv = True
      index += 1

    return retv
    
  def getData(self, needXErr=True, needYErr=True):
    # returns selected data as numpy array
    # argl, selectionModel is super slow ... we should maybe store the selected rows somewhere in the object and
    # update it on selection changed
    # determine selected rows
    if(len(self.selectedRows)):
      selind = self.selectedRows
    else:
      selind = self.selectionModel().selectedRows()
      selind = sorted([i.row() for i in selind])
      self.selectedRows = selind
     
    # retrieve data from table
    retv, roles = [], []
    # check whether at least x and y assigned
    if (((self.roles['x'] + 1) or (self.roles['labels'] + 1)) and (self.roles['y'] + 1)):
      if (len(selind)):
        # get all selected data rows
        selectedData = self.tableModel.getDataRows(selind)

        # deal with labels separately to allow non-numerical entries here
        if(self.roles['labels'] > -1):
          selectedLabels = []
          index = self.roles['labels']
          for entry in selectedData:
            selectedLabels.append(entry[index])
        if(self.roles['labels2'] > -1):
          selectedLabels2 = []
          index = self.roles['labels2']
          for entry in selectedData:
            selectedLabels2.append(entry[index])
        
        # reduce data to columns we are interested in
        activeKeys = ['x', 'xerr', 'y', 'yerr']
        if(not needXErr):
          activeKeys.pop(activeKeys.index('xerr'))
        if(not needYErr):
          activeKeys.pop(activeKeys.index('yerr'))
        activeKeys = [key for key in activeKeys if (self.roles[key] > -1)]
        indices = [self.roles[key] for key in activeKeys]
        for index, entry in enumerate(selectedData):
          selectedData[index] = [entry[i] for i in indices]
        
        # do some reporting
        goodRows, badRows = [], []
        
        # prepare list for numpy array
        prunedData, no_items = [], len(activeKeys)
        for index, row in enumerate(selectedData):
          types = [1 if type(i) in [int, float] else 0 for i in row]
          if(np.sum(types) == no_items):
            # only numerical entries on row
            if(self.roles['labels'] > -1):
              row.append(selectedLabels[index])
            if(self.roles['labels2'] > -1):
              row.append(selectedLabels2[index])
            prunedData.append(row)
            goodRows.append(selind[index])
          elif(np.sum(types) > 0):
            # at least one numerical entry on row
            lengths = [len(i) for index, i in enumerate(row) if(not types[index])]
            if(np.sum(lengths) == 0):
              # all other cells empty => replace by zero
              row = [i if types[index] else 0.0 for index, i in enumerate(row)]
              if(self.roles['labels'] > -1):
                row.append(selectedLabels[index])
              if(self.roles['labels2'] > -1):
                row.append(selectedLabels2[index])
              prunedData.append(row)
              goodRows.append(selind[index])
            else:
              badRows.append(selind[index])
          else:
            # check for completely empty row
            lengths = [len(i) for index, i in enumerate(row) if(not types[index])]
            if(np.sum(lengths) != 0):
              # only spaces in line
              badRows.append(selind[index])
              
        # can we retrieve table headers?
        xlabel, ylabel = '', ''
        if(len(goodRows) and ((min(goodRows) - 1) in badRows)):
          if((self.roles['x'] + 1) or (self.roles['labels'] + 1)):
            if((min(goodRows) - 1) in selind):
              if('x' in activeKeys):
                xlabel = str(selectedData[selind.index(min(goodRows)) - 1][activeKeys.index('x')])
              else:
                xlabel = str(selectedLabels[selind.index(min(goodRows)) - 1])
            else:
              xlabel = ''
            if(len(xlabel)):
              for target in ['plot', 'resid']:
                self.parent.parent.plotArea.setAxisLabel(xlabel, axis='x', redraw=False, target=target)
                if(target=='plot'):
                  self.parent.parent.graphicsarea.configXName.setText(xlabel)

          if('y' in activeKeys):
            if((min(goodRows) - 1) in selind):
              ylabel = str(selectedData[selind.index(min(goodRows)) - 1][activeKeys.index('y')])
              if(len(ylabel)):
                for target in ['plot', 'resid']:
                  self.parent.parent.plotArea.setAxisLabel(ylabel, axis='y', redraw=False, target=target)
                  if(target=='plot'):
                    self.parent.parent.graphicsarea.configYName.setText(ylabel)

        # color table temporarily
        useKeys = ['x', 'xerr', 'y', 'yerr', 'labels', 'labels2']
        if(self.parent.errorModel or (not self.parent.errorSwitch)):
          useKeys.remove('yerr')
        if(self.parent.errorXModel or (not self.parent.errorXSwitch)):
          useKeys.remove('xerr')
        selCols = [self.roles[key] for key in useKeys if (self.roles[key] > -1)]
        self.tableModel.setGoodBad(goodRows=goodRows, badRows=badRows, selCols=selCols)
        
        # convert nested list to numpy array
        retv = prunedData
        roles = activeKeys
        if(self.roles['labels'] > -1):
          roles.append('labels')
        if(self.roles['labels2'] > -1):
          roles.append('labels2')
    
        # sort data by ascending x values
        #if(len(retv)):
          # will sort according to first entry w/in nested list which should be 'x'
        #  retv = sorted(retv)
        # turned off this feature as it causes problems for cyclic data (e.g., voltammetry)
    return retv, roles, [xlabel, ylabel]

  def wheelEvent(self, event):
    # adjusts zoom level when Ctrl is pressed also
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier):
      if(event.angleDelta().y() > 0):
        self.zoomIn()
      else:
        self.zoomOut()
      # issue update
      self.update()
      QtCore.QCoreApplication.processEvents()
    else:
      # normal event handling
      QtWidgets.QTableView.wheelEvent(self, event)

  def zoomIn(self):
    self.zoom(increment=1)
    
  def zoomOut(self):
    self.zoom(increment=-1)

  def zoom(self, increment=1):
    # general zoom function
    font = self.tableModel.getFont()
    # check whether we run zoom() the first time
    if(self.currZoom < 0):
      ###fontSize = font.pointSize()
      ###temp = np.abs(np.array(self.zoomLevels) - fontSize)
      # better use WIDGET_FONTSIZE to properly heed stylesheet (which somehow overrides other font properties)
      temp = np.abs(np.array(self.zoomLevels) - float(WIDGET_FONTSIZE))
      self.currZoom = temp.argmin()
      
    if(((self.currZoom < len(self.zoomLevels) - 1) and (increment > 0)) or ((self.currZoom > 0) and (increment < 0))):
      # alter font size in cells
      self.currZoom += increment
      self.currZoom = int(self.currZoom)
      font.setPointSize(self.zoomLevels[self.currZoom])
      
      # alter row height
      fm = QtGui.QFontMetrics(font)
      self.rowHeight = int(fm.height() + 2)

      # alter font in horizontal header
      # make the header fonts a tad larger than the cell font
      self.verticalHeader().setStyleSheet("QHeaderView { font-size: " + str(self.zoomLevels[self.currZoom] + 2) + "pt; }")
      self.horizontalHeader().setStyleSheet("QHeaderView { font-size: " + str(self.zoomLevels[self.currZoom] + 2) + "pt; }")
      
      # resize column width proportionally
      if(len(self.sheetData)):
        dimx = min(15, len(self.sheetData[0]))
        hheader = self.horizontalHeader()
        scale = self.zoomLevels[self.currZoom] / self.zoomLevels[self.currZoom - increment]
        for entry in range(dimx):
          nuSize = int(hheader.sectionSize(entry) * scale)
          hheader.resizeSection(entry, nuSize)

      # finally readjust size
      self.readjustSize()

  def keyPressEvent(self, event):
    if event.matches(QtGui.QKeySequence.StandardKey.Copy):
      # prepare output
      if(len(self.selectedRows)):
        selind = self.selectedRows
      else:
        selind = self.selectionModel().selectedRows()
        selind = sorted([i.row() for i in selind])
        self.selectedRows = selind
      # get data
      selectedData = self.tableModel.getDataRows(selind)
      output = []
      for row in selectedData:
        row = [str(i) for i in row]
        output.append('\t'.join(row))
      output = '\n'.join(output)
      clipboard = QtWidgets.QApplication.clipboard()
      clipboard.setText(output)
    elif event.matches(QtGui.QKeySequence.StandardKey.Paste):
      clipboard = QtWidgets.QApplication.clipboard()
      clipMime = clipboard.mimeData()
      # check wether clip object contains text
      if(clipMime.hasText()):
        clipContent = clipboard.text()
        self.pasteText(pastedText=clipContent)
    elif event.matches(QtGui.QKeySequence.StandardKey.SelectAll):
      self.selectAll()
    elif(event.key() == QtCore.Qt.Key.Key_Down):
      if(self.currentRow < self.tableModel.rowCount() - 1):
        if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          self.clearSelection()
        self.currentRow += 1
        self.selectTo(self.currentRow, self.currentRow)
        nuIndex = self.model().index(self.currentRow, self.currentCol)
        self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() == QtCore.Qt.Key.Key_Up):
      if(self.currentRow > 0):
        if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          self.clearSelection()
        self.currentRow -= 1
        self.selectTo(self.currentRow, self.currentRow)
        nuIndex = self.model().index(self.currentRow, self.currentCol)
        self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() == QtCore.Qt.Key.Key_PageDown):
      if(self.currentRow < self.tableModel.rowCount() - 1):
        if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          self.clearSelection()
        origRow = self.currentRow
        self.currentRow += self.pageStep
        self.currentRow = min(self.currentRow, self.tableModel.rowCount() - 1)
        if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          origRow = self.currentRow
        self.selectTo(origRow, self.currentRow)
        nuIndex = self.model().index(self.currentRow, self.currentCol)
        self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() == QtCore.Qt.Key.Key_PageUp):
      if(self.currentRow > 0):
        if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          self.clearSelection()
        origRow = self.currentRow
        self.currentRow -= self.pageStep
        self.currentRow = max(self.currentRow, 0)
        if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          origRow = self.currentRow
        self.selectTo(origRow, self.currentRow)
        nuIndex = self.model().index(self.currentRow, self.currentCol)
        self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() in [QtCore.Qt.Key.Key_Left, QtCore.Qt.Key.Key_Right]):
      # ignore event such that we can capture the left/right keys
      event.ignore()
      flag = False
      if(event.key() == QtCore.Qt.Key.Key_Left):
        if(self.currentCol > 0):
          self.currentCol -= 1
          flag = True
      elif(self.currentCol < self.tableModel.columnCount() - 1):
        self.currentCol += 1
        flag = True
      if(flag):
        nuIndex = self.model().index(self.currentRow, self.currentCol)
        self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() in [QtCore.Qt.Key.Key_Home, QtCore.Qt.Key.Key_End]):
      if(event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier):
        flag = False
        if(event.key() == QtCore.Qt.Key.Key_Home):
          if(self.currentRow > 0):
            origRow = self.currentRow
            self.currentRow = 0
            flag = True
        elif(self.currentRow < self.tableModel.rowCount() - 1):
          origRow = self.currentRow
          self.currentRow = self.tableModel.rowCount() - 1
          flag = True
        if(flag):
          if(not (event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier)):
            self.clearSelection()
            origRow = self.currentRow
          self.selectTo(origRow, self.currentRow)
          nuIndex = self.model().index(self.currentRow, self.currentCol)
          self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
      else:
        flag = False
        if(event.key() == QtCore.Qt.Key.Key_Home):
          if(self.currentCol > 0):
            self.currentCol = 0
            flag = True
        elif(self.currentCol < self.tableModel.columnCount() - 1):
          self.currentCol = self.tableModel.columnCount() - 1
          flag = True
        if(flag):
          nuIndex = self.model().index(self.currentRow, self.currentCol)
          self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() in [QtCore.Qt.Key.Key_Tab, QtCore.Qt.Key.Key_Backtab]):
      # advance cell on tab
      if(event.key() == QtCore.Qt.Key.Key_Backtab):
        self.currentCol -= 1
        if(self.currentCol < 0):
          if(self.currentRow > 0):
            self.currentRow -= 1
            self.currentCol = self.tableModel.columnCount() - 1
          else:
            self.currentCol = 0
      else:
        self.currentCol += 1
        if(self.currentCol >= self.tableModel.columnCount()):
          if(self.currentRow < self.tableModel.rowCount() - 1):
            self.currentRow += 1
            self.currentCol = 0
          else:
            self.currentCol = self.tableModel.columnCount() - 1
      self.clearSelection()
      self.selectTo(self.currentRow, self.currentRow)
      nuIndex = self.model().index(self.currentRow, self.currentCol)
      self.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)
    elif(event.key() in [QtCore.Qt.Key.Key_Backspace, QtCore.Qt.Key.Key_Delete]):
      # check how many rows are selected -- if more than one, clear all contents
      selectedRows = self.selectionModel().selectedRows()
      if(len(selectedRows) > 1):
        # generate empty data block
        emptyRow = [[''] * self.tableModel.columnCount()]
        # cycle over rows and paste stuff
        for i in selectedRows:
          self.tableModel.pasteDataBlock(data=emptyRow, offsetRow=i.row(), offsetColumn=0)
        # calculate statistics on all columns
        for i in range(self.tableModel.columnCount()):
          self.updateColumnStatistics(column=i)
        # update table
        self.tableModel.layoutChanged.emit()
      else:
        # clear current cell
        self.tableModel.setData('', self.currentRow, self.currentCol)
        # calculate statistics on columns
        self.updateColumnStatistics(column=self.currentCol)
        # refresh table view
        cellIndex = self.tableModel.index(self.currentRow, self.currentCol)
        self.tableModel.dataChanged.emit(cellIndex, cellIndex, [QtCore.Qt.ItemDataRole.DisplayRole])
    elif (event.matches(QtGui.QKeySequence.StandardKey.ZoomIn)):
      self.zoomIn()
      event.accept()
    elif (event.matches(QtGui.QKeySequence.StandardKey.ZoomOut)):
      self.zoomOut()
      event.accept()
    elif (event.matches(QtGui.QKeySequence.StandardKey.Save) or event.matches(QtGui.QKeySequence.StandardKey.Open) or event.matches(QtGui.QKeySequence.StandardKey.HelpContents)\
          or event.matches(QtGui.QKeySequence.StandardKey.Print) or event.matches(QtGui.QKeySequence.StandardKey.Quit) or event.matches(QtGui.QKeySequence.StandardKey.Italic)\
          or event.matches(QtGui.QKeySequence.StandardKey.Find) or event.matches(QtGui.QKeySequence.StandardKey.New) or event.matches(QtGui.QKeySequence.StandardKey.Refresh) or event.matches(QtGui.QKeySequence.StandardKey.FindNext)):
      # pass event to main ui
      event.ignore()
    elif(not (event.key() in [QtCore.Qt.Key.Key_Escape])):
      openDialog = False
      if(event.key() in [QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Return]):
        openDialog = True
        initialEdit = ''
      elif(len(event.text())):
        openDialog = True
        initialEdit = event.text()
      if(openDialog):
        # ensure that cell is visible
        indexAt = self.model().index(self.currentRow, self.currentCol)
        self.scrollTo(indexAt)
        # open edit QMenu at cell position
        rowViewport, colViewport = self.rowViewportPosition(self.currentRow), self.columnViewportPosition(self.currentCol) + self.verticalHeader().width()
        menuPos = self.mapToGlobal(QtCore.QPoint(int(colViewport), int(rowViewport)))
        if((indexAt.row() != -1) and (indexAt.column() != -1)):
          if(self.currZoom >= 0):
            fontSize = self.zoomLevels[self.currZoom]
          else:
            fontSize = float(WIDGET_FONTSIZE)
          self.menu = EditDataMenu(parent=self, tableModel=self.tableModel, indexAt=indexAt, initialEdit=initialEdit, fontSize=fontSize)
          # apply styles to popup window
          if(QSTYLE != None):
            self.menu.setStyle(QSTYLE)
          if(QSTYLESHEET != None):
            self.menu.setStyleSheet(QSTYLESHEET)
          self.menu.popup(menuPos)    

  def selectTo(self, startRow=0, endRow=0):
    # selects target rows
    lowRow, hiRow = min(startRow, endRow), max(startRow, endRow)
    columnCount = self.tableModel.columnCount()
    
    topLeft = self.model().index(lowRow, 0)
    bottomRight = self.model().index(hiRow, columnCount - 1)
    itemSelection = QtCore.QItemSelection(topLeft, bottomRight)
    self.selectionModel().select(itemSelection, QtCore.QItemSelectionModel.SelectionFlag.Select)

  def pasteText(self, pastedText=''):
    # display busy pointer as this can take a long time for big data chunks
    QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
    # store target cell
    offsetRow, offsetColumn = self.currentRow, self.currentCol
    # determine size of text to be pasted
    clipRows = pastedText.split('\n')
    clipCols = [i.split('\t') for i in clipRows]
    clipColCount = [len(i) for i in clipCols]
    # determine new data sheet dimensions
    nuRowCount, nuColCount = len(clipRows), max(clipColCount)
    # check for trailing all-empty columns which we won't copy
    index = nuColCount - 1
    trailCol = nuColCount
    while(index >= 0):
      for entry in clipCols:
        if(0 < index < len(entry)):
          if(len(entry[index])):
            trailCol = index + 1
            index = -1
      index -= 1
    # make clipped text square and truncate after trailCol
    clipCols = [i + ([''] * nuColCount) for i in clipCols]
    clipCols = [i[:trailCol] for i in clipCols]
    # convert to number where possible
    clipCols = [[float(j) if self.isNumber(j) else j for j in i] for i in clipCols]
    # prepare pasting of text -- resize if needed
    dimy, dimx = self.tableModel.rowCount(), self.tableModel.columnCount()
    if(((offsetColumn + trailCol) > dimx) or ((offsetRow + nuRowCount) > dimy)):
      # store current data
      currData = self.tableModel.getAllData()
      # blank data
      dimx, dimy = max(dimx, offsetColumn + trailCol), max(dimy, offsetRow + nuRowCount)
      blankData = [[''] * dimx for i in range(dimy)]
      self.tableModel = DataTableModel(blankData, self)
      self.setModel(self.tableModel)
      # restore original data
      self.tableModel.pasteDataBlock(data=currData, offsetRow=0, offsetColumn=0)
    
    # paste new data
    self.tableModel.pasteDataBlock(data=clipCols, offsetRow=offsetRow, offsetColumn=offsetColumn)
    self.configTable(offsetColumn + trailCol, offsetRow + nuRowCount, retainRoles=True, retainSelection=True)
    self.tableModel.layoutChanged.emit()
    
    # update self.sheetData as well
    self.sheetData = self.tableModel.getAllData()

    # restore cursor
    QtWidgets.QApplication.restoreOverrideCursor()
    
  def mouseDoubleClickEvent(self, event):
    # allow editing of cell on double click
    # perform original event
    QtWidgets.QTableView.mouseDoubleClickEvent(self, event)
    
    # determine indices of clicked cell and scroll to ensure visibility
    indexAt = self.indexAt(event.pos())
    self.scrollTo(indexAt)
    row, col = indexAt.row(), indexAt.column()
    rowViewport, colViewport = self.rowViewportPosition(row), self.columnViewportPosition(col)
    
    # open edit QMenu at cell position
    menuPos = self.mapToGlobal(QtCore.QPoint(int(colViewport + self.verticalHeader().width()), int(rowViewport)))

    if((indexAt.row() != -1) and (indexAt.column() != -1)):
      if(self.currZoom >= 0):
        fontSize = self.zoomLevels[self.currZoom]
      else:
        fontSize = float(WIDGET_FONTSIZE)
      self.menu = EditDataMenu(parent=self, tableModel=self.tableModel, indexAt=indexAt, fontSize=fontSize)
      # apply styles to popup window
      if(QSTYLE != None):
        self.menu.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.menu.setStyleSheet(QSTYLESHEET)
      self.menu.popup(menuPos)    

  def currentCellChanged(self, current):
    # keeps tabs on current cell
    self.currentRow, self.currentCol = current.row(), current.column()

  def isNumber(self, s):
    # checks whether string is a number
    try:
      float(s)
      return True
    except ValueError:
      pass
   
    try:
      import unicodedata
      unicodedata.numeric(s)
      return True
    except (TypeError, ValueError):
      pass
    
    return False

# subclass edit to better capture key presses
class EditDataEdit(QtWidgets.QLineEdit):
  def __init__(self, parent=None):
    super(EditDataEdit, self).__init__(parent)
    self.parent = parent
  
  def keyPressEvent(self, event):
    # ignore alt keys as they would close the QMenu
    if(event.key() in [QtCore.Qt.Key.Key_Alt, QtCore.Qt.Key.Key_AltGr]):
      return

    # capture enter and arrow keys
    if(event.key() in [QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter]):
      if(event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier):
        self.parent.advanceCell(-1, 0)
      else:
        self.parent.advanceCell(1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Up):
      self.parent.advanceCell(-1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Down):
      self.parent.advanceCell(1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Left):
      if((event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier) or (self.cursorPosition() == 0)):
        self.parent.advanceCell(0, -1)
        return
    elif(event.key() == QtCore.Qt.Key.Key_Right):
      if((event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier) or (self.cursorPosition() == len(self.text()))):
        self.parent.advanceCell(0, 1)
        return
      
    # normal event processing
    QtWidgets.QLineEdit.keyPressEvent(self, event)

class EditDataMenu(KuhMenu):
  def __init__(self, parent=None, tableModel=None, indexAt=None, initialEdit='', fontSize=10):
    super(EditDataMenu, self).__init__()
    self.finalUpdate = False
    if(None in [parent, tableModel, indexAt]):
      self.close()
    # check for click outside cells
    elif((indexAt.row() == -1) or (indexAt.column() == -1)):
      self.close()
    else:
      self.parent = parent
      self.tableModel = tableModel
      self.maxRow, self.maxCol = self.tableModel.rowCount(), self.tableModel.columnCount()
      self.indexAt = indexAt
      self.row, self.col = self.indexAt.row(), self.indexAt.column()
      self.minWidth = int(100 * SCALEFONT)
      self.finalUpdate = True
      self.fontSize = fontSize
        
      # set up GUI
      self.buildRessource()
      
      # set QMenu position
      self.adjustWindowPosition(initialEdit=initialEdit)

  def adjustWindowPosition(self, initialEdit=''):
    # update label
    labelText = 'Edit cell ' + str(self.col + 1) + '/' + str(self.row + 1)
    useFont = self.editDataLabel.font()
    useFont.setBold(True)
    self.editDataLabel.setFont(useFont)
    self.editDataLabel.setText(labelText)
    self.editDataLabel.setStyleSheet("QLabel { font-size: " + str(self.fontSize) + "pt; }")

    # update QlineEdit
    if(len(initialEdit)):
      self.initValue = ''
      self.editData.setText(initialEdit)
    else:
      self.initValue = self.tableModel.dataByIndices(self.row, self.col)
      self.editData.setText(str(self.initValue))
    self.editData.setStyleSheet("QLineEdit { font-size: " + str(self.fontSize) + "pt; }")

    self.editData.selectAll()
    self.editData.setFocus()

    # ensure that cell is visible
    cellIndex = self.tableModel.index(self.row, self.col)
    self.parent.scrollTo(cellIndex)
    
    # adjust width of edit window -- use min/max size as resize not properly heeded
    cellWidth = int(max(self.minWidth, self.parent.columnWidth(self.col)))
    ###self.editData.setMaximumSize(QtCore.QSize(cellWidth, int(BASE_SIZE)))
    ###self.editData.setMinimumSize(QtCore.QSize(cellWidth, int(BASE_SIZE)))
    self.setMaximumWidth(cellWidth)
    self.setMinimumWidth(cellWidth)

    # adjusts window position to currently edited cell
    rowViewport = self.parent.rowViewportPosition(self.row)
    colViewport = self.parent.columnViewportPosition(self.col) + self.parent.verticalHeader().width()
    menuPos = self.parent.mapToGlobal(QtCore.QPoint(int(colViewport), int(rowViewport)))
    self.move(menuPos)
    
    if(len(initialEdit)):
      self.editData.deselect()
      self.editData.setCursorPosition(1)

  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    
    # QlineEdit for data modification
    self.editDataLabel = QtWidgets.QLabel()
    self.vLayout.addWidget(self.editDataLabel)
    
    self.editData = EditDataEdit(self)
    self.vLayout.addWidget(self.editData)
    self.editData.setFocus()
    
  def updateData(self):
    # updates data table if required
    currValue = self.editData.text()
    if(currValue != self.initValue):
      try:
        currValue = float(currValue)
      except:
        pass
      self.parent.tableModel.setData(currValue, self.row, self.col)

      # calculate statistics on columns
      self.parent.updateColumnStatistics(column=self.col)
  
      # refresh table view
      cellIndex = self.tableModel.index(self.row, self.col)
      self.tableModel.dataChanged.emit(cellIndex, cellIndex, [QtCore.Qt.ItemDataRole.DisplayRole])
      
  def keyPressEvent(self, event):
    # process tab keys and escape
    if(event.key() == QtCore.Qt.Key.Key_Backtab):
      self.advanceCell(0, -1)
    elif(event.key() == QtCore.Qt.Key.Key_Tab):
      self.advanceCell(0, 1)
    elif(event.key() == QtCore.Qt.Key.Key_Escape):
      self.finalUpdate = False
      self.close()
      
  def advanceCell(self, deltaRow=0, deltaCol=0):
    # update previous data
    self.updateData()
    
    # move edit window to new position
    # adjust cell indices
    self.deltaRow, self.deltaCol = deltaRow, deltaCol
    
    # apply column shift
    self.col += self.deltaCol
    if(self.col >= self.maxCol):
      if(self.row < self.maxRow - 1):
        self.col = 0; self.row += 1
      else:
        self.col = self.maxCol - 1
    elif(self.col < 0):
      if(self.row > 0):
        self.col = self.maxCol - 1; self.row -= 1
      else:
        self.col = 0

    # apply row shift
    self.row += deltaRow
    self.row = max(self.row, 0)
    self.row = min(self.row, self.maxRow - 1)

    # set cursor in data table to currently edited cell
    self.parent.currentCol, self.parent.currentRow = self.col, self.row
    self.parent.clearSelection()
    self.parent.selectTo(self.row, self.row)
    nuIndex = self.parent.model().index(self.row, self.col)
    self.parent.selectionModel().setCurrentIndex(nuIndex, QtCore.QItemSelectionModel.SelectionFlag.Select)

    # move window
    self.adjustWindowPosition()
    
  def closeEvent(self, event):
    # perform final update
    if(self.finalUpdate):
      self.updateData()
    # perform original close event
    QtWidgets.QMenu.closeEvent(self, event)

# subclass edit to better capture key presses
class EditResultsEdit(QtWidgets.QLineEdit):
  def __init__(self, parent=None):
    super(EditResultsEdit, self).__init__(parent)
    self.parent = parent
  
  def keyPressEvent(self, event):
    # ignore alt keys as they would close the QMenu
    if(event.key() in [QtCore.Qt.Key.Key_Alt, QtCore.Qt.Key.Key_AltGr]):
      return

    # capture enter and arrow keys
    if(event.key() in [QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter]):
      if(event.modifiers() & QtCore.Qt.KeyboardModifier.ShiftModifier):
        self.parent.advanceCell(-1, 0)
      else:
        self.parent.advanceCell(1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Up):
      self.parent.advanceCell(-1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Down):
      self.parent.advanceCell(1, 0)
#    elif(event.key() == QtCore.Qt.Key.Key_Left):
#      if((event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier) or (self.cursorPosition() == 0)):
#        self.parent.advanceCell(0, -1)
#        return
#    elif(event.key() == QtCore.Qt.Key.Key_Right):
#      if((event.modifiers() & QtCore.Qt.KeyboardModifier.ControlModifier) or (self.cursorPosition() == len(self.text()))):
#        self.parent.advanceCell(0, 1)
#        return
      
    # normal event processing
    QtWidgets.QLineEdit.keyPressEvent(self, event)

class EditResultsMenu(KuhMenu):
  def __init__(self, parent=None, tableModel=None, indexAt=None, initialEdit='', fontSize=10, validColumns=[]):
    super(EditResultsMenu, self).__init__()
    self.finalUpdate = False
    if(None in [parent, tableModel, indexAt]):
      self.close()
    # check for click outside cells
    elif((indexAt.row() == -1) or (indexAt.column() == -1)):
      self.close()
    else:
      self.parent = parent
      self.tableModel = tableModel
      self.maxRow, self.maxCol = self.tableModel.rowCount(), self.tableModel.columnCount()
      self.indexAt = indexAt
      self.row, self.col = self.indexAt.row(), self.indexAt.column()
      self.minWidth = int(100 * SCALEFONT)
      self.finalUpdate = True
      self.fontSize = fontSize
        
      # set up GUI
      self.buildRessource()
      
      # set QMenu position
      self.adjustWindowPosition(initialEdit=initialEdit)

  def adjustWindowPosition(self, initialEdit=''):
    # update label
    labelText = 'Edit label ' + str(self.row + 1)
    useFont = self.editDataLabel.font()
    useFont.setBold(True)
    self.editDataLabel.setFont(useFont)
    self.editDataLabel.setText(labelText)
    self.editDataLabel.setStyleSheet("QLabel { font-size: " + str(self.fontSize) + "pt; }")

    # update QlineEdit
    if(len(initialEdit)):
      self.initValue = ''
      self.editData.setText(initialEdit)
    else:
      self.initValue = self.tableModel.dataByIndices(self.row, self.col)
      self.editData.setText(str(self.initValue))
    self.editData.setStyleSheet("QLineEdit { font-size: " + str(self.fontSize) + "pt; }")

    self.editData.selectAll()
    self.editData.setFocus()

    # ensure that cell is visible
    cellIndex = self.tableModel.index(self.row, self.col)
    self.parent.scrollTo(cellIndex)
    
    # adjust width of edit window -- use min/max size as resize not properly heeded
    cellWidth = int(max(self.minWidth, self.parent.columnWidth(self.col)))
    self.setMaximumWidth(cellWidth)
    self.setMinimumWidth(cellWidth)

    # adjusts window position to currently edited cell
    rowViewport = self.parent.rowViewportPosition(self.row)
    colViewport = self.parent.columnViewportPosition(self.col) + self.parent.verticalHeader().width()
    menuPos = self.parent.mapToGlobal(QtCore.QPoint(int(colViewport), int(rowViewport)))
    self.move(menuPos)
    
    if(len(initialEdit)):
      self.editData.deselect()
      self.editData.setCursorPosition(1)

  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    
    # QlineEdit for data modification
    self.editDataLabel = QtWidgets.QLabel()
    self.vLayout.addWidget(self.editDataLabel)
    
    self.editData = EditResultsEdit(self)
    self.vLayout.addWidget(self.editData)
    self.editData.setFocus()
    
  def updateData(self):
    # updates data table if required
    currValue = self.editData.text()
    if(currValue != self.initValue):
      try:
        currValue = float(currValue)
      except:
        pass
      self.parent.tableModel.setData(currValue, self.row, self.col)

      # calculate statistics on columns
      self.parent.updateColumnStatistics(column=self.col)
  
      # refresh table view
      cellIndex = self.tableModel.index(self.row, self.col)
      self.tableModel.dataChanged.emit(cellIndex, cellIndex, [QtCore.Qt.ItemDataRole.DisplayRole])
      
  def keyPressEvent(self, event):
    # process tab keys and escape
    if(event.key() == QtCore.Qt.Key.Key_Backtab):
      self.advanceCell(-1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Tab):
      self.advanceCell(1, 0)
    elif(event.key() == QtCore.Qt.Key.Key_Escape):
      self.finalUpdate = False
      self.close()
      
  def advanceCell(self, deltaRow=0, deltaCol=0):
    # update previous data
    self.updateData()
    
    # move edit window to new position
    # adjust cell indices
    self.deltaRow, self.deltaCol = deltaRow, deltaCol
    
    # apply column shift
    self.col += self.deltaCol
    if(self.col >= self.maxCol):
      if(self.row < self.maxRow - 1):
        self.col = 0; self.row += 1
      else:
        self.col = self.maxCol - 1
    elif(self.col < 0):
      if(self.row > 0):
        self.col = self.maxCol - 1; self.row -= 1
      else:
        self.col = 0

    # apply row shift
    self.row += deltaRow
    self.row = max(self.row, 0)
    self.row = min(self.row, self.maxRow - 1)

    # set cursor in data table to currently edited cell
    self.parent.currentCol, self.parent.currentRow = self.col, self.row
    self.parent.clearSelection()
    self.parent.selectRow(self.row)

    # move window
    self.adjustWindowPosition()
    
  def closeEvent(self, event):
    # perform final update
    if(self.finalUpdate):
      self.updateData()
    # perform original close event
    QtWidgets.QMenu.closeEvent(self, event)
    # trigger update of labels in parent object
    self.parent.updateLabels()

# the results table widget
class ResultsTable(QtWidgets.QTableView):
  def __init__(self, parent=None):
    super(ResultsTable, self).__init__(parent)
    self.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
    self.setItemDelegate(FloatFormatDelegate())
    self.setEditTriggers(QtWidgets.QAbstractItemView.EditTrigger.NoEditTriggers)
    self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
    btn = self.findChild(QtWidgets.QAbstractButton)
    self.storedCornerTip = 'Click to select entire table'
    btn.setToolTip(self.storedCornerTip)

    self.parent = parent
    self.tableModel = None
    self.rowHeight = 10

    # settings for zoom of data table
    self.zoomLevels = [3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 24, 28, 32, 36, 40, 48, 56, 64, 72, 80, 90, 100]
    self.currZoom = -1

  def wheelEvent(self, event):
    # adjusts zoom level when Ctrl is pressed also
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if((self.tableModel != None) and self.tableModel.rowCount() and (modifiers & QtCore.Qt.KeyboardModifier.ControlModifier)):
      if(event.angleDelta().y() > 0):
        self.zoomIn()
      else:
        self.zoomOut()
      # issue update
      self.update()
      QtCore.QCoreApplication.processEvents()
    else:
      # normal event handling
      QtWidgets.QTableView.wheelEvent(self, event)

  def keyPressEvent(self, event):
    # captures zoom keys
    if(event.matches(QtGui.QKeySequence.StandardKey.ZoomIn)):
      self.zoomIn()
      event.accept()
    elif(event.matches(QtGui.QKeySequence.StandardKey.ZoomOut)):
      self.zoomOut()
      event.accept()
    elif(event.matches(QtGui.QKeySequence.StandardKey.SelectAll)):
      self.selectAll()
    else:
      event.ignore()

  def mouseDoubleClickEvent(self, event):
    # allow editing of cell on double click
    # perform original event
    QtWidgets.QTableView.mouseDoubleClickEvent(self, event)
    
    # determine indices of clicked cell and scroll to ensure visibility
    indexAt = self.indexAt(event.pos())
    self.scrollTo(indexAt)
    row, col = indexAt.row(), indexAt.column()
    rowViewport, colViewport = self.rowViewportPosition(row), self.columnViewportPosition(col)
    
    # open edit QMenu at cell position
    menuPos = self.mapToGlobal(QtCore.QPoint(int(colViewport + self.verticalHeader().width()), int(rowViewport)))

    if((indexAt.row() != -1) and (indexAt.column() != -1)):
      # determine if we have a label column and if we clicked on it
      validColumn = []
      for item in ['labels', 'labels2']:
        if(item in self.tableModel.getHeaders()):
          validColumn.append(self.tableModel.getHeaders().index(item))
      if(indexAt.column() in validColumn):
        if(self.currZoom >= 0):
          fontSize = self.zoomLevels[self.currZoom]
        else:
          fontSize = float(WIDGET_FONTSIZE)
        self.menu = EditResultsMenu(parent=self, tableModel=self.tableModel, indexAt=indexAt, fontSize=fontSize, validColumns=[validColumn])
        # apply styles to popup window
        if(QSTYLE != None):
          self.menu.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.menu.setStyleSheet(QSTYLESHEET)
        self.menu.popup(menuPos)
          
  def updateLabels(self):
    # callback from EditResultsMenu => update the labeli
    for item in ['labels', 'labels2']:
      if(item in self.tableModel.getHeaders()):
        validColumn = self.tableModel.getHeaders().index(item)
        labels = self.tableModel.getDataColumn(validColumn)
      
        # determine index value of data set on display
        currIndex = self.parent.dataSetSpinBox.value()
        # update corresponding data set
        self.parent.parent.data[currIndex - 1].addLabeli(labels=labels, labels2=item == 'labels2')

  def zoomIn(self):
    self.zoom(increment=1)
    
  def zoomOut(self):
    self.zoom(increment=-1)

  def zoom(self, increment=1):
    # general zoom function
    if(self.tableModel != None):
      font = self.tableModel.getFont()
      # check whether we run zoom() the first time
      if(self.currZoom < 0):
        ###fontSize = font.pointSize()
        ###temp = np.abs(np.array(self.zoomLevels) - fontSize)
        # better use WIDGET_FONTSIZE to properly heed stylesheet (which somehow overrides other font properties)
        temp = np.abs(np.array(self.zoomLevels) - float(WIDGET_FONTSIZE))
        self.currZoom = temp.argmin()
        
      if(((self.currZoom < len(self.zoomLevels) - 1) and (increment > 0)) or ((self.currZoom > 0) and (increment < 0))):
        # alter font size in cells
        self.currZoom += increment
        self.currZoom = int(self.currZoom)
        font.setPointSize(self.zoomLevels[self.currZoom])
        
        # alter row height
        fm = QtGui.QFontMetrics(font)
        self.rowHeight = int(fm.height() + 2)
  
        # alter font in horizontal header
        # make the header fonts a tad larger than the cell font
        self.verticalHeader().setStyleSheet("QHeaderView { font-size: " + str(self.zoomLevels[self.currZoom] + 2) + "pt; }")
        self.horizontalHeader().setStyleSheet("QHeaderView { font-size: " + str(self.zoomLevels[self.currZoom] + 2) + "pt; }")
        
        # resize column width proportionally
        if(self.tableModel.rowCount()):
          dimx = min(15, self.tableModel.columnCount())
          hheader = self.horizontalHeader()
          scale = self.zoomLevels[self.currZoom] / self.zoomLevels[self.currZoom - increment]
          for entry in range(dimx):
            nuSize = int(hheader.sectionSize(entry) * scale)
            hheader.resizeSection(entry, nuSize)
  
        # finally readjust size
        # fudge function to prevent PyQt5 from uncontrollably resizing row height
        vheader = self.verticalHeader()
        vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
        vheader.setDefaultSectionSize(self.rowHeight)

  def updateColumnStatistics(self, column=-1):
    # helper function to calculate column statistics
    self.tableModel.columnAverages = ['n/a'] * self.tableModel.columnCount()
    if(self.tableModel.rowCount()):
      if(column + 1):
        userange = range(column, column + 1)
      else:
        userange = range(self.tableModel.columnCount())
      for index in userange:
        temp = self.tableModel.getDataColumn(index)
        temp = [i for i in temp if type(i) in [int, float]]
        if(len(temp)):
          self.tableModel.setColumnStats(index, number=len(temp), csum=np.sum(temp), mean=np.mean(temp), minval=np.min(temp), maxval=np.max(temp))
        else:
          self.tableModel.setColumnStats(index, number=0, csum=0, mean='n/a', minval='n/a', maxval='n/a')

class ResultsArea(QWidgetMac):
  def __init__(self, parent = None):
    super(ResultsArea, self).__init__()
    self.parent = parent
    self.rolestr = {'x':'x', 'y':'y', 'xerr':u'\N{GREEK CAPITAL LETTER DELTA}x', \
      'yerr':u'\N{GREEK CAPITAL LETTER DELTA}y', 'fit':'fit', 'resid':'resid'}
    self.descriptors = []
    self.buildRessource()
    self.minColWidth = 35
    
    # initialize filename
    self.currExportFile = None
    
  def buildRessource(self):
    # set up results table
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)

    # allow change of data set on display
    self.dataSetBox = QWidgetMac()
    self.vLayout.addWidget(self.dataSetBox)
    self.dataSetLayout = QtWidgets.QHBoxLayout(self.dataSetBox)
    self.dataSetLayout.setContentsMargins(0, 0, 0, 0)
    self.dataSetLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.dataSetLabel = QtWidgets.QLabel()
    useFont = self.dataSetLabel.font()
    useFont.setBold(True)
    self.dataSetLabel.setFont(useFont)
    self.dataSetLabel.setText('Data Set')
    self.dataSetLabel.setMaximumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.dataSetLabel.setMinimumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.dataSetLayout.addWidget(self.dataSetLabel)
    
    self.dataSetSpinBox = QSpinClick()
    self.dataSetSpinBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
    self.dataSetSpinBox.setToolTip('Select data set to display in table')
    self.dataSetSpinBox.setMinimum(1)
    self.dataSetSpinBox.setMaximum(1)
    self.dataSetSpinBox.setValue(1)
    self.dataSetSpinBox.setMinimumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.dataSetSpinBox.setMaximumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.dataSetSpinBox.editingFinished.connect(self.changeDataSet)
    self.dataSetSpinBox.setEnabled(False)
    self.dataSetLayout.addWidget(self.dataSetSpinBox)
    
    self.dataSetName = QtWidgets.QLabel()
    self.dataSetName.setText(self.parent.data[self.parent.activeData].name)
    self.dataSetLayout.addWidget(self.dataSetName)
    
    self.dataSetLayout.addStretch()
    self.addLabelsButton = QPushButtonMac()
    self.addLabelsButton.setText('Add labels')
    self.addLabelsButton.setToolTip('Adds label column to data set (if not existing already)')
    self.addLabelsButton.setMinimumSize(int(70 * SCALEFONT), int(BASE_SIZE))
    self.addLabelsButton.setMaximumSize(int(70 * SCALEFONT), int(BASE_SIZE))
    self.addLabelsButton.clicked.connect(self.addLabeli)
    self.addLabelsButton.setEnabled(False)
    self.dataSetLayout.addWidget(self.addLabelsButton)

    self.resultstable = ResultsTable(parent=self)
    self.resultstable.setObjectName('data')
    self.vLayout.addWidget(self.resultstable)

    self.rowHeight = int(BASE_SIZE - 5)
    ###self.rowHeight = int(self.resultstable.fontMetrics().height() + 2)
    vheader = self.resultstable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)

    self.buttonBox = QtWidgets.QWidget()
    self.vLayout.addWidget(self.buttonBox)
    self.buttonBoxLayout = QtWidgets.QHBoxLayout(self.buttonBox)
    self.buttonBoxLayout.setContentsMargins(0, 0, 0, 0)
    self.buttonBoxLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.exportButton = QPushButtonMac()
    self.exportButton.setText(' Export Results')
    self.exportButton.setToolTip('Compile report of current graphics and fit results')
    self.exportButton.setMaximumHeight(int(BASE_SIZE))
    self.exportButton.setMinimumSize(QtCore.QSize(100, int(BASE_SIZE)))
    self.exportButton.clicked.connect(self.exportWrapper)
    self.exportButton.setIcon(FOM_ICON_ARROW_RIGHT)
    self.buttonBoxLayout.addWidget(self.exportButton, stretch=1)

    self.selectionButton = QPushButtonMac()
    self.selectionButton.setText(' Update Selection')
    self.selectionButton.setToolTip('Update data selection')
    self.selectionButton.setMaximumHeight(int(BASE_SIZE))
    self.selectionButton.setMinimumSize(QtCore.QSize(100, int(BASE_SIZE)))
    self.selectionButton.clicked.connect(self.updateSelection)
    self.selectionButton.setIcon(FOM_ICON_BROWSER_RELOAD)
    self.buttonBoxLayout.addWidget(self.selectionButton, stretch=1)

    self.clearButton = QPushButtonMac()
    self.clearButton.setText(' Clear Selection')
    self.clearButton.setToolTip('Clear data selection')
    self.clearButton.setMaximumHeight(int(BASE_SIZE))
    self.clearButton.setMinimumSize(QtCore.QSize(100, int(BASE_SIZE)))
    self.clearButton.clicked.connect(self.clearSelection)
    self.clearButton.setIcon(FOM_ICON_DIALOG_CANCEL_BUTTON)
    self.buttonBoxLayout.addWidget(self.clearButton, stretch=1)
    self.clearButton.setEnabled(False)

    self.spawnButton = QPushButtonMac()
    self.spawnButton.setText(' Spawn Data')
    self.spawnButton.setToolTip('Generate new data set from data selection')
    self.spawnButton.setMaximumHeight(int(BASE_SIZE))
    self.spawnButton.setMinimumSize(QtCore.QSize(100, int(BASE_SIZE)))
    self.spawnButton.clicked.connect(self.spawnMe)
    self.spawnButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.buttonBoxLayout.addWidget(self.spawnButton, stretch=1)
    self.spawnButton.setEnabled(False)

  def addLabeli(self, labels2=False):
    # add labels to data set unless already existing
    # determine index value of data set on display
    currIndex = self.dataSetSpinBox.value()
    # update corresponding data set
    self.parent.data[currIndex - 1].addLabeli([], labels2=labels2)
    # and update table
    self.changeDataSet()
  
  def spawnMe(self):
    # generates a new data set from current data selection
    values, roles = self.parent.selectedData.getData_n_Fit()
    name = self.parent.data[self.parent.activeData].name
    self.parent.objectsarea.dataSetCreate()
    self.parent.data[-1].setData(data=values, roles=roles)
    self.parent.data[-1].setName('spawned from ' + name)
    # display new data set
    self.parent.data[-1].drawMe(redraw=False)
    # need to update various tables
    self.parent.objectsarea.refreshDataTable()
    self.parent.globalarea.updateDataSetTable()
    # update results table
    self.setDataSet(currIndex=len(self.parent.data) - 1, maxIndex=len(self.parent.data) - 1)
    # update legend if needed
    self.parent.objectsarea.updateLegend(redraw=True)

  def updateSelection(self):
    # uses table selection to select data points
    currIndex = self.dataSetSpinBox.value()
    if(self.parent.activeData != currIndex - 1):
      self.parent.statusbar.showMessage('Data set ' + str(currIndex) + ' is on display, but data set ' + str(self.parent.activeData + 1) + ' is active => no selection done!', self.parent.STATUS_TIME)
      return
    
    if(self.resultstable.tableModel != None):
      # for good measure try/except this
      try:
        selind = self.resultstable.selectionModel().selectedRows()
        self.parent.selectedData.tableSelectIndices(selind)
        return
      except:
        pass
      
    self.parent.statusbar.showMessage('Results table appears to be empty, cannot update data set!', self.parent.STATUS_TIME)

  def clearSelection(self):
    # clears selection of data points
    self.resultstable.clearSelection()
    
    # clear selected data points (this will also control the display of the clear buttons)
    self.parent.selectedData.clearMe()

  def selectTheseRows(self, selind=[]):
    # selects rows in resultstable
    self.resultstable.clearSelection()
    if(len(selind)):
      # ensure that currently selected data set is on display
      if(self.parent.activeData + 1 != self.dataSetSpinBox.value()):
        self.dataSetSpinBox.setValue(self.parent.activeData + 1)
      selectionModel = self.resultstable.selectionModel()
      for index in selind:
        cellItem = selectionModel.model().index(index, 0)
        selectionModel.select(cellItem, QtCore.QItemSelectionModel.SelectionFlag.Select | QtCore.QItemSelectionModel.SelectionFlag.Rows)

  def toggleToolTips(self, state=True):
    # dynamically sets and unsets tool tips
    btn = self.resultstable.findChild(QtWidgets.QAbstractButton)
    if(state):
      btn.setToolTip(self.resultstable.storedCornerTip)
    else:
      btn.setToolTip('')

  def exportWrapper(self, modeHTMLOnly=False):
    # writes results to output file
    global REMEMBERDIR
    if(modeHTMLOnly):
      filter_options = ['HTML Files (*.html)']
    else:
      filter_options = ['HTML Files (*.html)','Excel Files (*.xlsx)']
    filterstring = ';;'.join(filter_options)
    usedir = REMEMBERDIR
    if(self.currExportFile != None):
      usedir = self.currExportFile
    if(modeHTMLOnly):
      caption = 'Compile Report'
    else:
      caption = 'Export Results'
    filename, filter_ = QtWidgets.QFileDialog.getSaveFileName(self, filter=filterstring, directory=usedir, caption=caption)
    filename = str(filename)
    if(PATH_SEPARATOR in filename):
      REMEMBERDIR = filename.split(PATH_SEPARATOR)[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    elif('/' in filename):
      REMEMBERDIR = filename.split('/')[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    if(len(filename) > 0):
      mode = filter_options.index(filter_)
      if(modeHTMLOnly or (mode == 0)):
        self.writeHTML(filename=filename)
      elif(mode == 1):
        if(XLSXWRITER_PRESENT):
          self.writeXLS_old(filename=filename)
        elif(OPENPYXL_PRESENT):
          self.writeXLS(filename=filename)
        else:
          self.parent.statusbar.showMessage('Results cannot be written as either xlsxwriter or openpyxl are required!', self.parent.STATUS_TIME)

  def writeHTML(self, filename=None):
    # writes Results table to HTML file
    if(filename != None):
      # set SVG export to path to ensure correct display in browser
      prevSVGSetting = None
      if('svg.fonttype' in matplotlib.rcParams):
        prevSVGSetting = matplotlib.rcParams['svg.fonttype']
      matplotlib.rcParams.update({'svg.fonttype': 'path'})
      # save current figure temporarily under new filename
      if(self.parent.plotArea.figureFill):
        useFaceColor = self.parent.plotArea.figureColor
      else:
        useFaceColor = 'none'
      if(self.parent.plotArea.frameDraw):
        useFrameColor = self.parent.plotArea.frameColor
      else:
        useFrameColor = 'none'
      try:
        self.parent.plotArea.matplot.savefig(filename, format='svg', dpi=600, facecolor=useFaceColor, edgecolor=useFrameColor)
        flag = True
      except:
        flag = False
      # store contents of SVG file in memory
      if(flag):
        with open(filename, 'r', encoding='utf-8') as readhandle:
          svg_plot = readhandle.readlines()
      else:
        svg_plot = []
      
      # save current residuals temporarily under new filename
      try:
        self.parent.plotArea.residplot.savefig(filename, format='svg', dpi=600, facecolor=useFaceColor, edgecolor=useFrameColor)
        flag2 = True
      except:
        flag2 = False

      # store contents of SVG file in memory
      if(flag2):
        with open(filename, 'r', encoding='utf-8') as readhandle:
          svg_resid = readhandle.readlines()
      else:
        svg_resid = []

      # restore SVG export setting to previous value
      if(prevSVGSetting != None):
        matplotlib.rcParams.update({'svg.fonttype': prevSVGSetting})
        
      # generate actual HTML file
      with open(filename, 'w', encoding='utf-8') as writehandle:
        if(writehandle):
          # write header
          writehandle.write('<html xmlns="http://www.w3.org/1999/xhtml">\n<head>\n')
          writehandle.write('<title>Fit-o-mat Results</title>\n')
          writehandle.write('<meta charset="UTF-8">\n')
          writehandle.write('<meta author="Moeglich laboratory, University of Bayreuth">\n')
          writehandle.write('<meta description="Fit-o-mat Fit Results">\n')
          writehandle.write('</head>\n<body>\n')
          
          writehandle.write('<h2>Fit-o-mat Results</h2>\n')
          writehandle.write(asctime() + '\n')
          # check whether current fit results are available
          if(self.parent.lastFitType != 'none'):
            # write heading and div
            writehandle.write('<div style="padding: 5px;">\n')
            writehandle.write('<a href="javascript:;" onclick="togglitsch(\'results\')" class="toggle">\n')
            writehandle.write('<h3 id="results_ctrl">&#9744; Fit Results</h3>\n')
            writehandle.write('</a>\n')
            writehandle.write('<div id="results" style="display:none;">\n')
            writehandle.write('<button id="results_button" class="little">Copy Results</button>\n')
            writehandle.write('<div id="results_content">\n')
            # further check whether these are local or global results
            writehandle.write('<span class="mypre">\n')
            if(self.parent.lastFitType == 'local'):
              if('<br/>' in self.parent.fitarea.outstring.lower()):
                fitresults = self.parent.fitarea.outstring.split('<br/>')
              else:
                fitresults = self.parent.fitarea.outstring.splitlines()
              if(self.parent.fitarea.bootString != ''):
                if('<br/>' in self.parent.fitarea.bootString.lower()):
                  fitresults += self.parent.fitarea.bootString.split('<br/>')
                else:
                  fitresults += self.parent.fitarea.bootString.splitlines()
            else:
              if('<br/>' in self.parent.globalarea.globalOutstring.lower()):
                fitresults = self.parent.globalarea.globalOutstring.split('<br/>')
              else:
                fitresults = self.parent.globalarea.globalOutstring.splitlines()
              writehandle.write('<h4>Global fit</h4>')
            writehandle.write('\n'.join(fitresults[1:]) + '\n')
            writehandle.write('</span>\n')

            # export covariances if available
            if(self.parent.lastFitType == 'local'):
              covar, covarParameters = self.parent.fitarea.covar, self.parent.fitarea.covarParameters
            else:
              covar, covarParameters = self.parent.globalarea.globalCovar, self.parent.globalarea.globalCovarParameters
            if(len(covar)):
              writehandle.write('<u>Covariances</u>\n')
              writehandle.write('<table class="covar">\n<thead>\n<tr>\n')
              theader = '<th>&nbsp;</th>'
              for param in covarParameters:
                theader += '\n<th>' + param + '</th>'
              writehandle.write(theader + '\n')
              writehandle.write('</tr>\n</thead>\n<tbody>\n')
              for index, entry in enumerate(covarParameters):
                if(index + 1 == len(covarParameters)):
                  trow = '<tr class="last">'
                else:
                  trow = '<tr>'
                trow += '<td class="global">' + entry + '</td>'
                for index2 in range(len(covarParameters)):
                  if(index == index2):
                    trow += '\n<td class="diag">'
                  elif(abs(covar[index][index2]) > .9):
                    trow += '\n<td class="excessive">'
                  else:
                    trow += '\n<td>'
                  trow += self.parent.formatNumber(covar[index][index2]) + '</td>'
                trow += '\n</tr>\n'
                writehandle.write(trow)
              writehandle.write('</tbody>\n</table>\n')

            writehandle.write('</div>\n')
            writehandle.write('</div>\n')
            writehandle.write('</div>\n')
          else:
            pass
              
          writehandle.write('<div class="container">\n')
  
          # write graphics
          if(flag or flag2):
            writehandle.write('<div class="flexmatic2">\n')
            writehandle.write('<a href="javascript:;" onclick="togglitsch(\'plot\')" class="toggle">\n')
            writehandle.write('<h3 id="plot_ctrl">&#9746; Plot and Residuals</h3>\n')
            writehandle.write('</a>\n')
            writehandle.write('<div id="plot" style="display:block;">\n')
            writehandle.write('<button id="plot_button" class="little">Copy Plots</button>\n')
            writehandle.write('<div id="plot_content">\n')
            max_width = 0
    
            # write plot figure if available
            if(flag):
              output = False
              for entry in svg_plot:
                if('<svg' in entry):
                  output = True
                  # extract width of SVG item
                  if('width' in entry):
                    red = entry.split('width')[1]
                    max_width = red.split('"')[1]
                if(output):
                  writehandle.write(entry)
                if('</svg' in entry):
                  output = False
                
            # write residuals figure if available
            if(flag2):
              output = False
              for entry in svg_resid:
                if('<svg' in entry):
                  output = True
                if(output):
                  writehandle.write(entry)
                if('</svg' in entry):
                  output = False
            writehandle.write('</div>\n')
            writehandle.write('</div>\n')
            writehandle.write('</div>\n')
  
          # count how many curves are on display currently
          curvesOnDisplay = [i for i in self.parent.fit if i.visibility]
          
          # write parameters
          if(len(curvesOnDisplay)):
            writehandle.write('<div class="flexmatic">\n')
            writehandle.write('<a href="javascript:;" onclick="togglitsch(\'param\')" class="toggle">\n')
            writehandle.write('<h3 id="param_ctrl">&#9746; Parameter Values</h3>\n')
            writehandle.write('</a>\n')
            writehandle.write('<div id="param" style="display:block;">\n')
            writehandle.write('<button id="param_button" class="little">Copy Parameters</button>\n')
            writehandle.write('<div id="param_content">\n')
    
            # function to write parameter values and statistics for one curve
            def writeParamTable(paramList, param_active, param, confidence, confidence_apriori, chisq, redchisq, globalparam):
              writehandle.write('<table class="param">\n<thead>\n<tr>\n')
              writehandle.write('<th>Name</th>\n<th>Value</th>\n<th>Confidence</th>\n<th>Conf. <i>a priori</i></th>\n')
              writehandle.write('</tr>\n</thead>\n')
      
              writehandle.write('<tbody>\n')
              for i, j, k, l, m, n in zip(paramList, param, confidence, confidence_apriori, param_active, globalparam):
                if(i == paramList[-1]):
                  writehandle.write('<tr class="last">\n')
                else:
                  writehandle.write('<tr>\n')
                if(m):
                  if(n):
                    writehandle.write('<td class="global">' + str(i) + '</td>\n')
                  else:
                    writehandle.write('<td>' + str(i) + '</td>\n')
                else:
                  if(n):
                    writehandle.write('<td class="fixed global">' + str(i) + '</td>\n')
                  else:
                    writehandle.write('<td class="fixed">' + str(i) + '</td>\n')
                if(m):
                  if(n):
                    tdString = '<td class="rite global">'
                  else:
                    tdString = '<td class="rite">'
                else:
                  if(n):
                    tdString = '<td class="rite fixed global">'
                  else:
                    tdString = '<td class="rite fixed">'
                writehandle.write(tdString + self.parent.formatNumber(j) + '</td>\n')
                writehandle.write(tdString + self.parent.formatNumber(k) + '</td>\n')
                writehandle.write(tdString + self.parent.formatNumber(l) + '</td>\n')
                writehandle.write('</tr>\n')
              writehandle.write('</tbody>\n</table>\n')
              if(1):
              ###if(self.parent.lastFitType != 'none'):
                try:
                  useChi = self.parent.formatNumber(chisq)
                except:
                  useChi = str(chisq)
                writehandle.write('<i>&#x1D6D8;</i><sup>2</sup> = ' + useChi + '\n')
                try:
                  useRedChi = self.parent.formatNumber(redchisq)
                except:
                  useRedChi = str(redchisq)
                writehandle.write('<br/>reduced <i>&#x1D6D8;</i><sup>2</sup> = ' + useRedChi + '\n')
    
            # check whether last fit local or global
            if(self.parent.lastFitType != 'global'):
              for index, entry in enumerate(self.parent.fit):
                if(entry.visibility):
                  tempLineString = '<span style=\'color:rgb(' + ', '.join([str(i * 255) for i in entry.style['color'][0:3]]) + ');\nfont-size:150%;\'>&#x2015;</span>'
                  writehandle.write('<h4>' + tempLineString + ' ' + str(index) + ' -- '  + entry.name + '</h4>\n')
                  paramList, param_active, param, confidence, confidence_apriori, chisq, redchisq = entry.retrieveInfoReport()
                  globalParam = [False for i in param]
                  writeParamTable(paramList, param_active, param, confidence, confidence_apriori, chisq, redchisq, globalParam)
                  writehandle.write('<br/>(fixed parameters in <i>italics</i>)\n')
                  writehandle.write('<br/>\n')
            else:
              writehandle.write('<h4>Global fit</h4>\n')
              paramList, param_active, param, confidence, confidence_apriori, chisq, redchisq, globalParam = self.parent.globalarea.reportGlobalParam()
              writeParamTable(paramList, param_active, param, confidence, confidence_apriori, chisq, redchisq, globalParam)
              writehandle.write('<br/>(fixed parameters in <i>italics</i>)\n')
              writehandle.write('<br/>(global parameters in <b>bold</b>)\n')
    
            writehandle.write('</div>\n')
            writehandle.write('</div>\n')
            writehandle.write('</div>\n')
  
          # define HTML translation of plot symbols
          translatedStyles = {'.': '&#x2022;', ',': '&#x2015;', 'o': '&#x25cf;', 'v': '&#x25bc;', '^': '&#x25b2;', '<': '&#x25c0;', '>': '&#x25b6;',\
                              '1': 'Y', '2': 'Y', '3': 'Y', '4': 'Y', '8': '&#x25cf;', 's': '&#x25fc;', 'p': '&#x2b1f;', '*': '&#x2605;',\
                              'h': '&#x2b22;', 'H': '&#x2b22;', '+': '<b>+</b>', 'x': '&#x00d7;', 'D': '&#x25c6;', 'd': '&#x25c6;',\
                              '|': '|', '_': '-', 'P': '&#x2b1f;', 'X': '&#x00d7;', 0: '-', 1: '-', 2: '|', 3: '|', 4: '&#x25c0;', 5: '&#x25b6;',\
                              6: '&#x25b2;', 7: '&#x25bc;', 8: '&#x25c0;', 9: '&#x25b6;', 10: '&#x25b2;', 11: '&#x25bc;',\
                              'None': '&#x2015;', None: '&#x2015;', ' ': '&#x2015;', '': '&#x2015;'}
          filledStyles = ['.', 'o', 'v', '^', '<', '>', '8', 's', 'p', 'P', '*', 'h', 'H', 'd', 'D', 'X', 4, 5, 6, 7, 8, 9, 10, 11]
          translatedOpenStyles = {'.': '&#x25cb;', 'o': '&#x25cb;', 'v': '&#x25bd;', '^': '&#x25b3;', '<': '&#x25c1;', '>': '&#x25b7;',\
                              '8': '&#x25cb;', 's': '&#x25fb;', 'p': '&#x2b20;', '*': '&#x2606;',\
                              'h': '&#x2b21;', 'H': '&#x2b21;', 'x': '&#x00d7;', 'D': '&#x25c7;', 'd': '&#x25c7;',\
                              'P': '&#x2b20;', 'X': '&#x00d7;', 4: '&#x25c1;', 5: '&#x25b7;',\
                              6: '&#x25b3;', 7: '&#x25bd;', 8: '&#x25c1;', 9: '&#x25b7;', 10: '&#x25b3;', 11: '&#x25bd;'}
          noneStyles = ['None', None, ' ', '']
  
          # write data
          dataFlag, dataButtons = False, []
          if(self.parent.lastFitType != 'global'):
            # collate and order data sets
            useDatasets = [i for i in self.parent.data if (i.visibility)]
            useDatasets = sorted(useDatasets, key=lambda k: k.zorder)
            if(len(useDatasets)):
              writehandle.write('<div class="flexmatic">\n')
              for entry in useDatasets:
                index = useDatasets.index(entry)
                dataButtons.append(index)
                values, roles = entry.getData_n_Fit()
                writehandle.write('<a href="javascript:;" onclick="togglitsch(\'data_' + str(index) + '\')" class="toggle">\n')
                useSymbol = entry.style['marker']
                toggledColor = False
                if((useSymbol in filledStyles) or (useSymbol in CUSTOM_MARKERS)):
                  useColor = entry.style['markerfacecolor']
                  if(((not entry.style['doFill']) or (sum(useColor[0:3]) > 2.8)) and (useSymbol in translatedOpenStyles)):
                    useColor = entry.style['markeredgecolor']
                    toggledColor = True
                elif(useSymbol in noneStyles):
                  useColor = entry.style['color']
                else:
                  useColor = entry.style['markeredgecolor']
                if(useSymbol in translatedStyles):
                  translatedSymbol = translatedStyles[useSymbol]
                  if(((not entry.style['doFill']) or toggledColor) and (useSymbol in translatedOpenStyles)):
                    translatedSymbol = translatedOpenStyles[useSymbol]
                elif(useSymbol in CUSTOM_MARKERS):
                  # replace funky symbols by default circle
                  translatedSymbol = translatedStyles['o']
                  if(not entry.style['doFill']):
                    translatedSymbol = translatedOpenStyles['o']
                else:
                  translatedSymbol = translatedStyles['None']
                tempLineString = '<span style=\'color:rgb(' + ', '.join([str(i * 255) for i in useColor[0:3]]) + ');\nfont-size:150%;\'>' + translatedSymbol + '</span>'
                writehandle.write('<h3 id="data_' + str(index) + '_ctrl">&#9744; ' + tempLineString + ' Data set ' + entry.name + '</h3>\n')
                writehandle.write('</a>\n')
                writehandle.write('<div id="data_' + str(index) + '" style="display:none;">\n')
                writehandle.write('<button id="data_' + str(index) + '_button" class="little">Copy Data</button>\n')
                if(hasattr(entry, 'notes') and (entry.notes != '')):
                  writehandle.write('<span class="notes"><span style="text-decoration:underline;margin-left:-5px;">Notes</span>\n')
                  writehandle.write(entry.notes.replace('"', '\"'))
                  writehandle.write('</span>\n')
                writehandle.write('<div id="data_' + str(index) + '_content">\n')
                writehandle.write('<table class="misc">\n<thead>\n<tr>\n')
                for header in roles:
                  writehandle.write('<th>' + str(header) + '</th>\n')
                writehandle.write('</tr>\n</thead>\n')
                # write data table
                writehandle.write('<tbody>\n')
                resultsData = values
                # check for selection
                if(self.parent.selectedData.isLive and (entry == self.parent.data[self.parent.activeData])):
                  indices = self.parent.selectedData.getIndices()
                else:
                  indices = []
                for count, row in enumerate(resultsData):
                  # deal with selection
                  if(len(roles) and (count < len(indices)) and indices[count]):
                    if(count + 1 < len(resultsData)):
                      writehandle.write('<tr class="selected">\n')
                    else:
                      writehandle.write('<tr class="selected last">\n')
                  elif(count + 1 < len(resultsData)):
                    writehandle.write('<tr>\n')
                  else:
                    writehandle.write('<tr class="last">\n')
                  row = [self.parent.formatNumber(i) for i in row]
                  rowstring = '</td><td>'.join(row)
                  writehandle.write('<td>' + rowstring + '</td>')
                  writehandle.write('\n</tr>\n')
      
                writehandle.write('</tbody>\n</table>\n')
                # did we have selected data?
                if(len(indices)):
                  writehandle.write('<br/>(selected data in <span style="background-color:#aaaaee;">blue</span>)\n')
                writehandle.write('</div>\n')
                writehandle.write('</div>\n')
  
              writehandle.write('</div>\n')
          else:
            if(len(self.parent.lastFitGlobalDataSets)):
              for index in self.parent.lastFitGlobalDataSets:
                if(index < len(self.parent.data)):
                  dataFlag = True
                  
              if(dataFlag):
                writehandle.write('<div class="flexmatic">\n')
                for index in self.parent.lastFitGlobalDataSets:
                  if(index < len(self.parent.data)):
                    dataButtons.append(index)
                    values, roles = self.parent.data[index].getData_n_Fit()
                    writehandle.write('<a href="javascript:;" onclick="togglitsch(\'data_' + str(index) + '\')" class="toggle">\n')
                    useSymbol = self.parent.data[index].style['marker']
                    toggledColor = False
                    if((useSymbol in filledStyles) or (useSymbol in CUSTOM_MARKERS)):
                      useColor = self.parent.data[index].style['markerfacecolor']
                      if(((not self.parent.data[index].style['doFill']) or (sum(useColor[0:3]) > 2.8)) and (useSymbol in translatedOpenStyles)):
                        useColor = self.parent.data[index].style['markeredgecolor']
                        toggledColor = True
                    elif(useSymbol in noneStyles):
                      useColor = self.parent.data[index].style['color']
                    else:
                      useColor = self.parent.data[index].style['markeredgecolor']
                    if(useSymbol in translatedStyles):
                      translatedSymbol = translatedStyles[useSymbol]
                      if(((not self.parent.data[index].style['doFill']) or toggledColor) and (useSymbol in translatedOpenStyles)):
                        translatedSymbol = translatedOpenStyles[useSymbol]
                    elif(useSymbol in CUSTOM_MARKERS):
                      # replace funky symbols by default circle
                      translatedSymbol = translatedStyles['o']
                      if(not self.parent.data[index].style['doFill']):
                        translatedSymbol = translatedOpenStyles['o']
                    else:
                      translatedSymbol = translatedStyles['None']
                    tempLineString = '<span style=\'color:rgb(' + ', '.join([str(i * 255) for i in useColor[0:3]]) + ');\nfont-size:150%;\'>' + translatedSymbol + '</span>'
                    writehandle.write('<h3 id="data_' + str(index) + '_ctrl">&#9744; ' + tempLineString + ' Data set ' + self.parent.data[index].name + '</h3>\n')
                    writehandle.write('</a>\n')
                    writehandle.write('<div id="data_' + str(index) + '" style="display:none;">\n')
                    writehandle.write('<button id="data_' + str(index) + '_button" class="little">Copy Data</button>\n')
                    if(hasattr(self.parent.data[index], 'notes') and (self.parent.data[index].notes != '')):
                      writehandle.write('<span class="notes"><span style="text-decoration:underline;margin-left:-5px;">Notes</span>\n')
                      writehandle.write(self.parent.data[index].notes.replace('"', '\"'))
                      writehandle.write('</span>\n')
                    writehandle.write('<div id="data_' + str(index) + '_content">\n')
                    writehandle.write('<table class="misc">\n<thead>\n<tr>\n')
                    for header in roles:
                      writehandle.write('<th>' + str(header) + '</th>\n')
                    writehandle.write('</tr>\n</thead>\n')
                    # write data table
                    writehandle.write('<tbody>\n')
                    resultsData = values
                    for count, row in enumerate(resultsData):
                      if(count + 1 < len(resultsData)):
                        writehandle.write('<tr>\n')
                      else:
                        writehandle.write('<tr class="last">\n')
                      row = [self.parent.formatNumber(i) for i in row]
                      rowstring = '</td><td>'.join(row)
                      writehandle.write('<td>' + rowstring + '</td>')
                      writehandle.write('\n</tr>\n')
      
                    writehandle.write('</tbody>\n</table>\n')
                    writehandle.write('</div>\n')
                    writehandle.write('</div>\n')
                
                writehandle.write('</div>\n')
  
          # write simulated data as well
          functionFlag, functionButtons = False, []
          if(self.parent.lastFitType != 'global'):
            # collate and order curves
            useCurves = [i for i in self.parent.fit if (i.visibility)]
            useCurves = sorted(useCurves, key=lambda k: k.zorder)
            if(len(useCurves)):
              writehandle.write('<div class="flexmatic">\n')
              for entry in useCurves:
                index = useCurves.index(entry)
                functionButtons.append(index)
                simX, simY = entry.x, entry.y
                writehandle.write('<a href="javascript:;" onclick="togglitsch(\'fxn_' + str(index) + '\')" class="toggle">\n')
                tempLineString = '<span style=\'color:rgb(' + ', '.join([str(i * 255) for i in entry.style['color'][0:3]]) + ');\nfont-size:150%;\'>&#x2015;</span>'
                writehandle.write('<h3 id="fxn_' + str(index) + '_ctrl">&#9744; ' + tempLineString + ' Curve ' + entry.name + '</h3>\n')
                writehandle.write('</a>\n')
                writehandle.write('<div id="fxn_' + str(index) + '" style="display:none;">\n')
                writehandle.write('<span class="mypre">\n' + entry.ffuncstr_base + '</span>\n<br/><br/>\n')
                writehandle.write('<button id="fxn_' + str(index) + '_button" class="little">Copy Curve</button>\n')
                if(hasattr(entry, 'notes') and (entry.notes != '')):
                  writehandle.write('<span class="notes"><span style="text-decoration:underline;margin-left:-5px;">Notes</span>\n')
                  writehandle.write(entry.notes.replace('"', '\"'))
                  writehandle.write('</span>\n')
                writehandle.write('<div id="fxn_' + str(index) + '_content">\n')
                writehandle.write('<table class="misc">\n<thead>\n<tr>\n')
                writehandle.write('<th>x</th>\n<th>f(x)</th>\n')
                writehandle.write('<tbody>\n')
                for count, row in enumerate(range(len(simX))):
                  if(count + 1 < len(simX)):
                    writehandle.write('<tr>\n')
                  else:
                    writehandle.write('<tr class="last">\n')
                  writehandle.write('<td>' + self.parent.formatNumber(simX[row]) + '</td>')
                  writehandle.write('<td>' + self.parent.formatNumber(simY[row]) + '</td>')
                  writehandle.write('\n</tr>\n')
                writehandle.write('</tbody>\n</table>\n')
                writehandle.write('</div>\n')
                writehandle.write('</div>\n')
  
              writehandle.write('</div>\n')
          else:
            if(len(self.parent.lastFitGlobalCurves)):
              for index in self.parent.lastFitGlobalCurves:
                if(index < len(self.parent.fit)):
                  functionFlag = True
                  
            if(functionFlag):
              writehandle.write('<div class="flexmatic">\n')
              for index in self.parent.lastFitGlobalCurves:
                if(index < len(self.parent.fit)):
                  functionButtons.append(index)
                  simX, simY = self.parent.fit[index].x, self.parent.fit[index].y
                  writehandle.write('<a href="javascript:;" onclick="togglitsch(\'fxn_' + str(index) + '\')" class="toggle">\n')
                  tempLineString = '<span style=\'color:rgb(' + ', '.join([str(i * 255) for i in self.parent.fit[index].style['color'][0:3]]) + ');\nfont-size:150%;\'>&#x2015;</span>'
                  writehandle.write('<h3 id="fxn_' + str(index) + '_ctrl">&#9744; ' + tempLineString + ' Curve ' + self.parent.fit[index].name + '</h3>\n')
                  writehandle.write('</a>\n')
                  writehandle.write('<div id="fxn_' + str(index) + '" style="display:none;">\n')
                  writehandle.write('<span class="mypre">\n' + self.parent.fit[index].ffuncstr_base + '</span>\n<br/><br/>\n')
                  writehandle.write('<button id="fxn_' + str(index) + '_button" class="little">Copy Curve</button>\n')
                  if(hasattr(self.parent.fit[index], 'notes') and (self.parent.fit[index].notes != '')):
                    writehandle.write('<span class="notes"><span style="text-decoration:underline;margin-left:-5px;">Notes</span>\n')
                    writehandle.write(self.parent.fit[index].notes.replace('"', '\"'))
                    writehandle.write('</span>\n')
                  writehandle.write('<div id="fxn_' + str(index) + '_content">\n')
                  writehandle.write('<table class="misc">\n<thead>\n<tr>\n')
                  writehandle.write('<th>x</th>\n<th>f(x)</th>\n')
                  writehandle.write('<tbody>\n')
                  for count, row in enumerate(range(len(simX))):
                    if(count + 1 < len(simX)):
                      writehandle.write('<tr>\n')
                    else:
                      writehandle.write('<tr class="last">\n')
                    writehandle.write('<td>' + self.parent.formatNumber(simX[row]) + '</td>')
                    writehandle.write('<td>' + self.parent.formatNumber(simY[row]) + '</td>')
                    writehandle.write('\n</tr>\n')
                  writehandle.write('</tbody>\n</table>\n')
                  writehandle.write('</div>\n')
                  writehandle.write('</div>\n')
  
              writehandle.write('</div>\n')
            
          writehandle.write('</div>\n')
          writehandle.write('<div class="disclaimer">generated by fit-o-mat v' + VERSION + ' by @MoeglichLab</div>\n')
  
          # add style definitions
          writehandle.write('</body>\n')
          writehandle.write('<style type="text/css">\n')
          writehandle.write('body {\nfont-family: Calibri,Helvetica,Arial,sans-serif;\n}\n')
          writehandle.write('.container {\npadding: 0;\nmargin: 0;\ndisplay: flex;\nflex-direction: row;\
            \nalign-items: flex-start;\n}\n')
          writehandle.write('.flexmatic {\npadding: 5px;\nmargin: 0;\nflex: 0 0 auto;\n}\n')
          writehandle.write('.flexmatic2 {\npadding: 5px;\nmargin: 0;\nflex: 1 1 auto;\n')
          if(max_width != 0):
            writehandle.write('max-width: ' + max_width + ';\n')
          writehandle.write('min-width: 250pt;\n}\n')
          writehandle.write('svg {\nwidth: 100%;\nheight: auto;\n}\n')
          writehandle.write('h2 {\ncolor: rgb(13, 78, 179);\nfont-size: 250%;\nfont-weight: normal;\n}\n')
          writehandle.write('h3 {\ntext-align: left;\nwhite-space: nowrap;\n}\n')
          writehandle.write('h3:hover {\nbackground-color: rgba(255, 243, 185, 255);\ndisplay: table;\n}\n')
          writehandle.write('.disclaimer {\nposition: fixed;\nbottom: 0px;\nright: 0px;\nfont-size: 125%;\
                                          \ncolor: rgb(13, 78, 179);\nbackground-color: rgba(255, 255, 255, 0.5);\
                                          \nborder: 1px;\nborder-style: solid;\nborder-radius: 2px;\
                                          \nborder-color: #333333;\npadding: 2px 10px 2px 10px;\n}\n')
          writehandle.write('td.rite {\ntext-align: right;\n}\n')
          writehandle.write('td.fixed {\ncolor: #555555;\nfont-style: italic;\n}\n')
          writehandle.write('td.global {\nfont-weight: bold;\n}\n')
          writehandle.write('td.diag {\nbackground-color: #aaaaaa;\n}\n')
          writehandle.write('td.excessive {\nbackground-color: #ffaabb;\n}\n')
          writehandle.write('tr.last > td {\nborder-bottom: 1px solid #333333;\n}\n')
          writehandle.write('tr.selected > td {\nbackground-color: #aaaaee !important;\n}\n')
          writehandle.write('th {\nborder-bottom: 1px solid #333333;\n}\n')
          writehandle.write('table.param > tbody > tr:nth-child(odd) > td {\nbackground-color: #e5e5e5;\n}\n')
          writehandle.write('table.misc > tbody > tr:nth-child(odd) > td {\nbackground-color: #e5e5e5;\n}\n')
          writehandle.write('a.toggle {\ntext-decoration: none;\ncolor: inherit;\n}\n')
          writehandle.write('button.little {\nfont-size: 75%;\n}\n')
          writehandle.write('.mypre {\nwhite-space: pre;\n}\n')
          writehandle.write('.notes {\nbackground-color: fffcb8;\ndisplay: block;\nmargin: 10px;\npadding: 5px;\nwhite-space:pre;\n}\n')
          writehandle.write('</style>\n\n')
  
          # add script controls
          writehandle.write('<script language="javascript">\n')
          writehandle.write('function togglitsch(id){\n')
          writehandle.write('\tvar content;\n')
          writehandle.write('\tif(document.getElementById(id).style.display == \'none\'){\n')
          writehandle.write('\t\tdocument.getElementById(id).style.display = \'block\';\n')
          writehandle.write('\t\tcontent = document.getElementById(id + \'_ctrl\').innerHTML;\n')
          writehandle.write('\t\tdocument.getElementById(id + \'_ctrl\').innerHTML = \'&#9746;\' + content.slice(1)\n')
          writehandle.write('\t} else {\n')
          writehandle.write('\t\tdocument.getElementById(id).style.display = \'none\';\n')
          writehandle.write('\t\tcontent = document.getElementById(id + \'_ctrl\').innerHTML;\n')
          writehandle.write('\t\tdocument.getElementById(id + \'_ctrl\').innerHTML = \'&#9744;\' + content.slice(1)\n')
          writehandle.write('\t}\n}\n\n')
  
          writehandle.write('// controls for copy to clipboard\n')
          writehandle.write('function copyHelper(item, mode){\n')
          writehandle.write('\t// selects target range and then restores previous selection\n')
          writehandle.write('\tvar sel, backup = [];\n\n')
          writehandle.write('\t// backup ranges\n')
          writehandle.write('\tsel = window.getSelection();\n')
          writehandle.write('\tfor(let i = 0; i < sel.rangeCount; i++) {\n')
          writehandle.write('\t\tbackup[i] = sel.getRangeAt(i);\n')
          writehandle.write('\t}\n\n')
          writehandle.write('\t// delete previous ranges\n')
          writehandle.write('\tsel.removeAllRanges();\n')
          writehandle.write('\tif(mode){\n')
          writehandle.write('\t\tsel.addRange(item);\n')
          writehandle.write('\t} else {\n')
          writehandle.write('\t\titem.focus();\n')
          writehandle.write('\t\titem.select();\n')
          writehandle.write('\t}\n')
          writehandle.write('\t// copy selection\n')
          writehandle.write('\tdocument.execCommand(\'copy\');\n')
          writehandle.write('\t// restore previous selection\n')
          writehandle.write('\tsel.removeAllRanges();\n')
          writehandle.write('\tfor(let i = 0; i < backup.length; i++) {\n')
          writehandle.write('\t\tsel.addRange(backup[i]);\n')
          writehandle.write('\t}\n}\n\n')
  
          writehandle.write('function copyThis(targetId){\n')
          writehandle.write('\tvar body = document.body, target, range;\n')
          writehandle.write('\ttarget = document.getElementById(targetId);\n\n')
          writehandle.write('\tif (document.createRange && window.getSelection) {\n')
          writehandle.write('\t\t// select target stuff\n')
          writehandle.write('\t\trange = document.createRange();\n')
          writehandle.write('\t\ttry {\n')
          writehandle.write('\t\t\trange.selectNodeContents(target);\n')
          writehandle.write('\t\t} catch (e) {\n')
          writehandle.write('\t\t\trange.selectNode(target);\n')
          writehandle.write('\t\t}\n')
          writehandle.write('\t\tcopyHelper(range, true);\n')
          writehandle.write('\t}\n}\n\n')
          
          writehandle.write('// enable copy of SVG plots\n')
          writehandle.write('function copyThisInnerHTML(targetId){\n')
          writehandle.write('\tvar target, textarea, scrollX, scrollY;\n')
          writehandle.write('\ttarget = document.getElementById(targetId);\n')
          writehandle.write('\t// create dummy text area to temporarily paste innerHTML data\n')
          writehandle.write('\ttextarea = document.createElement(\'textarea\');\n')
          writehandle.write('\ttextarea.style.height = 0;\n')
          writehandle.write('\tscrollX = window.scrollX;\n')
          writehandle.write('\tscrollY = window.scrollY;\n')
          writehandle.write('\tdocument.body.appendChild(textarea);\n')
          writehandle.write('\t// now fill in textarea\n')
          writehandle.write('\ttextarea.value = target.innerHTML;\n')
          writehandle.write('\t// now call copyThis() on the temp object\n')
          writehandle.write('\tcopyHelper(textarea, false);\n')
          writehandle.write('\t// clean up the mess\n')
          writehandle.write('\tdocument.body.removeChild(textarea);\n')
          writehandle.write('\twindow.scroll(scrollX, scrollY);\n')
          writehandle.write('}\n\n')
  
          writehandle.write('// set up ctrl buttons\n')
          if(self.parent.lastFitType != 'none'):
            items = ['results']
          else:
            items = []
          for entry in dataButtons:
            items.append('data_' + str(entry))
          if(len(curvesOnDisplay)):
            items.append('param')
          for entry in functionButtons:
            items.append('fxn_' + str(entry))
          for entry in items:
            writehandle.write('var btn_' + entry + ' = document.getElementById(\'' + entry + '_button\');\n')
            writehandle.write('btn_' + entry + '.addEventListener(\'click\', function(event) {\n')
            writehandle.write('\tcopyThis(\'' + entry + '_content\');\n')
            writehandle.write('});\n\n')
        
          writehandle.write('var btn_plot = document.getElementById(\'plot_button\');\n')
          writehandle.write('btn_plot.addEventListener(\'click\', function(event) {\n')
          writehandle.write('\tcopyThisInnerHTML(\'plot_content\');\n')
          writehandle.write('});\n\n')
  
          writehandle.write('</script>\n')
          writehandle.write('</html>\n')
        
        # save filename for future use
        self.currExportFile = filename
      
  def writeXLS(self, filename=None):
    # writes Results table to Excel file
    # this function uses openpyxl which, however, has awful documentation
    # hence, the output is somewhat rudimentary
    # (sorry, but do not have time to dive deeper into the inane .xlsx XML format)
    # (once again, it goes to show that Microsoft are a bunch of jerks)
    if(filename != None):
      workbook = openpyxl.Workbook()
      availableSheets = workbook.get_sheet_names()
      if(not len(availableSheets)):
        workbook.create_sheet()
      worksheet = workbook.active
      worksheet.title = 'Fit-o-mat forever'

      # check whether current fit results are available
      index = 0
      if(self.parent.lastFitType != 'none'):
        # further check whether these are local or global results
        if(self.parent.lastFitType == 'local'):
          fitresults = self.parent.fitarea.outstring.splitlines()
        else:
          fitresults = self.parent.globalarea.globalOutstring.splitlines()
        for index, entry in enumerate(fitresults):
          worksheet.cell(row=1, column=index + 1).value = entry
      if(not index):
        offset = index + 3
      else:
        offset = 1
        
      # write header
      if((self.resultstable.tableModel != None) and (self.resultstable.tableModel.columnCount() > 0)):
        resultsHeader = self.resultstable.tableModel.getHeaders()
        for col, header in enumerate(resultsHeader):
          worksheet.cell(row=offset, column=col + 1).value = header
        # write data
        resultsData = self.resultstable.tableModel.getAllData()
        for rowIndex, row in enumerate(resultsData):
          row = [float(self.parent.formatNumber(i)) if (not j in ['labels', 'labels2']) else i for i, j in zip(row, resultsHeader)]
          for colIndex, entry in enumerate(row):
            worksheet.cell(row=rowIndex + offset + 1, column=colIndex + 1).value = entry
        coloffset = self.resultstable.tableModel.columnCount() + 2
      else:
        coloffset = 1
        
      # write simulated data as well
      simX, simY = self.parent.fit[self.parent.activeFit].x, self.parent.fit[self.parent.activeFit].y
      worksheet.cell(offset, coloffset).value = 'x'
      worksheet.cell(offset, coloffset + 1).value = 'f(x)'
      for row in range(len(simX)):
        worksheet.cell(row=row + offset + 1, column=coloffset).value = simX[row]
        worksheet.cell(row=row + offset + 1, column=coloffset + 1).value = simY[row]

      # # write graphics
      chart = openpyxl.chart.ScatterChart(marker=False)
      chart.title = 'Fit-o-mat Results'

      # plot fit
      xval = openpyxl.chart.Reference(worksheet, min_col=coloffset, min_row=offset + 1, max_col=coloffset, max_row=offset + len(simX))
      yval = openpyxl.chart.Reference(worksheet, min_col=coloffset + 1, min_row=offset + 1, max_col=coloffset + 1, max_row=offset + len(simX))
      # seriously Microsoft, you utter jerks
      # putting y values before x values? what the hell, complete morons!
      series1 = openpyxl.chart.Series(yval, xval, title='fit')
      # set props and add to chart
      series1.graphicalProperties.line.solidFill = 'FF0000'
      series1.marker.symbol = None
      chart.series.append(series1)
      
      # plot data (if present)
      if(('x' in self.descriptors) and ('y' in self.descriptors)):
        xcol, ycol = self.descriptors.index('x'), self.descriptors.index('y')
        rowcount = self.resultstable.tableModel.rowCount()
        xval2 = openpyxl.chart.Reference(worksheet, min_col=xcol + 1, min_row=offset + 1, max_col=xcol + 1, max_row=offset + rowcount)
        yval2 = openpyxl.chart.Reference(worksheet, min_col=ycol + 1, min_row=offset + 1, max_col=ycol + 1, max_row=offset + rowcount)
        series2 = openpyxl.chart.Series(yval2, xval2, title='data')
        # set props and add to chart
        series2.graphicalProperties.line.solidFill = None
        series2.graphicalProperties.line.noFill =True
        series2.marker.symbol = 'circle'
        series2.marker.graphicalProperties.solidFill = '000000'

        # error bars are not supported for time being b/c:
        # 1. documentation of openpyxl positively atrocious
        # 2. .xlsx XML format absolutely insane
        chart.series.append(series2)

      # requisite chart formatting
      chart.x_axis.title = self.parent.plotArea.labelX
      chart.y_axis.title = self.parent.plotArea.labelX
      chart.x_axis.delete = False
      chart.y_axis.delete = False
      chart.x_axis.minorGridlines = None
      chart.x_axis.majorGridlines = None
      chart.y_axis.minorGridlines = None
      chart.y_axis.majorGridlines = None

      # place chart on worksheet
      worksheet.add_chart(chart, chr(65 + 1 + coloffset) + str(offset))

      try:
        workbook.save(filename)
      except:
        self.parent.statusbar.showMessage('Cannot write data file ' + filename, self.parent.STATUS_TIME)

  # this is the original implementation using xlsxwriter which, however, seems to be going stale
  # switch to openpyxl instead but retain this code for time being as it may become useful again, tbd
  # actually, retain xlsxwriter as preferred option as openpyxl is a major mess, phew!
  def writeXLS_old(self, filename=None):
    # writes Results table to Excel file
    if(filename != None):
      try:
        workbook = xlsxwriter.Workbook(filename, options={'nan_inf_to_errors': True})
        worksheet = workbook.add_worksheet()
      except:
        self.parent.statusbar.showMessage('Cannot write data file ' + filename, self.parent.STATUS_TIME)
      else:
        # check whether current fit results are available
        index = 0
        if(self.parent.lastFitType != 'none'):
          # further check whether these are local or global results
          if(self.parent.lastFitType == 'local'):
            fitresults = self.parent.fitarea.outstring.splitlines()
          else:
            fitresults = self.parent.globalarea.globalOutstring.splitlines()
          for index, entry in enumerate(fitresults):
            worksheet.write(index, 0, entry)
        offset = index + 2
        # write header
        if((self.resultstable.tableModel != None) and (self.resultstable.tableModel.columnCount() > 0)):
          resultsHeader = self.resultstable.tableModel.getHeaders()
          worksheet.write_row(offset, 0, resultsHeader)
          # write data
          resultsData = self.resultstable.tableModel.getAllData()
          for rowIndex, row in enumerate(resultsData):
            row = [float(self.parent.formatNumber(i)) if (not j in ['labels', 'labels2']) else i for i, j in zip(row, resultsHeader)]
            worksheet.write_row(rowIndex + offset + 1, 0, row)
          coloffset = self.resultstable.tableModel.columnCount() + 1
        else:
          coloffset = 0
        # write simulated data as well
        simX, simY = self.parent.fit[self.parent.activeFit].x, self.parent.fit[self.parent.activeFit].y
        worksheet.write(offset, coloffset, 'x')
        worksheet.write(offset, coloffset + 1, 'f(x)')
        for row in range(len(simX)):
          worksheet.write(row + offset + 1, coloffset, simX[row])
          worksheet.write(row + offset + 1, coloffset + 1, simY[row])
        # write graphics
        chart = workbook.add_chart({'type': 'scatter'})
        worksheet.insert_chart(chr(coloffset + 1 + 67) + str(offset + 2), chart)
        # write fit
        chart.add_series({'categories': ['Sheet1', offset + 1, coloffset, offset + row + 1, coloffset],\
                          'values': ['Sheet1', offset + 1, coloffset + 1, offset + row + 1, coloffset + 1],\
                          'line': {'color': 'red'},\
                          'name': 'fit',\
                          'marker': {'type': 'none'}})
        # write data (if present)
        if(('x' in self.descriptors) and ('y' in self.descriptors)):
          xcol, ycol = self.descriptors.index('x'), self.descriptors.index('y')
          rowcount = self.resultstable.tableModel.rowCount()
          chartdict = {'categories': ['Sheet1', offset+1, xcol, offset + rowcount, xcol],\
                            'values': ['Sheet1', offset+1, ycol, offset + rowcount, ycol],\
                            'name': 'data',\
                            'marker': {'type': 'diamond'}}
          # include x-errors
          if('xerr' in self.descriptors):
            xerrcol = self.descriptors.index('xerr')
            rangestring = 'Sheet1!$' + chr(xerrcol + 65) + '$' + str(offset + 2) + ':$' + chr(xerrcol + 65) + '$' + str(offset + 1 + rowcount)
            chartdict['x_error_bars'] = {'type': 'custom',\
                     'plus_values': rangestring,\
                     'minus_values': rangestring}
          # include y-errors
          if('yerr' in self.descriptors):
            yerrcol = self.descriptors.index('yerr')
            rangestring = 'Sheet1!$' + chr(yerrcol + 65) + '$' + str(offset + 2) + ':$' + chr(yerrcol + 65) + '$' + str(offset + 1 + rowcount)
            chartdict['y_error_bars'] = {'type': 'custom',\
                     'plus_values': rangestring,\
                     'minus_values': rangestring}
          chart.add_series(chartdict)
    
        workbook.close()

  def setDataSet(self, currIndex=0, maxIndex=None):
    # updates spinbox for dataset selection
    if(maxIndex != None):
      self.dataSetSpinBox.setMaximum(maxIndex + 1)
      if(maxIndex):
        self.dataSetSpinBox.setEnabled(True)
      else:
        self.dataSetSpinBox.setEnabled(False)
    # set spin box
    self.dataSetSpinBox.blockSignals(True)
    self.dataSetSpinBox.setValue(currIndex + 1)
    self.dataSetSpinBox.blockSignals(False)
    # trigger update
    self.changeDataSet()

  def changeDataSet(self):
    # change dataset to display
    currIndex = self.dataSetSpinBox.value()
    values, descriptors = self.parent.data[currIndex - 1].getData_n_Fit()
    labels = self.parent.data[currIndex - 1].getLabels(labels2=False)
    labels2 = self.parent.data[currIndex - 1].getLabels(labels2=True)
    self.dataSetName.setText(self.parent.data[currIndex - 1].name)
    # now set table content
    self.updateResults(values=values, descriptors=descriptors, labels=labels, labels2=labels2)
    # now check for presence of selectedData and update row selection
    if(self.parent.selectedData.isLive and (currIndex == self.parent.activeData + 1)):
      selectIndices = [i for i, j in enumerate(self.parent.selectedData.getIndices()) if j]
      self.selectTheseRows(selectIndices)
    # check whether data set has labels and update button accordingly
    if(len(labels)):
      self.addLabelsButton.setEnabled(False)
    else:
      self.addLabelsButton.setEnabled(True)
    
  def updateResults(self, values=[], descriptors=[], labels=[], labels2=[]):
    # updates results table
    if(len(values)):
      # prepare table
      if(len(labels) or len(labels2)):
        values = values.tolist()
        if(len(labels)):
          descriptors.append('labels')
          values = [i + [str(j)] for i, j in zip(values, labels)]
        if(len(labels2)):
          descriptors.append('labels2')
          values = [i + [str(j)] for i, j in zip(values, labels2)]
      self.descriptors = descriptors
      self.resultstable.tableModel = TableModel(values, self.resultstable)
      self.resultstable.setModel(self.resultstable.tableModel)
      self.resultstable.tableModel.setAllHeaders(self.descriptors)
      self.resultstable.updateColumnStatistics()

      # need to update font size correctly
      if(self.resultstable.currZoom >= 0):
        font = self.resultstable.tableModel.getFont()
        font.setPointSize(self.resultstable.zoomLevels[self.resultstable.currZoom])

      # set col width
      self.colWidth = int(self.resultstable.size().width() / 4.5)
      hheader = self.resultstable.horizontalHeader()
      hheader.setDefaultSectionSize(self.colWidth)

      # sponsored by recent PyQt5 version, we have to reset the row height
      vheader = self.resultstable.verticalHeader()
      vheader.setDefaultSectionSize(self.rowHeight)
      vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    else:
      self.resultstable.tableModel = None
      nullModel = TableModel([], self.resultstable)
      self.resultstable.setModel(nullModel)

    # set col widths
    if(len(descriptors)):
      colWidth = int(self.resultstable.size().width() / 4.5)
      colWidth = min(120, self.colWidth)
      hheader = self.resultstable.horizontalHeader()
      maxSize = int(0.5 * self.size().width())
      # limit resizing to first 15 columns as it becomes mortally slow otherwise
      maxDimX = min(len(descriptors), 15)
      for entry in range(maxDimX):
        hheader.setSectionResizeMode(entry, QtWidgets.QHeaderView.ResizeMode.Interactive)
        useSize = max(self.mySizeHintForColumn(entry) + 2, self.minColWidth)
        # restrict column size to max. 50% of visible area (to allow easy resizing)
        useSize = min(useSize, maxSize)
        hheader.resizeSection(entry, useSize)
    
  def mySizeHintForColumn(self, index):
    # custom size hint for column sizing to eke out some speed
    # INIT_SEARCH ensures that initially visible cells are used
    INIT_SEARCH, MAX_SEARCH, SPACER = 20, 50, 10
    # for size calculation, truncate float numbers after 6 significant places
    PRECISION_STRING = '%.6g'
    storeFontMetrics = self.fontMetrics()
    
    # analyze column contents
    data = self.resultstable.tableModel.getDataColumn(index)
    if(len(data) < MAX_SEARCH):
      # search through entire table
      sizes = [storeFontMetrics.horizontalAdvance(PRECISION_STRING % i) if type(i) == type(2.3) else storeFontMetrics.horizontalAdvance(str(i)) for i in data]
      if(len(sizes)):
        return max(sizes) + SPACER
      else:
        return SPACER
    else:
      # search through MAX_SEARCH randomly chosen cells
      sizes = data[:INIT_SEARCH] + np.random.choice(data[INIT_SEARCH:], size=MAX_SEARCH - INIT_SEARCH, replace=False).tolist()
      if(len(sizes)):
        maxelement = max(sizes, key = lambda i: len(PRECISION_STRING % i) if type(i) == type(2.3) else len(str(i)))
        if(type(maxelement) == type(2.3)):
          maxelement = PRECISION_STRING % maxelement
        return storeFontMetrics.horizontalAdvance(str(maxelement)) + SPACER
      else:
        return SPACER

  def keyPressEvent(self, event):
    if event.matches(QtGui.QKeySequence.StandardKey.Copy):
      # prepare output
      selind = self.resultstable.selectionModel().selectedRows()
      selind = sorted([i.row() for i in selind])      
      # get data
      selectedData = self.resultstable.tableModel.getDataRows(selind)
      output = ''
      for row in selectedData:
        row = [str(i) for i in row]
        output += '\t'.join(row) + '\n'
      
      clipboard = QtWidgets.QApplication.clipboard()
      clipboard.setText(output)
    else:
      QtWidgets.QWidget.keyPressEvent(self, event)

class BlankResizeMenu(KuhMenu):
  def __init__(self, parent = None, tableWidget = None):
    super(BlankResizeMenu, self).__init__()
    self.parent = parent
    self.tableWidget = tableWidget
    self.nrow, self.ncol = self.tableWidget.getDimension()
      
    # int validator
    self.validInt = MyValidInt()

    # set up GUI
    self.buildRessource()

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.blankResizeLabel = QtWidgets.QLabel()
    useFont = self.blankResizeLabel.font()
    useFont.setBold(True)
    self.blankResizeLabel.setFont(useFont)
    self.blankResizeLabel.setText('Blank/resize table')
    self.vLayout.addWidget(self.blankResizeLabel)
    
    # rows
    self.setRowGroup = QWidgetMac()
    self.vLayout.addWidget(self.setRowGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.setRowGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.setRowLabel = QtWidgets.QLabel('# rows')
    self.setRowLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
    self.setRowLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.setRowLabel)

    self.setRowEntry = QLineEditClick()
    self.setRowEntry.setToolTip('Number of rows in data table')
    self.setRowEntry.setText(str(self.nrow))
    self.setRowEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.setRowEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.setRowEntry.editingFinished.connect(partial(self.setDimension, self.setRowEntry, 'row', 1, 1e6))
    self.setRowEntry.setValidator(self.validInt)
    self.hLayout.addWidget(self.setRowEntry)

    # cols
    self.setColGroup = QWidgetMac()
    self.vLayout.addWidget(self.setColGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.setColGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.setColLabel = QtWidgets.QLabel('# cols')
    self.setColLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
    self.setColLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.setColLabel)

    self.setColEntry = QLineEditClick()
    self.setColEntry.setToolTip('Number of columns in data table')
    self.setColEntry.setText(str(self.ncol))
    self.setColEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.setColEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.setColEntry.editingFinished.connect(partial(self.setDimension, self.setColEntry, 'col', 1, 1e6))
    self.setColEntry.setValidator(self.validInt)
    self.hLayout2.addWidget(self.setColEntry)
    
    # resize button
    self.resizeButton = QPushButtonMac()
    self.resizeButton.setText('Resize table')
    self.resizeButton.setToolTip('Resize data table to new dimensions\n(cell contents outside new limits will irretrievably be deleted)')
    self.resizeButton.clicked.connect(self.resizeTable)
    self.resizeButton.setMaximumHeight(int(BASE_SIZE))
    self.resizeButton.setMinimumHeight(int(BASE_SIZE))
    self.vLayout.addWidget(self.resizeButton)

    # blank button
    self.blankButton = QPushButtonMac()
    self.blankButton.setText('Blank table')
    self.blankButton.setToolTip('Create blank data table with new dimensions\n(previous cell contents will irretrievably be deleted)')
    self.blankButton.clicked.connect(self.blankTable)
    self.blankButton.setMaximumHeight(int(BASE_SIZE))
    self.blankButton.setMinimumHeight(int(BASE_SIZE))
    self.vLayout.addWidget(self.blankButton)

    self.setFocus()    
    self.focusNextChild()

  def setDimension(self, entryfield=None, target=None, minval=1, maxval=100):
    # sets number of rows/cols in data table
    if((entryfield != None) and (target != None)):
      # check paramter boundaries
      try:
        value = int(entryfield.text())
        originalvalue = value
      except:
        value = 0
        originalvalue = 1
      value = min(value, maxval)
      value = max(value, minval)
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))

      # update row/column count
      if(target == 'row'):
        self.nrow = value
      else:
        self.ncol = value
          
  def blankTable(self):
    # resets table to empty new table
    self.tableWidget.generateEmptyTable(self.ncol, self.nrow)
    self.close()
    
  def resizeTable(self):
    # resizes table to new dimensions
    self.tableWidget.resizeTable(self.ncol, self.nrow)
    self.close()

class RecentFileMenu(KuhMenu):
  def __init__(self, parent = None):
    super(RecentFileMenu, self).__init__()
    self.parent = parent
    self.aboutToShow.connect(self.updateMe)
    self.installEventFilter(self)

    # set up GUI
    self.buildRessource()

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.recentFilesLabel = QtWidgets.QLabel()
    useFont = self.recentFilesLabel.font()
    useFont.setBold(True)
    self.recentFilesLabel.setFont(useFont)
    self.recentFilesLabel.setText('Recent data files')
    self.vLayout.addWidget(self.recentFilesLabel)

    # set up table view
    self.tableView = QtWidgets.QTableView()
    self.tableView.resizeColumnsToContents()
    self.tableView.horizontalHeader().hide()
    self.tableView.verticalHeader().hide()
    self.tableView.setShowGrid(False)

    self.tableView.clicked.connect(self.clickHandler)
    self.tableView.setSelectionMode(self.tableView.SelectionMode.NoSelection)
    self.tableView.setEditTriggers(self.tableView.EditTrigger.NoEditTriggers)
    self.vLayout.addWidget(self.tableView)
    ###self.tableView.setFocus()
    
  def keyPressEvent(self, event):
    # capture enter key to allow selection by this approach
    if(event.key() in [QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Return]):
      selectionModel = self.tableView.selectionModel()
      if(selectionModel != None):
        index = selectionModel.currentIndex()
        if(index != None):
          # okay, we can call clickHandler now
          self.clickHandler(index=index)
    else:
      # pass through event
      KuhMenu.keyPressEvent(self, event)

  def clickHandler(self, index):
    # callback for click on table
    global RECENTFILES
  
    selectedRow = index.row()
    if(self.parent != None):
      if(selectedRow < len(RECENTFILES)):
        filename, mode = RECENTFILES[selectedRow]
        if(isfile(filename)):
          if(mode == 0):
            self.parent.tableWidget.loadXLS(filename=filename, transpose=self.parent.transposeData)
          elif(mode == 1):
            self.parent.tableWidget.loadODS(filename=filename, transpose=self.parent.transposeData)
          elif(mode == 2):
            self.parent.tableWidget.loadTextFile(filename=filename, delimiter='\t', transpose=self.parent.transposeData)
          elif(mode == 3):
            self.parent.tableWidget.loadTextFile(filename=filename, delimiter=',', transpose=self.parent.transposeData)
          elif(mode == 4):
            self.parent.tableWidget.loadTextFile(filename=filename, delimiter=None, transpose=self.parent.transposeData)
          elif((mode == 5) and PYCORN_PRESENT):
            self.parent.tableWidget.loadUnicornFile(filename=filename, transpose=self.parent.transposeData)
          # store this for use in recent files
          if(mode in [0, 1, 2, 3, 4, 5]):
            # loop through list and remove previous entry
            index = len(RECENTFILES) - 1
            for entry in RECENTFILES[::-1]:
              if(entry[0] == filename):
                RECENTFILES.pop(index)
              index -= 1
            RECENTFILES.insert(0, [filename, mode])
            # truncate to 20 entries
            RECENTFILES = RECENTFILES[:20]
        else:
          self.parent.parent.statusbar.showMessage('Cannot locate data file ' + filename + '!', self.parent.parent.STATUS_TIME)
    
    # close QMenu
    self.close()

  def updateMe(self):
    # apply styles
    if(QSTYLE != None):
      self.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.setStyleSheet(QSTYLESHEET)

    styleString = "QTableView {background-color: rgba" + UI_ALTERNATE_BASE_STRING + ";}\n\
      QTableView {border-style: solid; border-radius: 4px;}"
    if(len(RECENTFILES)):
      styleString += "\nQTableView::item:hover {background-color: #0078d7; color: white; border-style: none; border-radius: 4px;}\n\
        QTableView::item {background-color: transparent; text-align: center; border-radius: 4px;}\n\
        QTableView::item:focus {color: white; background-color: #0078d7; border-style: none; border-radius: 4px;}\n\
        QTableView::item:hover:focus {color: white; background-color: #0078d7; border-style: none; border-radius: 4px;}"
    else:
      styleString += "\nQTableView::item {background-color: transparent; text-align: center; border-radius: 4px; font-style: italic; color: rgba" + UI_BRIGHT_STRING + ";}"
    self.tableView.setStyleSheet(styleString)
      
    # update list
    self.model = QtGui.QStandardItemModel()
    if(len(RECENTFILES)):
      for index, entry in enumerate(RECENTFILES):
        filename = entry[0].split('/')[-1]
        item = QtGui.QStandardItem(str(index + 1) + ' - ' + filename)
        item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignVCenter)
        self.model.setItem(index, item)
    else:
      self.model.setItem(0, QtGui.QStandardItem('<empty>'))
    self.tableView.setModel(self.model)
    
    # now have to resize everything to altered contents
    self.tableView.resizeColumnToContents(0)
    width, height = self.tableView.viewportSizeHint().width(), self.tableView.viewportSizeHint().height()
    # need to add some extra pixels b/c on repeat calls viewportSizeHint() screws up :( 
    # => what an ugly fudge
    self.tableView.setFixedSize(QtCore.QSize(width + int(4 * SCALEFONT), height + int(4 * SCALEFONT)))
    self.adjustSize()

class RecentStateMenu(KuhMenu):
  def __init__(self, parent = None):
    super(RecentStateMenu, self).__init__()
    self.parent = parent
    self.aboutToShow.connect(self.updateMe)
    self.installEventFilter(self)

    # set up GUI
    self.buildRessource()

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

  def showEvent(self, event):
    # show menu above button
    position = self.pos()
    position.setY(int(position.y() - self.height() - int(BASE_SIZE)))
    self.move(position)

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)
      
  def keyPressEvent(self, event):
    # capture enter key to allow selection by this approach
    if(event.key() in [QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Return]):
      selectionModel = self.tableView.selectionModel()
      if(selectionModel != None):
        index = selectionModel.currentIndex()
        if(index != None):
          # okay, we can call clickHandler now
          self.clickHandler(index=index)
    else:
      # pass through event
      KuhMenu.keyPressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.recentFilesLabel = QtWidgets.QLabel()
    useFont = self.recentFilesLabel.font()
    useFont.setBold(True)
    self.recentFilesLabel.setFont(useFont)
    self.recentFilesLabel.setText('Recent state files')
    self.vLayout.addWidget(self.recentFilesLabel)

    # set up table view
    self.tableView = QtWidgets.QTableView()
    self.tableView.resizeColumnsToContents()
    self.tableView.horizontalHeader().hide()
    self.tableView.verticalHeader().hide()
    self.tableView.setShowGrid(False)

    self.tableView.clicked.connect(self.clickHandler)
    self.tableView.setSelectionMode(self.tableView.SelectionMode.NoSelection)
    self.tableView.setEditTriggers(self.tableView.EditTrigger.NoEditTriggers)
    self.vLayout.addWidget(self.tableView)
    ###self.tableView.setFocus()
    
  def clickHandler(self, index):
    # callback for click on table
    selectedRow = index.row()
    if(self.parent != None):
      if(selectedRow < len(RECENTSTATES)):
        filename = RECENTSTATES[selectedRow]
        if(isfile(filename)):
          self.parent.loadState(stateFile=filename, dropEvent=True, appendState=True)
        else:
          self.parent.statusbar.showMessage('Cannot locate state file ' + filename + '!', self.parent.STATUS_TIME)
    
    # close QMenu
    self.close()
  
  def updateMe(self):
    # apply styles
    if(QSTYLE != None):
      self.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.setStyleSheet(QSTYLESHEET)
      
    styleString = "QTableView {background-color: rgba" + UI_ALTERNATE_BASE_STRING + ";}\n\
      QTableView {border-style: solid; border-radius: 4px;}"
    if(len(RECENTSTATES)):
      styleString += "\nQTableView::item:hover {background-color: #0078d7; color: white; border-style: none; border-radius: 4px;}\n\
        QTableView::item {background-color: transparent; text-align: center; border-radius: 4px;}\n\
        QTableView::item:focus {color: white; background-color: #0078d7; border-style: none; border-radius: 4px;}\n\
        QTableView::item:hover:focus {color: white; background-color: #0078d7; border-style: none; border-radius: 4px;}"
    else:
      styleString += "\nQTableView::item {background-color: transparent; text-align: center; border-radius: 4px; font-style: italic; color: rgba" + UI_BRIGHT_STRING + ";}"
    self.tableView.setStyleSheet(styleString)
      
    # update list
    self.model = QtGui.QStandardItemModel()
    if(len(RECENTSTATES)):
      for index, entry in enumerate(RECENTSTATES):
        filename = entry.split('/')[-1]
        item = QtGui.QStandardItem(str(index + 1) + ' - ' + filename)
        item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignVCenter)
        self.model.setItem(index, item)
    else:
      self.model.setItem(0, QtGui.QStandardItem('<empty>'))
    self.tableView.setModel(self.model)
    
    # now have to resize everything to altered contents
    self.tableView.resizeColumnToContents(0)
    width, height = self.tableView.viewportSizeHint().width(), self.tableView.viewportSizeHint().height()
    # need to add some extra pixels b/c on repeat calls viewportSizeHint() screws up :( 
    # => what an ugly fudge
    self.tableView.setFixedSize(QtCore.QSize(width + int(4 * SCALEFONT), height + int(4 * SCALEFONT)))
    self.adjustSize()
  
class DataArea(QWidgetMac):
  def __init__(self, parent = None):
    super(DataArea, self).__init__()
    self.parent = parent
    self.errorSwitch, self.errorXSwitch = True, False
    self.errorModel, self.errorXModel = 0, 1
    self.errorConst, self.errorXConst = 1.0, 1.0
    self.errorPercent, self.errorXPercent = 5.0, 5.0
    self.errorMultiply, self.errorXMultiply = 1.0, 1.0
    self.errorPropagate = True
    self.reductionSwitch, self.reductionModel = False, 1
    self.reductionSkip = 1
    self.reductionAvg = 2
    self.reductionMovAvg = 2
    self.reductionLog = 100
    self.sheetNumber = 1
    self.transposeData = False
    self.importFilter = ''
    self.firstLoad = True

    self.buildRessource()
    self.tableWidget.generateEmptyTable(3, 50)
    
    # set up namespace
    # import numpy again
    import numpy as np
    # import common functions from numpy for ease of access
    from numpy import abs, arccos, arcsin, arctan, exp, cos, cosh, log, log2, log10, power, sin, sinh, sqrt, tan, tanh
    self.mySpace = locals()

  def buildRessource(self):
    # set up GUI
    self.validFloat = MyValidFloat()
    self.validInt = MyValidInt()
    self.validInt.setBottom(1)

    # set up data table
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    
    self.importBox = QWidgetMac()
    self.vLayout.addWidget(self.importBox)
    self.importLayout = QtWidgets.QHBoxLayout(self.importBox)
    self.importLayout.setContentsMargins(0, 0, 0, 0)
    self.importLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    
    self.importButton = QToolButtonMac()
    self.importButton.setText(' Open File')
    if(PYCORN_PRESENT):
      self.importButton.setToolTip('Import Excel, text or unicorn data\n(Ctrl-I)')
    else:
      self.importButton.setToolTip('Import Excel or text data\n(Ctrl-I)')
    self.importButton.clicked.connect(self.loadData)
    self.importButton.setMaximumHeight(int(BASE_SIZE))
    self.importButton.setMinimumSize(int(90 * SCALEFONT), int(BASE_SIZE))
    self.importButton.setIcon(FOM_ICON_OPEN)
    self.importButton.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
    # QToolButton does not heed Alignment -- need to subclass button, argl
    ###self.importButton.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
    self.importLayout.addWidget(self.importButton, stretch=1)
    
    # set up recent file list menu
    self.recentMenu = RecentFileMenu(self)
    self.importButton.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.MenuButtonPopup)
    self.importButton.setMenu(self.recentMenu)

    self.resizeButton = QPushButtonMac()
    self.resizeButton.setText(' Blank/Resize \u25be')
    self.resizeButton.setToolTip('Clear the data sheet and/or alter its dimensions')
    self.resizeButton.clicked.connect(partial(self.blankResizeTable, self.resizeButton))
    self.resizeButton.setMaximumHeight(int(BASE_SIZE))
    self.resizeButton.setMinimumSize(int(90 * SCALEFONT), int(BASE_SIZE))
    self.resizeButton.setIcon(FOM_ICON_FILE)
    self.importLayout.addWidget(self.resizeButton, stretch=1)

    self.killCommaButton = QPushButtonMac()
    self.killCommaButton.setText(' Replace Comma')
    self.killCommaButton.setToolTip('Replace all commata in data sheet by periods')
    self.killCommaButton.clicked.connect(self.killTheComma)
    self.killCommaButton.setMaximumHeight(int(BASE_SIZE))
    self.killCommaButton.setMinimumSize(int(90 * SCALEFONT), int(BASE_SIZE))
    self.killCommaButton.setIcon(FOM_ICON_DIALOG_CLOSE_BUTTON)
    self.importLayout.addWidget(self.killCommaButton, stretch=1)

    self.transposeCheck = QPushButtonCheckable()
    self.transposeCheck.setText(' Transpose')
    self.transposeCheck.setToolTip('Swap columns and rows in data sheet')
    self.transposeCheck.setChecked(False)
    self.transposeCheck.setMaximumHeight(int(BASE_SIZE))
    self.transposeCheck.setMinimumSize(int(90 * SCALEFONT), int(BASE_SIZE))
    self.transposeCheck.clicked.connect(self.dataTransposition)
    self.transposeCheck.setIcon(FOM_ICON_MESSAGE_BOX_QUESTION)
    self.importLayout.addWidget(self.transposeCheck, stretch=1)
    
    self.sheetBox = QWidgetMac()
    self.vLayout.addWidget(self.sheetBox)
    self.sheetLayout = QtWidgets.QHBoxLayout(self.sheetBox)
    self.sheetLayout.setContentsMargins(0, 0, 0, 0)
    self.sheetLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.importSheetLabel = QtWidgets.QLabel('sheet')
    self.importSheetLabel.setMaximumSize(int(28 * SCALEFONT), int(BASE_SIZE))
    self.importSheetLabel.setMinimumSize(int(28 * SCALEFONT), int(BASE_SIZE))
    self.sheetLayout.addWidget(self.importSheetLabel)
    self.importSheetLabel.setEnabled(False)
    
    self.importSheetSpinBox = QSpinClick()
    self.importSheetSpinBox.setToolTip('Select data sheet within Excel file')
    self.importSheetSpinBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
    self.importSheetSpinBox.setMinimum(1)
    self.importSheetSpinBox.setMaximum(self.sheetNumber)
    self.importSheetSpinBox.setValue(1)
    self.importSheetSpinBox.setMinimumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.importSheetSpinBox.setMaximumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.importSheetSpinBox.editingFinished.connect(self.changeSheet)
    self.importSheetSpinBox.setEnabled(False)
    self.importSheetSpinBox.setAccelerated(True)
    self.importSheetSpinBox.setAdaptive(False)
    self.sheetLayout.addWidget(self.importSheetSpinBox)
    
    self.importSheetName = QtWidgets.QLabel()
    self.sheetLayout.addWidget(self.importSheetName)
    self.sheetBox.hide()

    self.tableWidget = DataTable(self)
    self.vLayout.addWidget(self.tableWidget)
    
    # import options
    blah = HLine()
    self.vLayout.addWidget(blah)
    self.dataImportLabel = QtWidgets.QLabel()
    useFont = self.dataImportLabel.font()
    useFont.setBold(True)
    self.dataImportLabel.setFont(useFont)
    self.dataImportLabel.setText('Data Import Options')
    self.vLayout.addWidget(self.dataImportLabel)

    self.outerBox1 = QWidgetMac()
    self.outerBox1.setObjectName('out1')
    self.vLayout.addWidget(self.outerBox1)
    self.Layout_outerBox1 = QtWidgets.QVBoxLayout(self.outerBox1)
    self.Layout_outerBox1.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox1.setSpacing(2)
    self.Layout_outerBox1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    # set up box for x-error specification
    self.errorXSelectorBox = QWidgetMac()
    self.errorXSelectorBox.setContentsMargins(0, 0, 0, 0)
    self.Layout_outerBox1.addWidget(self.errorXSelectorBox)
    self.errorXSelectorLayout = QtWidgets.QHBoxLayout(self.errorXSelectorBox)
    self.errorXSelectorLayout.setContentsMargins(0, 0, 0, 0)
###    self.vLayout.setSpacing(2)
    
    self.errorXSelectorLabel = QPushButtonCheckable()
    self.errorXSelectorLabel.setChecked(self.errorXSwitch)
    self.errorXSelectorLabel.setText('error x')
    self.errorXSelectorLabel.setToolTip('Set error \u0394x for x values')
    self.errorXSelectorLabel.clicked.connect(partial(self.toggleErrorSwitch, 'x'))
    self.errorXSelectorLabel.setMaximumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.errorXSelectorLabel.setMinimumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.errorXSelectorLayout.addWidget(self.errorXSelectorLabel)
    
    self.errorXSelectorGroup = QtWidgets.QGroupBox()
    self.errorXSelectorGroup.setMinimumHeight(int(BASE_SIZE + 4))
    self.errorXSelectorGroup.setMaximumHeight(int(BASE_SIZE + 4))
    self.errorXSelectorLayout.addWidget(self.errorXSelectorGroup)

    self.errorXGroupLayout = QtWidgets.QHBoxLayout()
    self.errorXSelectorGroup.setLayout(self.errorXGroupLayout)
    
    self.errorXSelectorButtons = []
    self.errorXSelectorButtons.append(QRadioButtonToolTip(self.errorXSelectorGroup))
    self.errorXSelectorButtons[-1].setGeometry(QtCore.QRect(int(240 * SCALEFONT), 2, int(42 * SCALEFONT), int(BASE_SIZE)))
    self.errorXSelectorButtons[-1].setChecked(False)
    self.errorXSelectorButtons[-1].toggled.connect(partial(self.toggleErrorXModel, 0))
    self.errorXSelectorButtons[-1].setText('\u0394x \u00D7')
    self.errorXSelectorButtons[-1].setToolTip('Use assigned \u0394x error column')
    self.errorXDeltaEntry = QLineEditClick(self.errorXSelectorGroup)
    self.errorXDeltaEntry.setGeometry(QtCore.QRect(int(288 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.errorXDeltaEntry.setText(str(self.errorXMultiply))
    self.errorXDeltaEntry.setToolTip('Multiply assigned \u0394x by factor')
    self.errorXDeltaEntry.setValidator(self.validFloat)
    self.errorXDeltaEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorXDeltaEntry, 'errorXMultiply'))
    self.errorXDeltaEntry.focusOutEvent = partial(self.lostFocus, self.errorXDeltaEntry, 'errorXMultiply', self.errorXDeltaEntry.focusOutEvent)
    self.errorXDeltaEntry.focusInEvent = partial(self.gainFocus, self.errorXSelectorButtons[-1], self.errorXDeltaEntry.focusInEvent)
    
    self.errorXSelectorButtons.append(QRadioButtonToolTip(self.errorXSelectorGroup))
    self.errorXSelectorButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), 2, int(44 * SCALEFONT), int(BASE_SIZE)))
    self.errorXSelectorButtons[-1].setChecked(True)
    self.errorXSelectorButtons[-1].toggled.connect(partial(self.toggleErrorXModel, 1))
    self.errorXSelectorButtons[-1].setText('const')
    self.errorXSelectorButtons[-1].setToolTip('Use constant \u0394x error')
    self.errorXConstEntry = QLineEditClick(self.errorXSelectorGroup)
    self.errorXConstEntry.setGeometry(QtCore.QRect(int(56 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.errorXConstEntry.setText(str(self.errorXConst))
    self.errorXConstEntry.setToolTip('Use constant \u0394x error')
    self.errorXConstEntry.setValidator(self.validFloat)
    self.errorXConstEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorXConstEntry, 'errorXConst'))
    self.errorXConstEntry.focusOutEvent = partial(self.lostFocus, self.errorXConstEntry, 'errorXConst', self.errorXConstEntry.focusOutEvent)
    self.errorXConstEntry.focusInEvent = partial(self.gainFocus, self.errorXSelectorButtons[-1], self.errorXConstEntry.focusInEvent)
    
    self.errorXSelectorButtons.append(QRadioButtonToolTip(self.errorXSelectorGroup))
    self.errorXSelectorButtons[-1].setGeometry(QtCore.QRect(int(126 * SCALEFONT), 2, int(42 * SCALEFONT), int(BASE_SIZE)))
    self.errorXSelectorButtons[-1].setChecked(False)
    self.errorXSelectorButtons[-1].toggled.connect(partial(self.toggleErrorXModel, 2))
    self.errorXSelectorButtons[-1].setText('prop')
    self.errorXSelectorButtons[-1].setToolTip('Use \u0394x error proportional to x value')
    self.errorXPercentEntry = QLineEditClick(self.errorXSelectorGroup)
    self.errorXPercentEntry.setGeometry(QtCore.QRect(int(172 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.errorXPercentEntry.setText(str(self.errorXPercent))
    self.errorXPercentEntry.setToolTip('Use \u0394x error proportional to x value')
    self.errorXPercentEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorXPercentEntry, 'errorXPercent'))
    self.errorXPercentEntry.focusOutEvent = partial(self.lostFocus, self.errorXPercentEntry, 'errorXPercent', self.errorXPercentEntry.focusOutEvent)
    self.errorXPercentEntry.focusInEvent = partial(self.gainFocus, self.errorXSelectorButtons[-1], self.errorXPercentEntry.focusInEvent)
    self.errorXPercentEntry.setValidator(self.validFloat)
    self.errorXPercentLabel = QtWidgets.QLabel(self.errorXSelectorGroup)
    self.errorXPercentLabel.setGeometry(QtCore.QRect(int(216 * SCALEFONT), 2, int(18 * SCALEFONT), int(BASE_SIZE)))
    self.errorXPercentLabel.setText('%')

    # set up controls for y error
    self.errorSelectorBox = QWidgetMac()
    self.errorSelectorBox.setContentsMargins(0, 0, 0, 0)
    self.Layout_outerBox1.addWidget(self.errorSelectorBox)
    self.errorSelectorLayout = QtWidgets.QHBoxLayout(self.errorSelectorBox)
    self.errorSelectorLayout.setContentsMargins(0, 0, 0, 0)
    
    self.errorSelectorLabel = QPushButtonCheckable()
    self.errorSelectorLabel.setChecked(self.errorSwitch)
    self.errorSelectorLabel.setText('error y')
    self.errorSelectorLabel.setToolTip('Set error \u0394y for y values')
    self.errorSelectorLabel.clicked.connect(partial(self.toggleErrorSwitch, 'y'))
    self.errorSelectorLabel.setMaximumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.errorSelectorLabel.setMinimumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.errorSelectorLayout.addWidget(self.errorSelectorLabel)

    self.errorSelectorGroup = QtWidgets.QGroupBox()
    self.errorSelectorGroup.setMinimumHeight(int(BASE_SIZE + 4))
    self.errorSelectorGroup.setMaximumHeight(int(BASE_SIZE + 4))
    self.errorSelectorLayout.addWidget(self.errorSelectorGroup)

    self.errorGroupLayout = QtWidgets.QHBoxLayout()
    self.errorSelectorGroup.setLayout(self.errorGroupLayout)
    
    self.errorSelectorButtons = []
    self.errorSelectorButtons.append(QRadioButtonToolTip(self.errorSelectorGroup))
    self.errorSelectorButtons[-1].setGeometry(QtCore.QRect(int(240 * SCALEFONT), 2, int(42 * SCALEFONT), int(BASE_SIZE)))
    self.errorSelectorButtons[-1].setChecked(True)
    self.errorSelectorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 0))
    self.errorSelectorButtons[-1].setText('\u0394y \u00D7')
    self.errorSelectorButtons[-1].setToolTip('Use assigned \u0394y error column')
    self.errorDeltaEntry = QLineEditClick(self.errorSelectorGroup)
    self.errorDeltaEntry.setGeometry(QtCore.QRect(int(288 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.errorDeltaEntry.setText(str(self.errorMultiply))
    self.errorDeltaEntry.setToolTip('Multiply assigned \u0394y by factor')
    self.errorDeltaEntry.setValidator(self.validFloat)
    self.errorDeltaEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorDeltaEntry, 'errorMultiply'))
    self.errorDeltaEntry.focusOutEvent = partial(self.lostFocus, self.errorDeltaEntry, 'errorMultiply', self.errorDeltaEntry.focusOutEvent)
    self.errorDeltaEntry.focusInEvent = partial(self.gainFocus, self.errorSelectorButtons[-1], self.errorDeltaEntry.focusInEvent)
    
    self.errorSelectorButtons.append(QRadioButtonToolTip(self.errorSelectorGroup))
    self.errorSelectorButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), 2, int(44 * SCALEFONT), int(BASE_SIZE)))
    self.errorSelectorButtons[-1].setChecked(False)
    self.errorSelectorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 1))
    self.errorSelectorButtons[-1].setText('const')
    self.errorSelectorButtons[-1].setToolTip('Use constant \u0394y error')
    self.errorConstEntry = QLineEditClick(self.errorSelectorGroup)
    self.errorConstEntry.setGeometry(QtCore.QRect(int(56 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.errorConstEntry.setText(str(self.errorConst))
    self.errorConstEntry.setToolTip('Use constant \u0394y error')
    self.errorConstEntry.setValidator(self.validFloat)
    self.errorConstEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorConstEntry, 'errorConst'))
    self.errorConstEntry.focusOutEvent = partial(self.lostFocus, self.errorConstEntry, 'errorConst', self.errorConstEntry.focusOutEvent)
    self.errorConstEntry.focusInEvent = partial(self.gainFocus, self.errorSelectorButtons[-1], self.errorConstEntry.focusInEvent)
    
    self.errorSelectorButtons.append(QRadioButtonToolTip(self.errorSelectorGroup))
    self.errorSelectorButtons[-1].setGeometry(QtCore.QRect(int(126 * SCALEFONT), 2, int(42 * SCALEFONT), int(BASE_SIZE)))
    self.errorSelectorButtons[-1].setChecked(False)
    self.errorSelectorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 2))
    self.errorSelectorButtons[-1].setText('prop')
    self.errorSelectorButtons[-1].setToolTip('Use \u0394y error proportional to y value')
    self.errorPercentEntry = QLineEditClick(self.errorSelectorGroup)
    self.errorPercentEntry.setGeometry(QtCore.QRect(int(172 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.errorPercentEntry.setText(str(self.errorPercent))
    self.errorPercentEntry.setToolTip('Use \u0394y error proportional to y value')
    self.errorPercentEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorPercentEntry, 'errorPercent'))
    self.errorPercentEntry.focusOutEvent = partial(self.lostFocus, self.errorPercentEntry, 'errorPercent', self.errorPercentEntry.focusOutEvent)
    self.errorPercentEntry.focusInEvent = partial(self.gainFocus, self.errorSelectorButtons[-1], self.errorPercentEntry.focusInEvent)
    self.errorPercentEntry.setValidator(self.validFloat)
    self.errorPercentLabel = QtWidgets.QLabel(self.errorSelectorGroup)
    self.errorPercentLabel.setGeometry(QtCore.QRect(int(216 * SCALEFONT), 2, int(18 * SCALEFONT), int(BASE_SIZE)))
    self.errorPercentLabel.setText('%')
    
    # set up controls for error propagation
    self.errorPropagateBox = QWidgetMac()
    self.errorPropagateBox.setContentsMargins(0, 0, 0, 0)
    self.Layout_outerBox1.addWidget(self.errorPropagateBox)
    self.errorPropagateLayout = QtWidgets.QHBoxLayout(self.errorPropagateBox)
    self.errorPropagateLayout.setContentsMargins(0, 0, 0, 0)
    
    self.errorPropagateCheck = QPushButtonCheckable()
    self.errorPropagateCheck.setToolTip('Propagate data errors through transformation')
    self.errorPropagateCheck.setMaximumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.errorPropagateCheck.setMinimumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.errorPropagateCheck.setChecked(self.errorPropagate)
    self.errorPropagateCheck.setText('propagate?')
    self.errorPropagateCheck.clicked.connect(self.toggleErrorPropagation)
    self.errorPropagateLayout.addWidget(self.errorPropagateCheck)
    self.errorPropagateLayout.addStretch()
    ###self.vLayout.addWidget(HLine())

    self.outerBox2 = QWidgetMac()
    self.outerBox2.setObjectName('out1')
    self.vLayout.addWidget(self.outerBox2)
    self.Layout_outerBox2 = QtWidgets.QVBoxLayout(self.outerBox2)
    self.Layout_outerBox2.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox2.setSpacing(2)
    # self.Layout_outerBox2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    # set up box for data reduction
    self.dataReductionBox = QWidgetMac()
    self.dataReductionBox.setContentsMargins(0, 0, 0, 0)
    self.Layout_outerBox2.addWidget(self.dataReductionBox)
    self.dataReductionLayout = QtWidgets.QHBoxLayout(self.dataReductionBox)
    self.dataReductionLayout.setContentsMargins(0, 0, 0, 0)
    
    self.dataReductionLabel = QPushButtonCheckable()
    self.dataReductionLabel.setChecked(self.reductionSwitch)
    self.dataReductionLabel.setText('reduce')
    self.dataReductionLabel.setToolTip('Reduction of data upon import')
    self.dataReductionLabel.clicked.connect(self.toggleReductionSwitch)
    self.dataReductionLabel.setMaximumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.dataReductionLabel.setMinimumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.dataReductionLayout.addWidget(self.dataReductionLabel)
    
    self.dataReductionGroup = QtWidgets.QGroupBox()
    self.dataReductionGroup.setMinimumHeight(int(2 * BASE_SIZE + 8))
    self.dataReductionGroup.setMaximumHeight(int(2 * BASE_SIZE + 8))
    self.dataReductionLayout.addWidget(self.dataReductionGroup)
    
    self.dataReductionButtons = []
    self.dataReductionButtons.append(QRadioButtonToolTip(self.dataReductionGroup))
    self.dataReductionButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.dataReductionButtons[-1].setChecked(True)
    self.dataReductionButtons[-1].toggled.connect(partial(self.toggleDataReduction, 1))
    self.dataReductionButtons[-1].setText('skip')
    self.dataReductionButtons[-1].setToolTip('Skip n data points')

    self.dataSkipEntry = QLineEditClick(self.dataReductionGroup)
    self.dataSkipEntry.setGeometry(QtCore.QRect(int(56 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.dataSkipEntry.setText(str(self.reductionSkip))
    self.dataSkipEntry.setToolTip('Skip n data points')
    self.dataSkipEntry.editingFinished.connect(partial(self.validateReductionEntry, self.dataSkipEntry, 'reductionSkip'))
    self.dataSkipEntry.focusOutEvent = partial(self.lostFocusInt, self.dataSkipEntry, 'reductionSkip', self.dataSkipEntry.focusOutEvent)
    self.dataSkipEntry.focusInEvent = partial(self.gainFocus, self.dataReductionButtons[-1], self.dataSkipEntry.focusInEvent)
    self.dataSkipEntry.setValidator(self.validInt)
    
    self.dataSkipLabel = QtWidgets.QLabel(self.dataReductionGroup)
    self.dataSkipLabel.setGeometry(QtCore.QRect(int(100 * SCALEFONT), 2, int(18 * SCALEFONT), int(BASE_SIZE)))
    self.dataSkipLabel.setText('pts')

    self.dataReductionButtons.append(QRadioButtonToolTip(self.dataReductionGroup))
    self.dataReductionButtons[-1].setGeometry(QtCore.QRect(int(126 * SCALEFONT), 2, int(36 * SCALEFONT), int(BASE_SIZE)))
    self.dataReductionButtons[-1].setChecked(False)
    self.dataReductionButtons[-1].toggled.connect(partial(self.toggleDataReduction, 2))
    self.dataReductionButtons[-1].setText('avg')
    self.dataReductionButtons[-1].setToolTip('Average n data points')

    self.dataAvgEntry = QLineEditClick(self.dataReductionGroup)
    self.dataAvgEntry.setGeometry(QtCore.QRect(int(172 * SCALEFONT), 2, int(40 * SCALEFONT), int(BASE_SIZE)))
    self.dataAvgEntry.setText(str(self.reductionAvg))
    self.dataAvgEntry.setToolTip('Average n data points')
    self.dataAvgEntry.editingFinished.connect(partial(self.validateReductionEntry, self.dataAvgEntry, 'reductionAvg'))
    self.dataAvgEntry.focusOutEvent = partial(self.lostFocusInt, self.dataAvgEntry, 'reductionAvg', self.dataAvgEntry.focusOutEvent)
    self.dataAvgEntry.focusInEvent = partial(self.gainFocus, self.dataReductionButtons[-1], self.dataAvgEntry.focusInEvent)
    self.dataAvgEntry.setValidator(self.validInt)
    
    self.dataAvgLabel = QtWidgets.QLabel(self.dataReductionGroup)
    self.dataAvgLabel.setGeometry(QtCore.QRect(int(216 * SCALEFONT), 2, int(15 * SCALEFONT), int(BASE_SIZE)))
    self.dataAvgLabel.setText('pts')

    self.dataReductionButtons.append(QRadioButtonToolTip(self.dataReductionGroup))
    self.dataReductionButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), int(BASE_SIZE + 4), int(50 * SCALEFONT), int(BASE_SIZE)))
    self.dataReductionButtons[-1].setChecked(False)
    self.dataReductionButtons[-1].toggled.connect(partial(self.toggleDataReduction, 3))
    self.dataReductionButtons[-1].setText('mvavg')
    self.dataReductionButtons[-1].setToolTip('Moving average of n data points')

    self.dataMovAvgEntry = QLineEditClick(self.dataReductionGroup)
    self.dataMovAvgEntry.setGeometry(QtCore.QRect(int(56 * SCALEFONT), int(BASE_SIZE + 4), int(40 * SCALEFONT), int(BASE_SIZE)))
    self.dataMovAvgEntry.setText(str(self.reductionMovAvg))
    self.dataMovAvgEntry.setToolTip('Moving average of n data points')
    self.dataMovAvgEntry.editingFinished.connect(partial(self.validateReductionEntry, self.dataMovAvgEntry, 'reductionMovAvg'))
    self.dataMovAvgEntry.focusOutEvent = partial(self.lostFocusInt, self.dataMovAvgEntry, 'reductionMovAvg', self.dataMovAvgEntry.focusOutEvent)
    self.dataMovAvgEntry.focusInEvent = partial(self.gainFocus, self.dataReductionButtons[-1], self.dataMovAvgEntry.focusInEvent)
    self.dataMovAvgEntry.setValidator(self.validInt)
    
    self.dataMovAvgLabel = QtWidgets.QLabel(self.dataReductionGroup)
    self.dataMovAvgLabel.setGeometry(QtCore.QRect(int(100 * SCALEFONT), int(BASE_SIZE + 4), int(18 * SCALEFONT), int(BASE_SIZE)))
    self.dataMovAvgLabel.setText('pts')

    self.dataReductionButtons.append(QRadioButtonToolTip(self.dataReductionGroup))
    self.dataReductionButtons[-1].setGeometry(QtCore.QRect(int(126 * SCALEFONT), int(BASE_SIZE + 4), int(36 * SCALEFONT), int(BASE_SIZE)))
    self.dataReductionButtons[-1].setChecked(False)
    self.dataReductionButtons[-1].toggled.connect(partial(self.toggleDataReduction, 4))
    self.dataReductionButtons[-1].setText('log')
    self.dataReductionButtons[-1].setToolTip('Logarithmically reduce data to ~ n data points')

    self.dataLogEntry = QLineEditClick(self.dataReductionGroup)
    self.dataLogEntry.setGeometry(QtCore.QRect(int(172 * SCALEFONT), int(BASE_SIZE + 4), int(40 * SCALEFONT), int(BASE_SIZE)))
    self.dataLogEntry.setText(str(self.reductionLog))
    self.dataLogEntry.setToolTip('Logarithmically reduce data to ~ n data points')
    self.dataLogEntry.editingFinished.connect(partial(self.validateReductionEntry, self.dataLogEntry, 'reductionLog'))
    self.dataLogEntry.focusOutEvent = partial(self.lostFocusInt, self.dataLogEntry, 'reductionLog', self.dataLogEntry.focusOutEvent)
    self.dataLogEntry.focusInEvent = partial(self.gainFocus, self.dataReductionButtons[-1], self.dataLogEntry.focusInEvent)
    self.dataLogEntry.setValidator(self.validInt)

    self.dataLogLabel = QtWidgets.QLabel(self.dataReductionGroup)
    self.dataLogLabel.setGeometry(QtCore.QRect(int(216 * SCALEFONT), int(BASE_SIZE + 4), int(42 * SCALEFONT), int(BASE_SIZE)))
    self.dataLogLabel.setText('pts (ca.)')
    ###self.vLayout.addWidget(HLine())

    self.outerBox3 = QWidgetMac()
    self.outerBox3.setObjectName('out1')
    self.vLayout.addWidget(self.outerBox3)
    self.Layout_outerBox3 = QtWidgets.QVBoxLayout(self.outerBox3)
    self.Layout_outerBox3.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox3.setSpacing(4)
    self.Layout_outerBox3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    # set up box for data transform
    self.dataTransformBox = QWidgetMac()
    self.dataTransformBox.setContentsMargins(0, 0, 0, 0)
    self.Layout_outerBox3.addWidget(self.dataTransformBox)
    self.dataTransformLayout = QtWidgets.QHBoxLayout(self.dataTransformBox)
    self.dataTransformLayout.setContentsMargins(0, 0, 0, 0)
    
    self.dataTransformXCheck = QPushButtonCheckable()
    self.dataTransformXCheck.setMaximumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformXCheck.setMinimumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformXCheck.setChecked(False)
    self.dataTransformXCheck.setText('transform x')
    self.dataTransformLayout.addWidget(self.dataTransformXCheck)
    self.dataTransformXCheck.setToolTip('Transformation of x values upon import')

    self.dataTransformLabel = QtWidgets.QLabel('x = ')
    self.dataTransformLabel.setMaximumSize(int(20 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformLabel.setMinimumSize(int(20 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformLayout.addWidget(self.dataTransformLabel)

    self.dataTransformXEntry = QLineEditClick()
    self.dataTransformXEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.dataTransformXEntry.setMaximumHeight(int(BASE_SIZE))
    self.dataTransformXEntry.setMinimumSize(int(300 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformXEntry.setText('x')
    self.dataTransformXEntry.setToolTip('Formula for transforming x values')
    self.dataTransformXEntry.textChanged.connect(partial(self.dataTransformXCheck.setChecked, True))
    self.dataTransformLayout.addWidget(self.dataTransformXEntry)

    self.dataTransformYBox = QWidgetMac()
    self.dataTransformYBox.setContentsMargins(0, 0, 0, 0)
    self.Layout_outerBox3.addWidget(self.dataTransformYBox)
    self.dataTransformYLayout = QtWidgets.QHBoxLayout(self.dataTransformYBox)
    self.dataTransformYLayout.setContentsMargins(0, 0, 0, 0)

    self.dataTransformYCheck = QPushButtonCheckable()
    self.dataTransformYCheck.setMaximumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformYCheck.setMinimumSize(int(64 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformYCheck.setChecked(False)
    self.dataTransformYCheck.setText('transform y')
    self.dataTransformYLayout.addWidget(self.dataTransformYCheck)
    self.dataTransformYCheck.setToolTip('Transformation of y values upon import')

    self.dataTransformYLabel = QtWidgets.QLabel('y = ')
    self.dataTransformYLabel.setMaximumSize(int(20 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformYLabel.setMinimumSize(int(20 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformYLayout.addWidget(self.dataTransformYLabel)

    self.dataTransformYEntry = QLineEditClick()
    self.dataTransformYEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.dataTransformYEntry.setMaximumHeight(int(BASE_SIZE))
    self.dataTransformYEntry.setMinimumSize(int(300 * SCALEFONT), int(BASE_SIZE))
    self.dataTransformYEntry.setText('y')
    self.dataTransformYEntry.setToolTip('Formula for transforming y values')
    self.dataTransformYEntry.textChanged.connect(partial(self.dataTransformYCheck.setChecked, True))
    self.dataTransformYLayout.addWidget(self.dataTransformYEntry)

    # set up data import controls
    blah = HLine()
    ###self.vLayout.addWidget(blah)
    
    self.refreshBox = QWidgetMac()
    self.refreshBox.setContentsMargins(0, 0, 0, 0)
    self.vLayout.addWidget(self.refreshBox)
    self.refreshLayout = QtWidgets.QHBoxLayout(self.refreshBox)
    self.refreshLayout.setContentsMargins(0, 0, 0, 0)
    self.refreshButton = QPushButtonMac()
    self.refreshButton.setText(' Import Data')
    self.refreshButton.setToolTip('Import the currently selected cells in the data table')
    self.refreshButton.setMaximumHeight(int(BASE_SIZE))
    self.refreshButton.setMinimumHeight(int(BASE_SIZE))
    self.refreshButton.clicked.connect(partial(self.updateData, False, True, False, False, False))
    self.refreshButton.setIcon(FOM_ICON_MEDIA_PLAY)
    self.refreshLayout.addWidget(self.refreshButton, stretch=2)

    self.refreshCreateButton = QPushButtonMac()
    self.refreshCreateButton.setText(' Import && Create')
    self.refreshCreateButton.setToolTip('Generate a new data object and import the currently selected cells in the data table')
    self.refreshCreateButton.setMaximumHeight(int(BASE_SIZE))
    self.refreshCreateButton.setMinimumHeight(int(BASE_SIZE))
    self.refreshCreateButton.clicked.connect(partial(self.updateData, False, True, False, True, False))
    self.refreshCreateButton.setIcon(FOM_ICON_MEDIA_PLAY)
    self.refreshLayout.addWidget(self.refreshCreateButton, stretch=1)

    self.refreshAddButton = QPushButtonMac()
    self.refreshAddButton.setText(' Import && Add')
    self.refreshAddButton.setToolTip('Import the currently selected cells in the data table and add to current data object')
    self.refreshAddButton.setMaximumHeight(int(BASE_SIZE))
    self.refreshAddButton.setMinimumHeight(int(BASE_SIZE))
    self.refreshAddButton.clicked.connect(partial(self.updateData, False, True, False, False, True))
    self.refreshAddButton.setIcon(FOM_ICON_MEDIA_PLAY)
    self.refreshLayout.addWidget(self.refreshAddButton, stretch=1)

    self.dataSeriesButton = QPushButtonMac()
    self.dataSeriesButton.setText(' Import Series')
    self.dataSeriesButton.setToolTip('Import y column and all data columns to its right')
    self.dataSeriesButton.setMaximumHeight(int(BASE_SIZE))
    self.dataSeriesButton.setMinimumHeight(int(BASE_SIZE))
    self.dataSeriesButton.clicked.connect(self.importDataSeries)
    self.dataSeriesButton.setIcon(FOM_ICON_MEDIA_SEEK_FORWARD)
    self.refreshLayout.addWidget(self.dataSeriesButton, stretch=1)
    
    self.refreshCheck = QPushButtonCheckable()
    self.refreshCheck.setText('Auto Import?')
    self.refreshCheck.setChecked(False)
    self.refreshCheck.clicked.connect(partial(self.updateData, True, True, False, False, False))
    #self.refreshLayout.addWidget(self.refreshCheck)

  def reportState(self):
    # reports data content for saveState function
    retv = self.tableWidget.tableModel.getAllData()
    return retv

  def restoreState(self, data):
    # restores data content from loadState function
    success = False
    try:
      tableData = literal_eval(data[0])
      success = True
    except:
      # literal_eval fails in rare cases, e.g., when encountering datetime objects (thank you, openpyxl ...)
      # try eval() -- yes, it's unsafe but as we exec() fit functions elsewhere, it does not really matter
      try:
        tableData = eval(data[0])
        success = True
      except:
        print('Failed to restore data table ' + data[0])
    
    if(success):
      self.resetSheetSpinBox(currVal=1, maxVal=1, currName='')
      self.transposeCheck.setChecked(False)
      self.tableWidget.restoreTable(tableData=tableData)

  def killTheComma(self):
    # scour data table for commata and replace them
    self.tableWidget.killTheComma()

  def dataTransposition(self):
    # set data transposition flag
    self.transposeData = self.transposeCheck.isChecked()
    # trigger data transposition
    self.tableWidget.transposeTable()

  def resetSheetSpinBox(self, currVal=1, maxVal=1, currName='', silent=False):
    # updates import spin box
    self.importSheetSpinBox.setMaximum(maxVal)
    self.sheetNumber = currVal
    if(silent):
      # prevent autoupdating of sheet contents when called from loadXLS
      self.importSheetSpinBox.blockSignals(True)
      self.importSheetSpinBox.setValue(self.sheetNumber)
      self.importSheetSpinBox.blockSignals(False)
    else:
      self.importSheetSpinBox.setValue(self.sheetNumber)
    
    if(maxVal > 1):
      self.sheetBox.show()
      self.importSheetSpinBox.setEnabled(True)
      self.importSheetLabel.setEnabled(True)
      self.importSheetName.setText(currName)
    else:
      self.importSheetSpinBox.setEnabled(False)
      self.importSheetLabel.setEnabled(False)
      self.importSheetName.setText('')
      self.sheetBox.hide()

  def changeSheet(self):
    # change current sheet in Excel files with several sheets
    self.sheetNumber = self.importSheetSpinBox.value()
    self.tableWidget.changeSheet(self.sheetNumber, transpose=self.transposeData)
    self.importSheetSpinBox.selectAll()

  def gainFocus(self, toggleOption=None, defaultHandler=None, event=None):
    # entry field gained focus
    # select corresponding option
    if(toggleOption != None):
      toggleOption.setChecked(True)

    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)

  def toggleDataReduction(self, mode=0):
    # change error model
    self.reductionModel = mode
    # check switch button
    self.dataReductionLabel.setChecked(True)
    self.reductionSwitch = True

  def toggleReductionSwitch(self):
    # do data reduction?
    value = self.dataReductionLabel.isChecked()
    self.reductionSwitch = value

  def toggleErrorModel(self, mode=0):
    # change error model
    self.errorModel = mode
    # check switch button
    self.errorSelectorLabel.setChecked(True)
    self.errorSwitch = True

  def toggleErrorXModel(self, mode=0):
    # change error model
    self.errorXModel = mode
    # check switch button
    self.errorXSelectorLabel.setChecked(True)
    self.errorXSwitch = True

  def toggleErrorSwitch(self, axis='y'):
    # use errors?
    if(axis in ['x', 'y']):
      if(axis == 'y'):
        value = self.errorSelectorLabel.isChecked()
        self.errorSwitch = value
      else:
        value = self.errorXSelectorLabel.isChecked()
        self.errorXSwitch = value

  def toggleErrorPropagation(self):
    # toggles error propagation
    self.errorPropagate = self.errorPropagateCheck.isChecked()

  def lostFocusInt(self, entryobject=None, quantity=None, defaultHandler=None, event=None):
    # entry field lost focus, perform sanity check
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        self.__dict__[quantity] = int(entrytext)
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))
    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)
    
  def validateReductionEntry(self, entryobject=None, quantity=None):
    # validates entryfield
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        newnumber = int(entrytext)
        self.__dict__[quantity] = np.abs(newnumber)
        if(newnumber < 0):
          entryobject.setText(str(np.abs(newnumber)))
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))

  def lostFocus(self, entryobject=None, quantity=None, defaultHandler=None, event=None):
    # entry field lost focus, perform sanity check
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        self.__dict__[quantity] = float(entrytext)
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))
    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)
    
  def validateErrorEntry(self, entryobject=None, quantity=None):
    # validates entryfield
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        newnumber = float(entrytext)
        self.__dict__[quantity] = np.abs(newnumber)
        if(newnumber < 0):
          entryobject.setText(str(np.abs(newnumber)))
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))

  def importDataSeries(self):
    # greedy import of data
    cycleColors = self.parent.objectsarea.cycleColors
    # check whether any data has been loaded
    if((self.tableWidget.tableModel == None) or (self.tableWidget.tableModel.rowCount() == 0)):
      self.parent.statusbar.showMessage('Open a data file first!', self.parent.STATUS_TIME)
    else:
      # can do this by repeatedly calling updateData
      roles = self.tableWidget.roles
      columnCount = self.tableWidget.tableModel.columnCount()
      
      # check for presence of x and y
      if((not 'x' in roles) or (not 'y' in roles)):
        self.parent.statusbar.showMessage('Assign x and y columns!', self.parent.STATUS_TIME)
      else:
        # remember original y column
        originalY = roles['y']
        
        # change dataset style to line and no symbol
        style = self.parent.data[self.parent.activeData].getStyle()
        if(style['linestyle'] == 'None'):
          self.parent.data[self.parent.activeData].setStyle('linestyle', 'solid', redraw=False)
        if(style['marker'] != 'None'):
          self.parent.data[self.parent.activeData].setStyle('marker', 'None', redraw=False)
        # at least temporarily set dataset visibility to True to speed up import (this obviates a call to
        # setVisibility in updateData())
        if(self.parent.data[self.parent.activeData].visibility):
          invisible = False
        else:
          invisible, originalDataset = True, self.parent.activeData
          self.parent.data[self.parent.activeData].setVisibility(True, redraw=False)
        
        # loop over columns
        successes = 0
        currY = originalY
        while((currY < columnCount) and (successes < 100)):
          # check whether current column is already assigned to sth. else
          if((currY == originalY) or (not (currY in list(roles.values())))):
            # update status message as this can take a while
            self.parent.statusbar.showMessage('Now processing column ' + str(currY + 1) + '!', self.parent.STATUS_TIME, color='blue')
            # set current color
            currColor = (currY - originalY) % len(cycleColors)
            self.parent.data[self.parent.activeData].setStyle('color', cycleColors[currColor], redraw=False)
            # assign new y column
            self.tableWidget.roles['y'] = currY
            # get the new data
            if((currY + 1 == columnCount) or (successes == 99)):
              # need to update togglo sidebar before issuing updateData() in regular mode
              self.parent.plotArea.updateToggloContainer()
              # this is the last item to import => updateData() must behave differently
              axis_labels = self.updateData(docheck=False, redraw=False, quiet=True, create=False, add_data=False, series=False)
            else:
              axis_labels = self.updateData(docheck=False, redraw=False, quiet=True, create=False, add_data=False, series=True)
            # check the new data
            nuData = self.parent.data[self.parent.activeData].value()
            if(('x' in nuData) and (len(nuData['x']))):
              # assign name to data set
              if((len(axis_labels) > 1) and len(axis_labels[1])):
                self.parent.data[-1].setName(axis_labels[1])
              else:
                self.parent.data[-1].setName('Data_' + str(len(self.parent.data)))
              # keep track of successes
              successes += 1
              if((currY + 1 < columnCount) and (successes < 100)):
                # generate a new data set (if needed)
                self.parent.data.append(DataObject(self.parent))
                # need to copy contents of original object
                self.parent.data[-1].spawned(self.parent.data[self.parent.activeData])
                # reinit values (as they were copied by spawned)
                self.parent.data[-1].initValues()
                # set new data object as active
                self.parent.activeData = (len(self.parent.data) - 1)
                # need to update dataset table, otherwise updateData() will fail
                self.parent.objectsarea.refreshDataTable()
          currY += 1

        # restore original roles
        self.tableWidget.roles['y'] = originalY
        
        # was original data set invisible?
        if(invisible):
          self.parent.data[originalDataset].setVisibility(False, redraw=False)
        # for safe measure, always update legend -- should be fine with no redraw issued
        self.updateLegend(redraw=False)
        
        # update objects area
        self.parent.objectsarea.refreshDataTable()
        self.parent.objectsarea.refreshResidTable()
        self.parent.objectsarea.refreshCurvesTable()
        self.parent.objectsarea.refreshExtrasTable()
        self.parent.globalarea.updateDataSetTable()
        # for good measure, issue another update of togglo sidebar
        self.parent.plotArea.updateToggloContainer()
        
        # update results table
        self.parent.resultsarea.setDataSet(currIndex=len(self.parent.data) - 1, maxIndex=len(self.parent.data) - 1)

        # issue refresh of plots
        self.parent.plotArea.dataplotwidget.myRefresh()
        # do we need this call? (theoretically, original dataset [with residuals] could be thus reset -- so, likely needed)
        self.parent.plotArea.residplotwidget.myRefresh()

  def updateData(self, docheck=False, redraw=True, quiet=False, create=False, add_data=False, series=False):
    # initialize return value for importDataSeries
    axis_labels = ['', '']
    # check whether autoimport enabled
    if ((not docheck) or (self.refreshCheck.isChecked())):
      # check whether any data has been loaded
      if((self.tableWidget.tableModel == None) or (self.tableWidget.tableModel.rowCount() == 0)):
        self.parent.statusbar.showMessage('Open a data file first!', self.parent.STATUS_TIME)
      else:
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
        QtCore.QCoreApplication.processEvents()

        # if selected dataset exists, clear it
        if(self.parent.selectedData.isLive):
          self.parent.selectedData.clearMe(redraw=False)
        
        # check whether we need to retrieve x-err and y-err columns at all
        needXErr, needYErr = self.errorXSwitch and (not self.errorXModel), self.errorSwitch and (not self.errorModel)
        # get and process data
        useLabelFlag = False
        new_data, roles, axis_labels = self.tableWidget.getData(needXErr=needXErr, needYErr=needYErr)
        if(needXErr and ('xerr' in roles)):
          # apply factor to x errors
          errXCol = roles.index('xerr')
          new_data = [[k if (j != errXCol) else (k * self.errorXMultiply) for j, k in enumerate(i)] for i in new_data]
        if(needYErr and ('yerr' in roles)):
          # apply factor to x errors
          errYCol = roles.index('yerr')
          new_data = [[k if (j != errYCol) else (k * self.errorMultiply) for j, k in enumerate(i)] for i in new_data]
        labels, labels2 = [], []
        labelCols = []
        if('labels' in roles):
          # separate numerical data from labels
          labelCols.append(roles.index('labels'))
          labels = [str(i[labelCols[-1]]) for i in new_data]
        if('labels2' in roles):
          # separate numerical data from labels
          labelCols.append(roles.index('labels2'))
          labels2 = [str(i[labelCols[-1]]) for i in new_data]
        if(len(labelCols)):
          new_data = [[k for j, k in enumerate(i) if j not in labelCols] for i in new_data]
          newRoles = [i for i in roles if i not in ['labels', 'labels2']]
          # check whether x values in roles
          # if no x-values present (but labels), generate pseudo-x categories on the fly
          if(not 'x' in roles):
            newRoles.append('x')
            roles.append('x')
            for index, row in enumerate(new_data):
              row.append(index)
            useLabelFlag = True
        else:
          newRoles = roles
        labels = np.array(labels)
        labels2 = np.array(labels2)
        new_data = np.array(new_data)

        # check for presence of x and y
        if((not 'x' in roles) or (not 'y' in roles)):
          if(not quiet):
            self.parent.statusbar.showMessage('Assign x (or, labels) and y columns!', self.parent.STATUS_TIME)
        else:
          # check whether the new selection makes sense
          array_dim = new_data.shape
          if(len(array_dim) == 1):
            if(array_dim[0] > 0):
              if(not quiet):
                self.parent.statusbar.showMessage('Select at least two data rows!', self.parent.STATUS_TIME)
            elif(self.tableWidget.hasComma()):
              if(not quiet):
                self.parent.statusbar.showMessage('Select some data to import them -- try replacing comma in data!', self.parent.STATUS_TIME)
            else:
              if(not quiet):
                self.parent.statusbar.showMessage('Select some data to import them!', self.parent.STATUS_TIME)
          elif ((len(array_dim) > 1) and (array_dim[1] > 1)):
            # make selected import buttons flash to remind user that they have been active
            self.highlightButtons = [self.errorSelectorLabel, self.errorXSelectorLabel, self.errorPropagateCheck,
                                     self.dataReductionLabel, self.dataTransformXCheck, self.dataTransformYCheck]
            self.timers = []
            for index, button in enumerate(self.highlightButtons):
              if(button.isChecked()):
                button.setHighlightMe(True)
                self.timers.append(QtCore.QTimer())
                self.timers[-1].timeout.connect(partial(self.timerHelper, index))
                self.timers[-1].setSingleShot(True)
                self.timers[-1].start(5000)
          
            # process the error model for y
            if(self.errorSwitch):
              if(self.errorModel):
                if(self.errorModel == 1):
                  # use const
                  errors = [self.errorConst] * array_dim[0]
                  errors = np.array(errors)
                elif(self.errorModel == 2):
                  # use percentage of y
                  if('y' in newRoles):
                    index = newRoles.index('y')
                    errors = [abs(self.errorPercent / 100.0 * i) for i in new_data[:, index]]
                    errors = np.array(errors)
                    #errors = errors.transpose()
                  else:
                    if(not quiet):
                      self.parent.statusbar.showMessage('Cannot locate y values for percentage error calculation!', self.parent.STATUS_TIME, color='blue')
                    
                # check if y-error column already exists
                if('yerr' in newRoles):
                  index = newRoles.index('yerr')
                  new_data[:, index] = errors
                else:
                  newRoles.append('yerr')
                  # repackage errors to enable hstacking
                  errors = [[i] for i in errors]
                  errors = np.array(errors)
                  new_data = np.hstack((new_data, errors))
            else:
              # no errors => possibly delete yerr column
              if('yerr' in newRoles):
                index = newRoles.index('yerr')
                newRoles.pop(index)
                new_data = np.delete(new_data, index, 1)
              
            # process the error model for x
            if(self.errorXSwitch):
              if(self.errorXModel):
                if(self.errorXModel == 1):
                  # use const
                  errors = [self.errorXConst] * array_dim[0]
                  errors = np.array(errors)
                elif(self.errorXModel == 2):
                  # use percentage of x
                  if('x' in newRoles):
                    index = newRoles.index('x')
                    errors = [abs(self.errorXPercent / 100.0 * i) for i in new_data[:, index]]
                    errors = np.array(errors)
                  else:
                    if(not quiet):
                      self.parent.statusbar.showMessage('Cannot locate x values for percentage error calculation!', self.parent.STATUS_TIME, color='blue')
                    
                # check if x-error column already exists
                if('xerr' in newRoles):
                  index = newRoles.index('xerr')
                  new_data[:,index] = errors
                else:
                  newRoles.append('xerr')
                  # repackage errors to enable hstacking
                  errors = [[i] for i in errors]
                  errors = np.array(errors)
                  new_data = np.hstack((new_data, errors))
            else:
              # no x errors => possibly delete xerr column
              if('xerr' in newRoles):
                index = newRoles.index('xerr')
                newRoles.pop(index)
                new_data = np.delete(new_data, index, 1)
              
             # process the data reduction model
            if(self.reductionSwitch):
              if(self.reductionModel == 1):
                # skip data points by numpy slicing
                new_data = new_data[::self.reductionSkip + 1]
                if('labels' in roles):
                  labels = labels[::self.reductionSkip + 1]
                if('labels2' in roles):
                  labels2 = labels2[::self.reductionSkip + 1]
              elif(self.reductionModel == 2):
                # average with error propagation
                if(('labels' in roles) or ('labels2' in roles)):
                  new_data, labels, labels2 = self.movingAverage(sourceData=new_data, roles=newRoles, average=self.reductionAvg, stepsize=self.reductionAvg, labels=labels, labels2=labels2)
                else:
                  new_data = self.movingAverage(sourceData=new_data, roles=newRoles, average=self.reductionAvg, stepsize=self.reductionAvg)
              elif(self.reductionModel == 3):
                # moving average with error propagation
                if(('labels' in roles) or ('labels2' in roles)):
                  new_data, labels, labels2 = self.movingAverage(sourceData=new_data, roles=newRoles, average=self.reductionMovAvg, stepsize=1, labels=labels, labels2=labels2)
                else:
                  new_data = self.movingAverage(sourceData=new_data, roles=newRoles, average=self.reductionMovAvg, stepsize=1)
              elif(self.reductionModel == 4):
                # logarithmic data reduction
                if(('labels' in roles) or ('labels2' in roles)):
                  new_data, labels, labels2 = self.logAverage(sourceData=new_data, roles=newRoles, targetpoints=self.reductionLog, labels=labels, labels2=labels2)
                else:
                  new_data = self.logAverage(sourceData=new_data, roles=newRoles, targetpoints=self.reductionLog)
                
            # do data transform if necessary
            # make copy of original data in case we also have to transform y axis
            if(self.dataTransformYCheck.isChecked()):
              new_data2 = deepcopy(new_data)
            if(self.dataTransformXCheck.isChecked()):
              formula = str(self.dataTransformXEntry.text())
              if(len(formula) > 0):
                # transformer
                formula = 'x = ' + formula
                new_data, _ = self.transformer(sourceData=new_data, roles=newRoles, formula=formula, axis='x')
              else:
                if(not quiet):
                  self.parent.statusbar.showMessage('Enter formula for x transformation!', self.parent.STATUS_TIME)
                
            if(self.dataTransformYCheck.isChecked()):
              formula = str(self.dataTransformYEntry.text())
              if(len(formula) > 0):
                # transformer
                formula = 'y = ' + formula
                new_data2, _ = self.transformer(sourceData=new_data2, roles=newRoles, formula=formula, axis='y')
                # replace y and yerr columns in new_data
                ycol = newRoles.index('y')
                new_data[:, ycol] = new_data2[:, ycol]
                # do error propagation?
                if(('yerr' in newRoles) and self.errorPropagate):
                  yerrcol = newRoles.index('yerr')
                  new_data[:, yerrcol] = new_data2[:, yerrcol]
              else:
                if(not quiet):
                  self.parent.statusbar.showMessage('Enter formula for y transformation!', self.parent.STATUS_TIME)
                
            # delete all rows with non-numerical content
            for role in newRoles:
              index = newRoles.index(role)
              if('labels' in roles):
                labels = labels[np.isfinite(new_data[:, index])]
              if('labels2' in roles):
                labels2 = labels2[np.isfinite(new_data[:, index])]
              new_data = new_data[np.isfinite(new_data[:, index])]
              
            # should we add data to existing data here?
            if(add_data):
              sourceData = self.parent.data[self.parent.activeData].value()
              sourceRoles = list(sourceData.keys())
              sourceLabels = [i for i in self.parent.data[self.parent.activeData].labels]
              sourceLabels2 = [i for i in self.parent.data[self.parent.activeData].labels2]

              # first consider labels?
              if(len(labels) or len(sourceLabels)):
                # expand labels to correct size
                # first convert labels to list
                labels = list(labels)
                i = 0
                while(len(sourceLabels) < len(sourceData[sourceRoles[0]])):
                  sourceLabels.append(i)
                  i += 1
                while(len(labels) < len(new_data[0])):
                  labels.append(i)
                  i += 1
              combinedLabels = sourceLabels + labels

              # first consider labels?
              if(len(labels2) or len(sourceLabels2)):
                # expand labels to correct size
                # first convert labels to list
                labels2 = list(labels2)
                i = 0
                while(len(sourceLabels2) < len(sourceData[sourceRoles[0]])):
                  sourceLabels2.append(i)
                  i += 1
                while(len(labels2) < len(new_data[0])):
                  labels2.append(i)
                  i += 1
              combinedLabels2 = sourceLabels2 + labels2
              
              # then deal with data
              allRoles = list(set(sourceRoles + newRoles))
              # expand data sets prior to merger?
              defaultValues = {'xerr': 0.0, 'yerr': 0.0, 'labels': '', 'labels2': '', 'fval': 0.0, 'resid': 0.0, 'x': 0.0, 'y': 0.0}
              missingRoles = [i for i in allRoles if (not i in sourceRoles)]
              # expand source data set by needed columns
              length = len(sourceData[sourceRoles[0]])
              for entry in missingRoles:
                sourceData[entry] = [defaultValues[entry] for i in range(length)]
              # also expand merge data set by needed columns
              length = len(new_data)
              for entry in sourceData:
                if(entry in newRoles):
                  sourceData[entry] = np.hstack((sourceData[entry], [item[newRoles.index(entry)] for item in new_data]))
                else:
                  sourceData[entry] = np.hstack((sourceData[entry], np.array([defaultValues[entry] for i in range(length)])))
              
              # prepare combined data so that it can be gobbled up by updateData()
              new_data, newRoles, labels, labels2 = [], [], combinedLabels, combinedLabels2
              for key in sourceData:
                newRoles.append(key)
                if(len(new_data)):
                  new_data = np.vstack((new_data, sourceData[key]))
                else:
                  new_data = sourceData[key]
              new_data = np.transpose(new_data)
  
            # assign new data
            if(create):
              self.parent.objectsarea.dataSetCreate()
              self.parent.activeData = len(self.parent.data) - 1
            if(('labels' in roles) or ('labels2' in roles)):
              labels = list(labels)
              self.parent.data[self.parent.activeData].setData(new_data, newRoles, labels=labels, labels2=labels2)
            else:
              self.parent.data[self.parent.activeData].setData(new_data, newRoles)
            
            # make data object visibile
            self.parent.objectsarea.dataSetTable.cellWidget(self.parent.activeData, 0).setChecked(True)
            if(not series):
              self.parent.data[self.parent.activeData].setVisibility(True, redraw=False)
              # update toggle buttons in side bar
              self.parent.plotArea.checkToggloItem(kind='data', index=self.parent.activeData, state=True)
            
            # here we should update the plot
            if(series):
              self.parent.data[self.parent.activeData].handleData, self.parent.data[self.parent.activeData].handleErr, self.parent.data[self.parent.activeData].handleErrShady,\
                self.parent.data[self.parent.activeData].handleBar, self.parent.data[self.parent.activeData].handleStack, self.parent.data[self.parent.activeData].handleStackNeg,\
                self.parent.data[self.parent.activeData].handleText, self.parent.data[self.parent.activeData].handleViolin, self.parent.data[self.parent.activeData].handleScatter = \
                self.parent.plotArea.plotData(self.parent.data[self.parent.activeData].value(), dataobject = self.parent.data[self.parent.activeData], \
                handleData = self.parent.data[self.parent.activeData].handleData, handleErr = self.parent.data[self.parent.activeData].handleErr, handleErrShady=self.parent.data[self.parent.activeData].handleErrShady,\
                handleBar = self.parent.data[self.parent.activeData].handleBar, handleStack = self.parent.data[self.parent.activeData].handleStack,\
                handleStackNeg = self.parent.data[self.parent.activeData].handleStackNeg, handleText = self.parent.data[self.parent.activeData].handleText, handleViolin = self.parent.data[self.parent.activeData].handleViolin,\
                handleScatter = self.parent.data[self.parent.activeData].handleScatter, redraw=False, rescale=False, autoIgnoreCurrentY=True)
              self.parent.data[self.parent.activeData].handleData_div, self.parent.data[self.parent.activeData].handleErr_div, self.parent.data[self.parent.activeData].handleErrShady_div,\
                self.parent.data[self.parent.activeData].handleBar_div, self.parent.data[self.parent.activeData].handleStack_div, self.parent.data[self.parent.activeData].handleStackNeg_div,\
                self.parent.data[self.parent.activeData].handleText_div, self.parent.data[self.parent.activeData].handleViolin_div, self.parent.data[self.parent.activeData].handleScatter_div = \
                self.parent.plotArea.plotData(self.parent.data[self.parent.activeData].value(), dataobject = self.parent.data[self.parent.activeData], \
                handleData = self.parent.data[self.parent.activeData].handleData_div, handleErr = self.parent.data[self.parent.activeData].handleErr_div, handleErrShady=self.parent.data[self.parent.activeData].handleErrShady_div,\
                handleBar = self.parent.data[self.parent.activeData].handleBar_div, handleStack = self.parent.data[self.parent.activeData].handleStack_div,\
                handleStackNeg = self.parent.data[self.parent.activeData].handleStackNeg_div, handleText = self.parent.data[self.parent.activeData].handleText_div, handleViolin = self.parent.data[self.parent.activeData].handleViolin_div,\
                handleScatter = self.parent.data[self.parent.activeData].handleScatter_div, redraw=False, splitX=True, rescale=False, autoIgnoreCurrentY=True)
            else:
              self.parent.data[self.parent.activeData].handleData, self.parent.data[self.parent.activeData].handleErr, self.parent.data[self.parent.activeData].handleErrShady,\
                self.parent.data[self.parent.activeData].handleBar, self.parent.data[self.parent.activeData].handleStack, self.parent.data[self.parent.activeData].handleStackNeg,\
                self.parent.data[self.parent.activeData].handleText, self.parent.data[self.parent.activeData].handleViolin, self.parent.data[self.parent.activeData].handleScatter = \
                self.parent.plotArea.plotData(self.parent.data[self.parent.activeData].value(), dataobject = self.parent.data[self.parent.activeData], \
                handleData = self.parent.data[self.parent.activeData].handleData, handleErr = self.parent.data[self.parent.activeData].handleErr, handleErrShady=self.parent.data[self.parent.activeData].handleErrShady,\
                handleBar = self.parent.data[self.parent.activeData].handleBar, handleStack = self.parent.data[self.parent.activeData].handleStack,\
                handleStackNeg = self.parent.data[self.parent.activeData].handleStackNeg, handleText = self.parent.data[self.parent.activeData].handleText, handleViolin = self.parent.data[self.parent.activeData].handleViolin,\
                handleScatter = self.parent.data[self.parent.activeData].handleScatter, redraw=False, autoIgnoreCurrentY=True)
              self.parent.data[self.parent.activeData].handleData_div, self.parent.data[self.parent.activeData].handleErr_div, self.parent.data[self.parent.activeData].handleErrShady_div,\
                self.parent.data[self.parent.activeData].handleBar_div, self.parent.data[self.parent.activeData].handleStack_div, self.parent.data[self.parent.activeData].handleStackNeg_div,\
                self.parent.data[self.parent.activeData].handleText_div, self.parent.data[self.parent.activeData].handleViolin_div, self.parent.data[self.parent.activeData].handleScatter_div = \
                self.parent.plotArea.plotData(self.parent.data[self.parent.activeData].value(), dataobject = self.parent.data[self.parent.activeData], \
                handleData = self.parent.data[self.parent.activeData].handleData_div, handleErr = self.parent.data[self.parent.activeData].handleErr_div, handleErrShady=self.parent.data[self.parent.activeData].handleErrShady_div,\
                handleBar = self.parent.data[self.parent.activeData].handleBar_div, handleStack = self.parent.data[self.parent.activeData].handleStack_div,\
                handleStackNeg = self.parent.data[self.parent.activeData].handleStackNeg_div, handleText = self.parent.data[self.parent.activeData].handleText_div, handleViolin = self.parent.data[self.parent.activeData].handleViolin_div,\
                handleScatter = self.parent.data[self.parent.activeData].handleScatter_div, redraw=False, splitX=True, autoIgnoreCurrentY=True)
              # and we should redraw the fit function to cover new x-range
              self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
                fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot,\
                handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False, doAutoZoom=False)
              self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
                fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div,\
                handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True, doAutoZoom=False)
              # copy in case split axes are shown
              curve = self.parent.fit[self.parent.activeFit]
              if(self.parent.plotArea.splitY and curve.onBothAxes):
                curve.duplicateForSplit()
              # check whether we should set category labels on x-axis
              if(useLabelFlag):
                self.parent.plotArea.setDataAxisTicks(dataSet=self.parent.activeData, redraw=False, target='plot')
                self.parent.plotArea.setDataAxisTicks(dataSet=self.parent.activeData, redraw=False, target='resid')
              elif((self.parent.plotArea.ticksXDataSet == self.parent.activeData) and (not self.parent.graphicsarea.configTickXAuto.isChecked())):
                # only clear tick labels if inmporting to same object
                self.parent.plotArea.ticksXDataSet = -1
                nuTicks = self.parent.plotArea.setAutoTicks(axis='x', redraw=False, target='plot')
                self.parent.plotArea.setAutoTicks(axis='x', redraw=False, target='resid')
                # manually check button on graphicsarea
                self.parent.graphicsarea.configTickXAuto.setChecked(True)
                self.parent.plotArea.ticksXAuto = True
                tickstr = self.parent.graphicsarea.magicTickstring(nuTicks)
                self.parent.graphicsarea.configTickXEntry.setText(tickstr)
              # and we should update the legend
              self.updateLegend(redraw=redraw)
              # and we should update the corresponding residuals
              self.parent.data[self.parent.activeData].handleResid, self.parent.plotArea.handleResidZero,\
                self.parent.data[self.parent.activeData].handleResidBar, self.parent.data[self.parent.activeData].handleResidStack,\
                self.parent.data[self.parent.activeData].handleResidStackNeg, self.parent.data[self.parent.activeData].handleResidText,\
                self.parent.data[self.parent.activeData].handleResidScatter = self.parent.plotArea.plotResid(\
                dataobject = self.parent.data[self.parent.activeData], handleResid = self.parent.data[self.parent.activeData].handleResid,\
                handleResidZero = self.parent.plotArea.handleResidZero, handleResidBar = self.parent.data[self.parent.activeData].handleResidBar,\
                handleResidStack = self.parent.data[self.parent.activeData].handleResidStack, handleResidStackNeg = self.parent.data[self.parent.activeData].handleResidStackNeg,\
                handleResidText = self.parent.data[self.parent.activeData].handleResidText, handleResidScatter=self.parent.data[self.parent.activeData].handleResidScatter, redraw=False)
              self.parent.data[self.parent.activeData].handleResid_div, self.parent.plotArea.handleResidZero_div,\
                self.parent.data[self.parent.activeData].handleResidBar_div, self.parent.data[self.parent.activeData].handleResidStack_div,\
                self.parent.data[self.parent.activeData].handleResidStackNeg_div, self.parent.data[self.parent.activeData].handleResidText_div,\
                self.parent.data[self.parent.activeData].handleResidScatter_div = self.parent.plotArea.plotResid(\
                dataobject = self.parent.data[self.parent.activeData], handleResid = self.parent.data[self.parent.activeData].handleResid_div,\
                handleResidZero = self.parent.plotArea.handleResidZero_div, handleResidBar = self.parent.data[self.parent.activeData].handleResidBar_div,\
                handleResidStack = self.parent.data[self.parent.activeData].handleResidStack_div, handleResidStackNeg = self.parent.data[self.parent.activeData].handleResidStackNeg_div,\
                handleResidText = self.parent.data[self.parent.activeData].handleResidText_div, handleResidScatter=self.parent.data[self.parent.activeData].handleResidScatter_div, redraw=False, splitX=True)
              # and we should update the resid plot (as x-axis will most likely have rescaled)
              self.parent.plotArea.setAxisLimits(lower=self.parent.plotArea.minX, upper=self.parent.plotArea.maxX, axis='x',\
                updateLabel=False, target='resid', redraw=False, updateGrid=True)
              self.parent.plotArea.setAxisLimits(lower=self.parent.plotArea.minX_div, upper=self.parent.plotArea.maxX_div, axis='x2',\
                updateLabel=False, target='resid', redraw=False, updateGrid=True)
              # draw resid line (again) to ensure coverage of entire x range
              self.parent.plotArea.handleResidZero = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero, redraw=False)
              self.parent.plotArea.handleResidZero_div = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero_div, redraw=redraw, splitX=True)
              
              # and we should update the results table
              self.parent.resultsarea.setDataSet(currIndex=self.parent.activeData)

        QtWidgets.QApplication.restoreOverrideCursor()
        if(True):
          return axis_labels

  def timerHelper(self, index=-1):
    # helper function to control button highlighting
    if(index + 1):
      self.highlightButtons[index].setHighlightMe(False)

  def transformer(self, sourceData=None, roles=None, formula='', axis='x'):
    self.EPSILON, success = 1e-9, False
    # does axis transform
    if(axis in ['x', 'y']):
      if(type(sourceData) != type(None)):
        if(axis in roles):
          # try defining transformation function
          try:
            funcstr = 'def transformThis(self, x, y):'
            funcstr += '\n\t' + formula + '\n\treturn ' + axis
            # generate ffunc in local namespace (this is needed for Python3 vs. Python2, bummer)
            namespace = self.mySpace
            exec(funcstr, namespace)
            # now define the new function in the object scope
            setattr(DataArea, 'transformThis', namespace['transformThis'])
          except:
            self.parent.statusbar.showMessage('Error when setting transformation for ' + axis, self.parent.STATUS_TIME)
          else:
            # do the actual transform
            index = roles.index(axis) #val = sourceData[:,index]
            xindex = roles.index('x'); xval = deepcopy(sourceData[:,xindex])
            yindex = roles.index('y'); yval = deepcopy(sourceData[:,yindex])
            try:
              newVal = self.transformThis(xval, yval)
              # now copy transformed data to data matrix
              sourceData[:,index] = newVal
              success = True
            except:
              self.parent.statusbar.showMessage('Error when calculating transform for ' + axis, self.parent.STATUS_TIME)
            else:
              # deal with data errors
              errname = axis + 'err'
              if(errname in roles):
                errindex = roles.index(errname)
                # numerically determine derivatives in x and y
                # consider x
                if('xerr' in roles):
                  xerrindex = roles.index('xerr'); xerrval = sourceData[:,xerrindex]; xerrval = xerrval ** 2
                  try:
                    xderiv = self.transformThis(xval + self.EPSILON, yval)
                    xderiv = (xderiv - newVal) / self.EPSILON
                    xderiv = xderiv ** 2
                    newErr = xerrval * xderiv
                  except:
                    pass
                else:
                  newErr = np.array([0] * len(newVal))
                
                # consider y
                if('yerr' in roles):
                  yerrindex = roles.index('yerr'); yerrval = sourceData[:,yerrindex]; yerrval = yerrval ** 2
                  try:
                    yderiv = self.transformThis(xval, yval + self.EPSILON)
                    yderiv = (yderiv - newVal) / self.EPSILON
                    yderiv = yderiv ** 2
                    newErr = newErr + (yerrval * yderiv)
                  except:
                    pass
                  
                # calculate root of error
                newErr = newErr ** 0.5
                sourceData[:,errindex] = newErr

    return sourceData, success

  def logAverage(self, sourceData=None, roles=None, targetpoints=100, labels=np.array([]), labels2=np.array([])):
    # reduces data logarithmically to (approx.) target no. of points
    if(type(sourceData) != type(None)):
      if(('x' in roles) and ('y' in roles)):
        # locate x values
        xcol = roles.index('x')
        xval = sourceData[:,xcol]

        # get positive x values
        posXval = xval[xval > 0]
        if(len(posXval) > 0):
          # calculate target x values on log-spaced scale
          logXval = np.linspace(np.log(np.min(posXval)), np.log(np.max(posXval)), targetpoints)
          targetXval = np.exp(logXval)
          targetBoundary = [0]
          targetBoundary.extend([(targetXval[i] + targetXval[i+1])/2 for i in range(len(targetXval)-1)])
          targetBoundary.append(targetXval[-1])

          # cycle through boundary list
          output, avgLabels, avgLabels2 = np.array([]), [], []
          for index, entry in enumerate(targetBoundary[:-1]):
            targetRows = sourceData[sourceData[:,xcol] > targetBoundary[index]]
            labelIndex = len(sourceData[sourceData[:,xcol] <= targetBoundary[index]])
            targetRows = targetRows[targetRows[:,xcol] <= targetBoundary[index+1]]
          
            # check current entry
            if(targetRows.size > 0):
              if((len(targetRows.shape) > 1) and (targetRows.shape[0] > 1)):
                # calculate averages
                newRow = []
                for index2, entry2 in enumerate(roles):
                  if(entry2 in ['x', 'y']):
                    # numerically average x and y values
                    newRow.append(np.average(targetRows[:,index2]))
                  elif(entry2 in ['xerr', 'yerr']):
                    errVal = (targetRows[:,index2] / len(targetRows[:,index2])) ** 2
                    errVal = np.sum(errVal) ** 0.5
                    newRow.append(errVal)
                    
                targetRows = np.array(newRow)
                
              if(labels.size):
                avgLabels.append(labels[labelIndex])
              if(labels2.size):
                avgLabels2.append(labels2[labelIndex])

              # append current line to output
              if(len(output) > 0):
                output = np.vstack((output, targetRows))
              else:
                output = targetRows
          
          if(labels.size or labels2.size):
            return output, np.array(avgLabels), np.array(avgLabels2)
          else:
            return output
  
        else:
          self.parent.statusbar.showMessage('No positive x values, cannot do any reduction!', self.parent.STATUS_TIME)
          return sourceData

  def movingAverage(self, sourceData=None, roles=None, average=1, stepsize=1, labels=np.array([]), labels2=np.array([])):
    # calculate a moving average with error propagation
    if(type(sourceData) != type(None)):
      if(('x' in roles) and ('y' in roles)):
        # locate x and y values
        xcol, ycol = roles.index('x'), roles.index('y')
        xval, yval = sourceData[:, xcol], sourceData[:, ycol]
        # moving average
        avgXval = np.array([np.average(xval[i: i + average]) for i in range(0, len(xval) - average + 1, stepsize)])
        avgYval = np.array([np.average(yval[i: i + average]) for i in range(0, len(yval) - average + 1, stepsize)])
        # check for presence of error values
        if('yerr' in roles):
          # need to do error propagation
          yerrcol = roles.index('yerr')
          yerrval = sourceData[:, yerrcol]
          yerrval = (yerrval / average) ** 2
          # error propagation
          avgYerrval = np.array([np.sum(yerrval[i: i + average]) for i in range(0, len(yerrval) - average + 1, stepsize)])
          avgYerrval = avgYerrval ** 0.5
        if('xerr' in roles):
          # need to do error propagation
          xerrcol = roles.index('xerr')
          xerrval = sourceData[:, xerrcol] 
          xerrval = (xerrval / average) ** 2
          # error propagation
          avgXerrval = np.array([np.sum(xerrval[i: i + average]) for i in range(0, len(xerrval) - average + 1, stepsize)])
          avgXerrval = avgXerrval ** 0.5
          
        # deal with data labels
        if(labels.size):
          # use label of first data point to be averaged
          avgLabel = np.array([labels[i] for i in range(0, len(xval) - average + 1, stepsize)])
        if(labels2.size):
          # use label of first data point to be averaged
          avgLabel2 = np.array([labels2[i] for i in range(0, len(xval) - average + 1, stepsize)])

        # now need to assemble columns according to roles
        procData = np.array([])
        for entry in roles:
          # get current column
          if(entry == 'x'):
            addItem = avgXval
          elif(entry == 'y'):
            addItem = avgYval
          elif(entry == 'xerr'):
            addItem = avgXerrval
          elif(entry == 'yerr'):
            addItem = avgYerrval
          
          # assemble output data
          if(procData.size > 0):
            procData = np.vstack((procData, addItem))
          else:
            procData = addItem

        if(labels.size or labels2.size):
          return procData.transpose(), avgLabel, avgLabel2
        else:
          return procData.transpose()
        
  def loadData(self):
    global REMEMBERDIR
    global RECENTFILES
    filter_options = ['Excel Files (*.xls *.xlsx)', 'Open Office (*.ods)', 'Text Tab Delimited (*.txt)', 'Text Comma Separated (*.txt *.csv)', 'Text Whitespace Delimited (*.txt)']
    if(PYCORN_PRESENT):
      filter_options.append('Unicorn Files (*.res)')
    filterstring = ';;'.join(filter_options)
    filename, filter_ = QtWidgets.QFileDialog.getOpenFileName(self, filter=filterstring, directory = REMEMBERDIR, caption='Open Data', initialFilter=self.importFilter)
    self.importFilter = filter_
    filename = str(filename)
    if(PATH_SEPARATOR in filename):
      REMEMBERDIR = filename.split(PATH_SEPARATOR)[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    elif('/' in filename):
      REMEMBERDIR = filename.split('/')[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    if(len(filename) > 0):
      mode = filter_options.index(filter_)
      if(mode == 0):
        self.tableWidget.loadXLS(filename=filename, transpose=self.transposeData)
      elif(mode == 1):
        self.tableWidget.loadODS(filename=filename, transpose=self.transposeData)
      elif(mode == 2):
        self.tableWidget.loadTextFile(filename=filename, delimiter='\t', transpose=self.transposeData)
      elif(mode == 3):
        self.tableWidget.loadTextFile(filename=filename, delimiter=',', transpose=self.transposeData)
      elif(mode == 4):
        self.tableWidget.loadTextFile(filename=filename, delimiter=None, transpose=self.transposeData)
      elif(mode == 5):
        self.tableWidget.loadUnicornFile(filename=filename, transpose=self.transposeData)
      # store this for use in recent files
      if(mode in [0, 1, 2, 3, 4, 5]):
        # loop through list and remove previous entry
        index = len(RECENTFILES) - 1
        for entry in RECENTFILES[::-1]:
          if(entry[0] == filename):
            RECENTFILES.pop(index)
          index -= 1
        RECENTFILES.insert(0, [filename, mode])
        # truncate to 20 entries
        RECENTFILES = RECENTFILES[:20]
    else:
      return False
    
    # mayhaps display tool tip (only do this on first time after program start)
    if(self.firstLoad):
      self.firstLoad = False
      QtWidgets.QToolTip.showText(self.refreshButton.mapToGlobal(QtCore.QPoint(50, -20)), '\u2199 After selecting data, click here to import and plot them', self.refreshButton)

    return True

  def blankResizeTable(self, callButton=None):
    # opens a QMenu to allow blanking and resizing of data table
    self.menu = BlankResizeMenu(self, self.tableWidget)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      menuY += callButton.geometry().height()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    # open at mouse pointer
    self.menu.popup(QtCore.QPoint(int(menuX), int(menuY)))

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.plotArea.setLegend(value=value, redraw=redraw)
      
class ShrinkoWidget(QWidgetMac):
  def __init__(self, notify=None, notifyResid=False, parent=None):
    super(ShrinkoWidget, self).__init__()
    self.notify = notify
    self.notifyResid = notifyResid
    self.parent = parent
    
  def resizeEvent(self, event):
    # do the regular resize event
    QtWidgets.QWidget.resizeEvent(self, event)
    # is this the container with the matplot inside?
    if(self.notify != None):
      if(self.notifyResid):
        # can probably skip the horizontal resize as this is already done by the partner shrink-o-widget
        self.notify.verticalRulerResid.updateRuler()
        # also need to update tick entry fields and axis grid
        if(self.parent != None):
          self.parent.updateTickEntryField(axis='resid')
          for axis in ['x', 'x2', 'y']:
            self.parent.drawAxisGrid(axis=axis, target='resid', redraw=False)
      else:
        self.notify.horizontalRuler.updateRuler()
        self.notify.verticalRuler.updateRuler()
        self.parent.destructAboutLogo()
        # also need to update tick entry fields and axis grid
        # still does not fix the issue on split axis toggling
        if(self.parent != None):
          for axis in ['x', 'x2', 'y', 'y2']:
            self.parent.updateTickEntryField(axis=axis)
            self.parent.drawAxisGrid(axis=axis, target='plot', redraw=False)

class Ruler(QtWidgets.QFrame):
  def __init__(self, parent=None, mode=0, resid=False, numTicks=5):
    super(Ruler, self).__init__()
    self.parent = parent
    self.mode = mode
    self.resid = resid
    self.ticks = {}
    self.LARGE_RECTANGLE = 1000
    self.NUMBER_TICKS = numTicks
    self.ROUNDNESS = 3
    self.currwidth, self.currheight, self.currheight_resid = 4.0, 4.0, 1.0
    
    # init appearance
    self.setFrameShape(QtWidgets.QFrame.Shape.Box)
    self.setFrameShadow(QtWidgets.QFrame.Shadow.Sunken)

  def paintEvent(self, event):
    # draw ruler
    s = self.size()
    width, height = s.width(), s.height()
    qp = QtGui.QPainter()
    if(qp is not None):
      qp.begin(self)
      qp.setPen(QtGui.QColor('black'))
      qp.setBrush(QtGui.QColor('white'))
      qp.setPen(QtGui.QColor(*UI_TEXT_COLOR))
      qp.setBrush(QtGui.QColor(*UI_BASE_COLOR))
      qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
      qp.drawRoundedRect(0, 0, width, height, int(self.ROUNDNESS), int(self.ROUNDNESS))
      
      # draw ticks
      if(self.mode):
        # vertical ruler
        qp.rotate(90)
        for entry in self.ticks:
          posY = int(entry * height + 0.5 - self.LARGE_RECTANGLE / 2)
          label = self.ticks[entry]
          if(entry > 0):
            qp.drawText(posY, -int(BASE_SIZE) - 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignVCenter, label)
            
        # draw dots in between
        labelPos = sorted(list(self.ticks.keys()))
        if(len(labelPos) > 1):
          # extend by one to draw dots beyond last number
          labelPos.append(2.0 * labelPos[-1] - labelPos[-2])
        for index in range(len(labelPos) - 1):
          posY = ((labelPos[index] + labelPos[index + 1]) / 2.0)
          posY = int(posY * height + 0.5 - self.LARGE_RECTANGLE / 2)
          qp.drawText(posY, -int(BASE_SIZE) - 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignVCenter, '\u2022')
          ###if((self.resid and (self.currheight_resid >= 2.5)) or ((not self.resid) and (self.currheight > 2.5))):
          # never draw small dots
          if(0):
            # small dots
            posY = ((3.0 * labelPos[index] + labelPos[index + 1]) / 4.0)
            posY = int(posY * height + 0.5 - self.LARGE_RECTANGLE / 2)
            qp.drawText(posY, -int(BASE_SIZE) - 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignVCenter, '\u00b7')
            posY = ((labelPos[index] + 3.0 * labelPos[index + 1]) / 4.0)
            posY = int(posY * height + 0.5 - self.LARGE_RECTANGLE / 2)
            qp.drawText(posY, -int(BASE_SIZE) - 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignVCenter, '\u00b7')
        qp.rotate(-90)
      else:
        # horizontal ruler
        for entry in self.ticks:
          posX = int(entry * width + 0.5 - self.LARGE_RECTANGLE / 2)
          label = self.ticks[entry]
          if(entry > 0):
            # for alignment of text, need to specify a bounding box (which can be fairly large)
            qp.drawText(posX, 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop, label)
            
        # draw dots in between
        labelPos = sorted(list(self.ticks.keys()))
        if(len(labelPos) > 1):
          # extend by one to draw dots beyond last number
          labelPos.append(2.0 * labelPos[-1] - labelPos[-2])
        for index in range(len(labelPos) - 1):
          posX = ((labelPos[index] + labelPos[index + 1]) / 2.0)
          posX = int(posX * width + 0.5 - self.LARGE_RECTANGLE / 2)
          qp.drawText(posX, 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop, '\u2022')
          # never draw small dots
          if(0):
            posX = ((3.0 * labelPos[index] + labelPos[index + 1]) / 4.0)
            posX = int(posX * width + 0.5 - self.LARGE_RECTANGLE / 2)
            qp.drawText(posX, 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop, '\u00b7')
            posX = ((labelPos[index] + 3.0 * labelPos[index + 1]) / 4.0)
            posX = int(posX * width + 0.5 - self.LARGE_RECTANGLE / 2)
            qp.drawText(posX, 2, self.LARGE_RECTANGLE, int(BASE_SIZE), QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop, '\u00b7')
        
      qp.end()

  def resizeEvent(self, event):
    # custom resize event
    QtWidgets.QFrame.resizeEvent(self, event)
    ###self.updateRuler()
    ### need to get the synchronization right, has to trigger after canvas resize
    ### we better trigger the update in the parental shrink-o-widget

  def updateRuler(self):
    # updates the ruler
    # first delete all previous ticks
    self.ticks = {}

    # caluclate new ones
    if(self.resid):
      currwidth, currheight = self.parent.residplot.get_size_inches()
      self.currheight_resid = currheight
    else:
      currwidth, currheight = self.parent.matplot.get_size_inches()
      self.currwidth, self.currheight = currwidth, currheight
    
    if(self.mode):
      # vertical ruler
      useTicks = self.NUMBER_TICKS
      if(currheight < 2.5):
        useTicks = 2
      incrementV = int(currheight / useTicks)
      # check for large zoom scale
      escalate = 1
      while(not incrementV):
        escalate *= 2.0
        incrementV = int(escalate * currheight / useTicks)
      incrementV *= 1.0 / escalate
      # and the actual calculation
      currPos, incPos = 0, 1.0 * incrementV / currheight
      while((currPos * incPos) < 1):
        posY = currPos * incPos
        label = currPos * incrementV
        self.ticks[posY] = '{0:g}'.format(label)
        currPos += 1
    else:
      # horizontal ruler
      useTicks = self.NUMBER_TICKS
      if(currwidth < 2.5):
        useTicks = 2
      incrementH = int(currwidth / useTicks)
      # check for large zoom scale
      escalate = 1
      while(not incrementH):
        escalate *= 2.0
        incrementH = int(escalate * currwidth / useTicks)
      incrementH *= 1.0 / escalate
      # and the actual calculation
      currPos, incPos = 0, 1.0 * incrementH / currwidth
      while((currPos * incPos) < 1):
        posX = currPos * incPos
        label = currPos * incrementH
        self.ticks[posX] = '{0:g}'.format(label)
        currPos += 1
      
    # issue paint event
    self.update()

class MyAutoMinorLocator(matplotlib.ticker.AutoMinorLocator):
  # custom minor locator to heed missing major ticks
  def __init__(self, n=None):
    super(MyAutoMinorLocator, self).__init__(n=n)

  def __call__(self):
    'Return the locations of the ticks'
    majorlocs = self.axis.get_majorticklocs()
    # are we on a log scale?
    mode = self.axis.get_scale()

    if(mode != 'log'):
      try:
        majorstep = majorlocs[1] - majorlocs[0]
      except IndexError:
        return []
    else:
      try:
        majorstep = majorlocs[1] / majorlocs[0]
      except IndexError:
        return []
      except ZeroDivisionError:
        return []

    if self.ndivs is None:
      ndivs = 9
    else:
      ndivs = self.ndivs

    # view interval
    vmin, vmax = self.axis.get_view_interval()
    if vmin > vmax:
      vmin, vmax = vmax, vmin

    # calculate minor ticks
    locs = np.array([])
    for index in range(len(majorlocs) - 1):
      # can treat linear and log axes the same
      nulocs = np.linspace(majorlocs[index], majorlocs[index + 1], ndivs + 1)
      locs = np.hstack((locs, nulocs[1: -1]))
    
    # run through conditions
    cond1 = locs >= vmin
    cond2 = locs <= vmax
    locs = locs.compress(cond1 & cond2)

    return self.raise_if_exceeds(np.array(locs))

class MyLassoli(Lasso):
  def __init__(self, *args, **kwargs):
    item = 'parent'
    if(item in kwargs):
      self.__dict__[item] = kwargs[item]
      del kwargs[item]
    else:
      self.__dict__[item] = None
    # normal constructor
    super(MyLassoli, self).__init__(*args, **kwargs)
    # ensure compatibility with matplotlib 3.5.0 and above
    if(not hasattr(self, 'line')):
      self.line = self._selection_artist
    self.line.set_linewidth(2.0)
    self.line.set_solid_capstyle('round')
    self.line.set_color([0.2, 0.15, 0.9, 1.0])
    # to coordinate crosstalk with rectangle selector
    # adjust line size acording to zoom level
    try:
      if(self.parent != None):
        currZoomLevel = self.parent.matplot.get_dpi()
        linewidth = 2.0 * 100.0 / currZoomLevel
        self.line.set_linewidth(linewidth)
        if(linewidth >= 4.0):
          self.line.set_path_effects([PathEffects.withStroke(linewidth=12.0, foreground=[1.0, 1.0, 1.0, 1.0])])
        elif(linewidth >= 2.0):
          self.line.set_path_effects([PathEffects.withStroke(linewidth=5.0, foreground=[1.0, 1.0, 1.0, 1.0])])
        elif(linewidth >= 1.0):
          self.line.set_path_effects([PathEffects.withStroke(linewidth=2.0, foreground=[1.0, 1.0, 1.0, 1.0])])
        else:
          self.line.set_path_effects([PathEffects.withStroke(linewidth=1.0, foreground=[1.0, 1.0, 1.0, 1.0])])
    except:
      pass

  # need to override onmove to get crosstalk with crosshair cursor right
  # other than that, copied from widgets.py
  def onmove(self, event):
    if (self.ignore(event)
        or self.verts is None
        or event.button != 1
        or not self.ax.contains(event)[0]):
      return
    # allow for different matplotlib versions
    try:
      self.verts.append(self._get_data_coords(event))
    except:
      self.verts.append((event.xdata, event.ydata))
    self.line.set_data(list(zip(*self.verts)))

    # only draw if cursor is hidden
    if((self.parent != None) and (not self.parent.cursorVisible)):
      if self.useblit:
        self.canvas.restore_region(self.background)
        self.ax.draw_artist(self.line)
        self.canvas.blit(self.ax.bbox)
      else:
        self.canvas.draw_idle()
    
  def drawMe(self):
    # draws the artist
    self.ax.draw_artist(self.line)
  
  def cleanUp(self):
    # remove lassoli after use
    # don't blit here to avoid crosstalk with rectangle selector
    #if(self.useblit):
    if(False):
      self.canvas.restore_region(self.background)
      self.canvas.blit(self.ax.bbox)
    else:
      self.canvas.draw_idle()

class MyRectangli(AxesWidget):
  # adapted from Lasso in matplotlib.widgets.py
  def __init__(self, ax, xy, callback, useblit=True, parent=None):
    super().__init__(ax)

    self.parent = parent
    self.useblit = useblit and self.canvas.supports_blit
    if self.useblit:
      self.background = self.canvas.copy_from_bbox(self.ax.bbox)

    x, y = xy
    self.verts = [(x, y)]
    self.rectangle = matplotlib.patches.Rectangle((0, 0), 0, 1, visible=True)
    self.ax.add_patch(self.rectangle)
    self.rectangle.set_linewidth(4.0)
    self.rectangle.set_linestyle('solid')
    self.rectangle.set_edgecolor([0, 0, 0, 1])
    self.rectangle.set_facecolor([0, 0, 1, .4])
    self.callback = callback
    self.connect_event('button_release_event', self.onrelease)
    self.connect_event('motion_notify_event', self.onmove)

    # adjust line size acording to zoom level
    try:
      if(self.parent != None):
        currZoomLevel = self.parent.matplot.get_dpi()
        linewidth = 2.0 * 100.0 / currZoomLevel
        self.rectangle.set_linewidth(linewidth)
        if(linewidth >= 4.0):
          self.rectangle.set_path_effects([PathEffects.withStroke(linewidth=12.0, foreground=[1.0, 1.0, 1.0, 1.0])])
        elif(linewidth >= 2.0):
          self.rectangle.set_path_effects([PathEffects.withStroke(linewidth=5.0, foreground=[1.0, 1.0, 1.0, 1.0])])
        elif(linewidth >= 1.0):
          self.rectangle.set_path_effects([PathEffects.withStroke(linewidth=2.0, foreground=[1.0, 1.0, 1.0, 1.0])])
        else:
          self.rectangle.set_path_effects([PathEffects.withStroke(linewidth=1.0, foreground=[1.0, 1.0, 1.0, 1.0])])
    except:
      pass

  def onrelease(self, event):
    if self.ignore(event):
        return
    if self.verts is not None:
      # allow for different matplotlib versions
      try:
        self.verts.append(self._get_data_coords(event))
      except:
        self.verts.append((event.xdata, event.ydata))
      if len(self.verts) > 2:
        self.callback(self.verts)
      self.rectangle.remove()
    self.verts = None
    self.disconnect_events()

  def onmove(self, event):
    if (self.ignore(event)
      or self.verts is None
      or event.button != 1
      or not self.ax.contains(event)[0]):
        return
    # allow for different matplotlib versions
    try:
      self.verts.append(self._get_data_coords(event))
    except:
      self.verts.append((event.xdata, event.ydata))
    if(len(self.verts) > 1):
      xy = (np.min((self.verts[0][0], self.verts[-1][0])), np.min((self.verts[0][1], self.verts[-1][1])))
      width, height = np.abs((self.verts[0][0] - self.verts[-1][0])), np.abs((self.verts[0][1] - self.verts[-1][1]))
      self.rectangle.set_xy(xy)
      self.rectangle.set_height(height)
      self.rectangle.set_width(width)

      # only draw if cursor is hidden
      if((self.parent != None) and (not self.parent.cursorVisible)):
        if self.useblit:
          self.canvas.restore_region(self.background)
          self.ax.draw_artist(self.rectangle)
          self.canvas.blit(self.ax.bbox)
        else:
          self.canvas.draw_idle()
        
  def drawMe(self):
    # draws the rectangle
    self.ax.draw_artist(self.rectangle)

  def cleanUp(self):
    # remove rectangli after use
    # don't blit here to avoid crosstalk
    #if(self.useblit):
    if(False):
      self.canvas.restore_region(self.background)
      self.canvas.blit(self.ax.bbox)
    else:
      self.canvas.draw_idle()

  def getMyExtents(self):
    # returns first and last element of self.vert
    if(len(self.verts) > 2):
      return self.verts[0], self.verts[-1]
    else:
      return None, None

class MatplotlibCanvas(QWidgetMac):
  def __init__(self, parent=None):
    super(MatplotlibCanvas, self).__init__()
    self.parent = parent
    
    # initialize param values
    self.initParam()

    # set initial matplotlib style
    originalFont = matplotlib.rcParams.get('mathtext.fontset')
    matplotlib.style.use(self.stylemodel)
    # ensure that Dejavu Sans is used for mathtext (rather use original font as dejavusans is platform/version-depedent)
    matplotlib.rc('mathtext', fontset=originalFont)
    
    plt.rcParams['axes.unicode_minus'] = False
    
    # a validator
    self.validFloat = MyValidFloat()
    
    # generate GUI elements
    self.buildRessource()
    
    # initialize plot
    self.initPlot(initialize=True)

    # toggle split axes
    self.toggleSplit(redraw=False, toggled=False)
    
    # update rulers
    self.horizontalRuler.updateRuler()
    self.verticalRuler.updateRuler()
    self.verticalRulerResid.updateRuler()
    
  def initParam(self):
    # allow some spacing around data when autoscaling
    self.data_spacer = 0.025
    
    # initialize some values
    self.x, self.y, self.y2 = np.array([]), np.array([]), np.array([])
    self.minX, self.maxX = 0.0, 1.0
    self.minY, self.maxY = 0.0, 1.0
    self.storeCoord = []
    self.minResidY, self.maxResidY = -0.5, 0.5
    self.modeX, self.modeY = 'linear', 'linear'
    self.EPSILON = 1e-6
    self.DATAPOINTS_SIMULATION = 2000
    self.autoScaleX, self.autoScaleY = True, True
    self.labelX = 'x'
    self.labelY = 'y'
    self.labelXShow, self.labelYShow = True, True
    self.labelXColor, self.labelYColor = [0.2, 0.2, 0.2, 1.0], [0.2, 0.2, 0.2, 1.0]
    self.labelXSize, self.labelYSize = 14.0, 14.0
    self.labelXWeight, self.labelYWeight = 'normal', 'normal'
    self.labelXStyle, self.labelYStyle = 'italic', 'italic'
    self.labelXVariant, self.labelYVariant = 'normal', 'normal'
    self.labelXPad, self.labelYPad = 4.0, 4.0
    self.labelXLinespacing, self.labelYLinespacing = 1., 1.
    self.labelXPos, self.labelYPos = 0.5, 0.5
    self.labelXAngle, self.labelYAngle = 0.0, 90.0
    self.labelXAlignment, self.labelYAlignment = 'center', 'center'
    self.labelXAlignmentVertical, self.labelYAlignmentVertical = 'top', 'baseline'
    self.axisVisible = {'left':True, 'right':True, 'bottom':True, 'top':True, 'left2':True, 'right2':True}
    self.axisWidth = {'left':1.0, 'right':1.0, 'bottom':1.0, 'top':1.0, 'left2':1.0, 'right2':1.0}
    self.axisStyle = {'left':'solid', 'right':'solid', 'bottom':'solid', 'top':'solid', 'left2':'solid', 'right2':'solid'}
    self.axisDashStyle = {'left':'butt', 'right':'butt', 'bottom':'butt', 'top':'butt', 'left2':'butt', 'right2':'butt'}
    self.axisColor = {'left':[0.2, 0.2, 0.2, 1.0], 'right':[0.2, 0.2, 0.2, 1.0], 'bottom':[0.2, 0.2, 0.2, 1.0],\
                      'top':[0.2, 0.2, 0.2, 1.0], 'left2':[0.2, 0.2, 0.2, 1.0], 'right2':[0.2, 0.2, 0.2, 1.0]}
    self.axisFont = {'x': 'DejaVu Sans', 'y': 'DejaVu Sans', 'y2': 'DejaVu Sans', 'xs': 'DejaVu Sans'}
    self.axisPosition = {'left':'axes', 'right':'axes', 'bottom':'axes', 'top':'axes', 'left2':'axes', 'right2':'axes'}
    self.axisPositionValue = {'left':0.0, 'right':1.0, 'bottom':0.0, 'top':1.0, 'left2':0.0, 'right2':1.0}
    self.axisBoundary = {'left':[0.0, 1.0], 'right':[0.0, 1.0], 'bottom':[0.0, 1.0], 'top':[0.0, 1.0], 'left2':[0.0, 1.0], 'right2':[0.0, 1.0]}
    self.axisBoundaryCheck = {'left':False, 'right':False, 'bottom':False, 'top':False, 'left2':False, 'right2':False}
    self.tickFont = {'x': 'DejaVu Sans', 'y': 'DejaVu Sans', 'y2': 'DejaVu Sans', 'xs': 'DejaVu Sans'}
    self.ticksVisible = {'left':True, 'right':True, 'bottom':True, 'top':True, 'left2':False, 'right2':True, 'slaved': True}
    self.ticksWidth = {'left':0.5, 'right':0.5, 'bottom':0.5, 'top':0.5, 'left2':0.5, 'right2':0.5, 'slaved': .5}
    self.ticksLength = {'left':5.0, 'right':5.0, 'bottom':5.0, 'top':5.0, 'left2':5.0, 'right2':5.0, 'slaved': 5.}
    self.ticksColor = {'left':[0.2, 0.2, 0.2, 1.0], 'right':[0.2, 0.2, 0.2, 1.0], 'bottom':[0.2, 0.2, 0.2, 1.0],\
                       'top':[0.2, 0.2, 0.2, 1.0], 'left2':[0.2, 0.2, 0.2, 1.0], 'right2':[0.2, 0.2, 0.2, 1.0], 'slaved': [0.9, 0.2, 0.2, 1.0]}
    self.ticksDirection = {'left':'in', 'right':'in', 'bottom':'in', 'top':'in', 'left2':'in', 'right2':'in', 'slaved': 'in'}
    self.ticksLabelShow = {'left': True, 'right': False, 'bottom': True, 'top': False, 'left2': False, 'right2': True, 'slaved': True}
    self.canvasColor = [0.9, 0.9, 0.9, 1.0]
    self.canvasFill, self.figureFill, self.frameDraw = True, True, False
    self.figureColor = [1.0, 1.0, 1.0, 1.0]
    self.ticksX, self.ticksY, self.ticksResidY = [], [], []
    self.ticksXShow, self.ticksYShow, self.ticksResidYShow = True, True, True
    self.ticksXAuto, self.ticksYAuto, self.ticksResidYAuto = True, True, True
    self.ticksXMinor, self.ticksYMinor, self.ticksY2Minor, self.ticksResidYMinor = 2, 2, 2, 2
    self.ticksXMinorRelativeLength, self.ticksYMinorRelativeLength, self.ticksY2MinorRelativeLength, self.ticksResidYMinorRelativeLength = 0.5, 0.5, 0.5, 0.5
    self.ticksXLabel, self.ticksXDataSet = [], -1
    self.ticksYLabel, self.ticksYDataSet = [], -1
    self.ticksXColor = [0.2, 0.2, 0.2, 1.0]
    self.ticksYColor = [0.2, 0.2, 0.2, 1.0]
    self.ticksXSize, self.ticksYSize = 12.0, 12.0
    self.ticksXWeight, self.ticksYWeight = 'normal', 'normal'
    self.ticksXStyle, self.ticksYStyle = 'normal', 'normal'
    self.ticksXAngle, self.ticksYAngle = 0.0, 0.0
    self.ticksXAlignment, self.ticksYAlignment = 'center', 'right'
    self.ticksXAlignmentVertical, self.ticksYAlignmentVertical = 'top', 'center'
    self.ticksXLinespacing, self.ticksYLinespacing = 1., 1.
    self.ticksXPad, self.ticksYPad = 4.0, 4.0
    self.ticksXPad2, self.ticksYPad2 = 0.0, 0.0
    self.ticksXFormat, self.ticksYFormat, self.ticksY2Format, self.ticksResidYFormat = 'default', 'default', 'default', 'default'
    self.ticksXFormatPrecision, self.ticksYFormatPrecision, self.ticksY2FormatPrecision, self.ticksResidYFormatPrecision = 2, 2, 2, 2
    self.ticksXFormatTrailZero, self.ticksYFormatTrailZero, self.ticksY2FormatTrailZero, self.ticksResidYFormatTrailZero = False, False, False, False
    self.ticksXFormatSeparator, self.ticksYFormatSeparator, self.ticksY2FormatSeparator, self.ticksResidYFormatSeparator = False, False, False, False
    self.ticksXFormatComma, self.ticksYFormatComma, self.ticksY2FormatComma, self.ticksResidYFormatComma = False, False, False, False
    self.ticksXFormatPrefix, self.ticksYFormatPrefix, self.ticksY2FormatPrefix, self.ticksResidYFormatPrefix = '', '', '', ''
    self.ticksXFormatPostfix, self.ticksYFormatPostfix, self.ticksY2FormatPostfix, self.ticksResidYFormatPostfix = '', '', '', ''
    self.fallback_ticksXFormat, self.fallback_ticksYFormat, self.fallback_ticksY2Format, self.fallback_ticksResidYFormat = None, None, None, None
    self.gridVisible = {'x': False, 'x2': False, 'y': False, 'y2': False}
    self.gridLinesStore = {'x': [], 'x2': [], 'y': [], 'y2': []}
    self.gridLinesStore_resid = {'x': [], 'x2': [], 'y': [], 'y2': []}
    self.gridWidth = {'x': 0.5, 'x2': 0.5, 'y': 0.5, 'y2': 0.5}
    self.gridStyle = {'x': 'solid', 'x2': 'solid', 'y': 'solid', 'y2': 'solid'}
    self.gridDashStyle = {'x': 'butt', 'x2': 'butt', 'y': 'butt', 'y2': 'butt'}
    self.gridColor = {'x': [0.2, 0.2, 0.2, 1.0], 'x2': [0.2, 0.2, 0.2, 1.0], 'y': [0.2, 0.2, 0.2, 1.0], 'y2': [0.2, 0.2, 0.2, 1.0]}
    self.gridOrder = {'x': 'back', 'x2': 'back', 'y': 'back', 'y2': 'back'}
    self.gridMinorVisible = {'x': False, 'x2': False, 'y': False, 'y2': False}
    self.gridMinorLinesStore = {'x': [], 'x2': [], 'y': [], 'y2': []}
    self.gridMinorLinesStore_resid = {'x': [], 'x2': [], 'y': [], 'y2': []}
    self.gridMinorWidth = {'x': 0.5, 'x2': 0.5, 'y': 0.5, 'y2': 0.5}
    self.gridMinorStyle = {'x': 'dashed', 'x2': 'dashed', 'y': 'dashed', 'y2': 'dashed'}
    self.gridMinorDashStyle = {'x': 'butt', 'x2': 'butt', 'y': 'butt', 'y2': 'butt'}
    self.gridMinorColor = {'x': [0.2, 0.2, 0.2, 1.0], 'x2': [0.2, 0.2, 0.2, 1.0], 'y': [0.2, 0.2, 0.2, 1.0], 'y2': [0.2, 0.2, 0.2, 1.0]}
    self.gridMinorOrder = {'x': 'back', 'x2': 'back', 'y': 'back', 'y2': 'back'}
    self.gridRectStore = {'x': [], 'x2': [], 'y': [], 'y2': []}
    self.gridRectStore_resid = {'x': [], 'x2': [], 'y': [], 'y2': []}
    self.gridRectVisible = {'x': False, 'x2': False, 'y': False, 'y2': False}
    self.gridRectColor = {'x': [0.2, 0.2, 0.2, .2], 'x2': [0.2, 0.2, 0.2, .2], 'y': [0.2, 0.2, 0.2, .2], 'y2': [0.2, 0.2, 0.2, .2]}
    
    self.exportWidth, self.exportHeight = 8.0, 6.0
    self.padSize = {'left': 0.15, 'right': 0.95, 'bottom': 0.15, 'top': 0.95}
    self.frameWidth, self.frameColor, self.frameStyle, self.frameDashStyle = 0.5, [0.2, 0.2, 0.2, 1.0], 'solid', 'butt'
    self.visibilityResidLine = True
    self.zorderResidLine = 1
    self.legendVisible = True
    self.legendPlacement = 'best'
    self.placementStyles = 'best;upper right;upper left;lower left;lower right;right;center left;center right;lower center;upper center;center;custom'.split(';')
    self.legendX, self.legendY = 0.5, 0.5
    self.legendColor = {'face':[1.0, 1.0, 1.0, 0.5], 'edge':[0.2, 0.2, 0.2, 1.0]}
    self.legendEdgeWidth = 0.5
    self.legendShadow = False
    self.legendShadowDeltaX, self.legendShadowDeltaY, self.legendShadowFaceColor = 2, -2, [0.5, 0.5, 0.5, 1.0]
    self.legendLabelColor = [0.0, 0.0, 0.0, 1.0]
    self.legendLabelSize = 14
    self.legendLabelLinespacing = 1.
    self.legendLabelWeight, self.legendLabelStyle = 'normal', 'normal'
    self.legendLabelFont = 'DejaVu Sans'
    self.legendNumPoints = 1
    self.legendMarkerFirst = True
    self.legendNumCol = 1
    self.legendMarkerScale = 1.0
    self.legendBorderPad = 0.4
    self.legendLabelSpacing = 0.4
    self.legendLabelPad = 1.
    self.legendColumnSpacing = 2
    self.legendFill, self.legendEdge = True, True
    self.legendHatch, self.legendHatchMultiply, self.legendHatchLinewidth, self.legendRounding = '', 1, 1., 0.2
    self.xkcd = False
    self.xkcdScale, self.xkcdLength, self.xkcdRandomness = 1.0, 100.0, 2.0
    self.xkcdStoreFonts = ['DejaVu Sans']
    self.applyPathStroke = False
    self.pathStrokeWidth, self.pathStrokeColor = 2.0, [1.0, 1.0, 1.0, 1.0]
    self.applyPathShadow = False
    self.pathShadowX, self.pathShadowY, self.pathShadowColor, self.pathShadowAlpha = 2, -2, [0.4, 0.4, 0.4, 0.5], 0.5
    self.pathRhoCheck, self.pathRho = True, 0.5
    self.handleArrow = {'x': None, 'y': None, 'y2': None}
    self.handleArrowResid = {'x': None, 'y': None}
    self.handleArrow2 = {'x': None, 'y': None, 'y2': None}
    self.handleArrowResid2 = {'x': None, 'y': None}
    self.arrowVisible = {'x': False, 'y': False, 'y2': False}
    self.arrowOverhang = {'x': 0.1, 'y': 0.1, 'y2': 0.1}
    self.arrowColor = {'x': [0.2, 0.2, 0.2, 1.0], 'y': [0.2, 0.2, 0.2, 1.0], 'y2': [0.2, 0.2, 0.2, 1.0]}
    self.arrowFill = {'x': [0.2, 0.2, 0.2, 1.0], 'y': [0.2, 0.2, 0.2, 1.0], 'y2': [0.2, 0.2, 0.2, 1.0]}
    self.arrowHeadLength = {'x': 0.2, 'y': 0.2, 'y2': 0.2}
    self.arrowHeadWidth = {'x': 0.2, 'y': 0.2, 'y2': 0.2}
    self.arrowEdge = {'x': 0.5, 'y': 0.5, 'y2': 0.5}
    self.arrowOffset = {'x': 0.2, 'y': 0.2, 'y2': 0.2}
    self.arrowEdgeShow = {'x': True, 'y': True, 'y2': True}
    self.arrowFillShow = {'x': True, 'y': True, 'y2': True}
    self.arrowLocation = {'x': 'bottom', 'y': 'left', 'y2': 'right'}
    self.arrowZ = {'x': 'axis', 'y': 'axis', 'y2': 'axis'}
    self.tickLabelData = False
    self.cursorVisible = False
    self.cursor = None
    self.pickedExtra, self.pickedAxes, self.pickedBackground, self.pickedMode, self.inAxes = None, None, None, 0, None
    self.pickedLegend = False
    
    # second y axis
    self.minY2, self.maxY2 = 0.0, 1.0
    self.modeY2 = 'linear'
    self.autoScaleY2 = True
    self.ticksY2 = []
    self.ticksY2Show = True
    self.ticksY2Auto = True
    self.ticksY2Color = [0.2, 0.2, 0.2, 1.0]
    self.ticksY2Size = 12.0
    self.ticksY2Weight = 'normal'
    self.ticksY2Style = 'normal'
    self.ticksY2Angle = 0.0
    self.ticksY2Alignment = 'left'
    self.ticksY2AlignmentVertical = 'center'
    self.ticksY2Linespacing = 1.
    self.ticksY2Pad, self.ticksY2Pad2 = 4.0, 0.0
    self.ticksY2Label, self.ticksY2DataSet = [], -1
    self.labelY2 = 'y'
    self.labelY2Show = True
    self.labelY2Color = [0.2, 0.2, 0.2, 1.0]
    self.labelY2Size = 14.0
    self.labelY2Weight = 'normal'
    self.labelY2Style = 'italic'
    self.labelY2Variant = 'normal'
    self.labelY2Pad = 4.0
    self.labelY2Pos = 0.5
    self.labelY2Angle = 90.0
    self.labelY2Alignment = 'center'
    self.labelY2AlignmentVertical = 'top'
    self.labelY2Linespacing = 1.
    
    self.splitShow, self.splitFraction, self.splitPad = False, 1.0, 0.1
    self.splitDivider, self.splitDividerLength, self.splitDividerWidth, self.splitDividerLocation = True, 0.07, 0.5, 'both'
    self.splitDividerColor, self.splitDividerDashStyle, self.splitDividerAngle = [0.2, 0.2, 0.2, 1.0], 'round', 60
    self.handleDivider, self.handleDividerResid, self.handleDividerY = None, None, None
    self.minX_div, self.maxX_div, self.modeX_div = 0, 1.0, 'linear'
    self.innerAxes, self.innerTicks = False, False
    self.ticksX_div, self.ticksXLabel_div, self.ticksXAuto_div, self.ticksXShow_div, self.ticksXShow_resid_div = [], [], True, True, True
    self.ticksXFormat_div, self.ticksXFormatPrecision_div, self.fallback_ticksXFormat_div, self.ticksXFormatTrailZero_div, self.ticksXFormatSeparator_div, self.ticksXFormatComma_div = 'default', 2, None, False, False, False
    self.ticksXFormatPrefix_div, self.ticksXFormatPostfix_div = '', ''
    self.cursor_div, self.cursor2, self.cursor2_div = None, None, None
    self.autoScaleX_div = True
    self.ticksXMinor_div = 2
    
    self.canvasGradientVisible = False
    self.canvasGradient = []
    self.canvasGradientStyle = 'linear'
    self.canvasGradientColor1, self.canvasGradientColor2 = [0.8, 0.8, 0.8, 1.0], [0.2, 0.2, 0.2, 1.0]
    self.canvasGradientAngle, self.canvasGradientWidth, self.canvasGradientCenter = 0.0, 0.5, [0.5, 0.5]
    
    # controls for split y axis
    self.splitY = False
    self.splitYFraction, self.splitYPad = 1.0, 0.03
    self.innerAxesY, self.innerTicksY = False, False
    self.splitYDivider, self.splitYDividerLength, self.splitYDividerWidth, self.splitYDividerLocation = True, 0.07, 0.5, 'both'
    self.splitYDividerColor, self.splitYDividerDashStyle, self.splitYDividerAngle = [0.2, 0.2, 0.2, 1.0], 'round', 30
    self.splitYBackup = {}
    
    # parameters for second x axes labels
    self.secondXLines, self.secondXLabels = [], []
    self.secondXLines_div, self.secondXLabels_div = [], []
    self.secondXLinesResid, self.secondXLabelsResid = [], []
    self.secondXLinesResid_div, self.secondXLabelsResid_div = [], []
    self.slavedX = False
    self.handleSlavedXLabel, self.handleSlavedXLabelResid = None, None
    self.slavedXLabel, self.slavedXLabelShow = 'transformed x', True
    self.slavedXTransform = []
    self.slavedLabelXColor, self.slavedLabelXSize, self.slavedLabelXWeight, self.slavedLabelXStyle = [0.2, 0.2, 0.2, 1.0], 14., 'normal', 'italic'
    self.slavedLabelXVariant, self.slavedLabelXPad, self.slavedLabelXLinespacing, self.slavedLabelXPos = 'normal', 4., 1., .5
    self.slavedLabelXAngle, self.slavedLabelXAlignment, self.slavedLabelXAlignmentVertical = 0.0, 'center', 'bottom'
    self.slavedTicksXMinor, self.slavedTicksXMinorRelativeLength = 2, 0.5
    self.slavedTicksXColor, self.slavedTicksXSize, self.slavedTicksXWeight, self.slavedTicksXStyle = [0.2, 0.2, 0.2, 1.0], 12., 'normal', 'normal'
    self.slavedTicksXAngle, self.slavedTicksXAlignment, self.slavedTicksXAlignmentVertical = 0., 'center', 'bottom'
    self.slavedTicksXLinespacing, self.slavedTicksXPad, self.slavedTicksXPad2 = 1.0, 4.0, 0.
    self.slavedXFormat, self.slavedXFormatPrecision, self.slavedXFormatTrailZero, self.slavedXFormatSeparator = 'default', 2, False, False
    self.slavedXFormatComma, self.slavedXFormatPrefix, self.slavedXFormatPostfix = False, '', ''
    self.slavedXFormatter, self.fallback_slavedXFormat = None, None
    self.slavedTicksX, self.slavedTicksXAuto = [], True
    
    # initialize values for toggleCrossHairEncore()
    self.toggleShift, self.toggleCtrl = False, False
    
    # interactive drawing of freehand shape
    self.drawFreehand = False
    self.pick2Freehand = False
    
    # store UI style here (does not really belong but put here for convenience to allow style updating)
    self.themeUI = 'default'
    
    # temporarily store selector and cursors to resolve blit shenanigans under recent matplotlib versions
    self.selector2Restore, self.cursors2Restore = None, []

    # generate copies of certain settings for improved drawing control for resid window
    items = 'axisVisible,axisWidth,axisStyle,axisDashStyle,axisPosition,axisPositionValue,axisBoundaryCheck,axisBoundary,axisColor,labelX,labelY,labelXShow,labelYShow'.split(',')
    items.extend('labelXColor,labelYColor,labelXSize,labelYSize,labelXWeight,labelYWeight,labelXStyle,labelYStyle,labelXVariant,labelYVariant'.split(','))
    items.extend('labelXPad,labelYPad,labelXPos,labelYPos,labelXAlignment,labelYAlignment,labelXAlignmentVertical,labelYAlignmentVertical,labelXLinespacing,labelYLinespacing,labelXAngle,labelYAngle,axisFont'.split(','))
    items.extend('tickFont,ticksVisible,ticksWidth,ticksLength,ticksColor,ticksDirection,ticksLabelShow'.split(','))
    items.extend('ticksXShow,ticksYShow,ticksXColor,ticksYColor,ticksXSize,ticksYSize,ticksXWeight,ticksYWeight,ticksXStyle,ticksYStyle'.split(','))
    items.extend('ticksXAngle,ticksYAngle,ticksXMinorRelativeLength'.split(','))
    items.extend('ticksXAlignment,ticksYAlignment,ticksXAlignmentVertical,ticksYAlignmentVertical,ticksXLinespacing,ticksYLinespacing,ticksXPad,ticksYPad,ticksXPad2,ticksYPad2'.split(','))
    items.extend('gridVisible,gridWidth,gridStyle,gridDashStyle,gridColor,gridOrder,padSize,frameWidth,frameColor,frameStyle,frameDashStyle,frameDraw'.split(','))
    items.extend('gridMinorVisible,gridMinorWidth,gridMinorStyle,gridMinorDashStyle,gridMinorColor,gridMinorOrder,gridRectVisible,gridRectColor'.split(','))
    items.extend('canvasGradientStyle,canvasGradientColor1,canvasGradientColor2,canvasGradientAngle,canvasGradientWidth,canvasGradientCenter'.split(','))
    items.extend('arrowEdgeShow,arrowFillShow,arrowLocation,arrowZ'.split(','))
    items.extend('slavedX,slavedXLabel,slavedXLabelShow,slavedXTransform'.split(','))
    items.extend('slavedLabelXColor,slavedLabelXSize,slavedLabelXWeight,slavedLabelXStyle'.split(','))
    items.extend('slavedLabelXVariant,slavedLabelXPad,slavedLabelXLinespacing,slavedLabelXPos'.split(','))
    items.extend('slavedLabelXAngle,slavedLabelXAlignment,slavedLabelXAlignmentVertical'.split(','))
    items.extend('slavedTicksXMinor,slavedTicksXMinorRelativeLength,slavedTicksXColor,slavedTicksXSize,slavedTicksXWeight'.split(','))
    items.extend('slavedTicksXStyle,slavedTicksXAngle,slavedTicksXAlignment,slavedTicksXAlignmentVertical'.split(','))
    items.extend('slavedTicksXLinespacing,slavedTicksXPad,slavedTicksXPad2'.split(','))
    items.extend('slavedXFormat,slavedXFormatPrecision,slavedXFormatTrailZero,slavedXFormatSeparator'.split(','))
    items.extend('slavedXFormatComma,slavedXFormatPrefix,slavedXFormatPostfix'.split(','))
    for entry in items:
      self.__dict__[entry + '_resid'] = deepcopy(self.__dict__[entry])

    if('bmh' in matplotlib.style.available):
      self.stylemodel = 'bmh'
    else:
      self.stylemodel = matplotlib.style.available[0]
      
    # pick event handling
    self.lastPickEvent = None
            
  def buildRessource(self):
    # set up matplotlib and canvas
    self.vLayout_master = QtWidgets.QVBoxLayout(self)
    self.vLayout_master.setContentsMargins(0, 0, 0, 0)

    self.masterwidget = QtWidgets.QSplitter(QtCore.Qt.Orientation.Vertical)
    self.vLayout_master.addWidget(self.masterwidget)
    self.masterwidget.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
    self.masterwidget.setChildrenCollapsible(False)
    self.masterwidget.setOpaqueResize(False)
    
    self.mainPlotBox = QWidgetMac()
    self.masterwidget.addWidget(self.mainPlotBox)
    self.hLayout = QtWidgets.QHBoxLayout(self.mainPlotBox)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(1)
    
    # controls for y-axis
    self.yControlBox = QWidgetMac(self)
    self.yControlBox.setGeometry(QtCore.QRect(0, 0, int(50 * SCALEFONT), 500))
    self.yControlBox.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), 16777215))
    self.yControlBox.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(200 * SCALEFONT)))

    self.hLayout.addWidget(self.yControlBox)
    self.vLayout = QtWidgets.QVBoxLayout(self.yControlBox)
    self.vLayout.setContentsMargins(0, 0, 0, 0)

    self.SpacerBox0 = QWidgetMac(self)
    self.SpacerBox0.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.SpacerBox0.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.vLayout.addWidget(self.SpacerBox0)
    
    # controls for main plot
    self.yControlsPlotContainer = QWidgetMac(self)
    self.vLayout.addWidget(self.yControlsPlotContainer)
    self.LayoutYControlsPlotContainer = QtWidgets.QVBoxLayout(self.yControlsPlotContainer)
    self.LayoutYControlsPlotContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutYControlsPlotContainer.setSpacing(1)
    
    self.autoScaleBoxY = QWidgetMac(self)
    self.autoScaleBoxY.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.autoScaleBoxY.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_ScaleBoxY = QtWidgets.QHBoxLayout(self.autoScaleBoxY)
    self.Layout_ScaleBoxY.setContentsMargins(0, 0, 0, 0)
    self.Layout_ScaleBoxY.setSpacing(1)
    self.Layout_ScaleBoxY.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft | QtCore.Qt.AlignmentFlag.AlignTop)
    self.autoScaleButtonY = QPushButtonMac()
    self.autoScaleButtonY.setText('Auto')
    self.autoScaleButtonY.setToolTip('Scale y axis to current data set and/or curve')
    self.autoScaleButtonY.setMinimumHeight(int(BASE_SIZE))
    self.autoScaleButtonY.setMaximumHeight(int(BASE_SIZE))
    self.autoScaleButtonY.clicked.connect(partial(self.doAutoScale, 'y', True))
    self.Layout_ScaleBoxY.addWidget(self.autoScaleButtonY)
    self.autoScaleCheckY = QPushButtonCheckable()
    self.autoScaleCheckY.setToolTip('Automatically scale y axis')
    self.autoScaleCheckY.setCheckMe(True)
    self.autoScaleCheckY.setChecked(self.autoScaleY)
    self.autoScaleCheckY.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.autoScaleCheckY.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.autoScaleCheckY.clicked.connect(partial(self.setAutoScale, 'y'))
    self.Layout_ScaleBoxY.addWidget(self.autoScaleCheckY)
    self.LayoutYControlsPlotContainer.addStretch()
    self.LayoutYControlsPlotContainer.addWidget(self.autoScaleBoxY)
    
    self.upperLimity = QLineEditClick()
    self.upperLimity.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.upperLimity.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.upperLimity.setValidator(self.validFloat)
    self.upperLimity.setText(str(self.parent.formatNumber(self.maxY)))
    self.upperLimity.setToolTip('Upper limit of y axis')
    self.upperLimity.editingFinished.connect(partial(self.changeAxisLimits, 'y', 'plot', True))
    self.LayoutYControlsPlotContainer.addStretch()
    self.LayoutYControlsPlotContainer.addWidget(self.upperLimity)

    self.modeSelectory = QComboBoxMac()
    self.modeSelectory.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.modeSelectory.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.LayoutYControlsPlotContainer.addStretch()
    self.LayoutYControlsPlotContainer.addWidget(self.modeSelectory)
    self.modeSelectory.addItem('linear')
    self.modeSelectory.addItem('log')
    self.modeSelectory.currentIndexChanged.connect(partial(self.changeAxisMode, 'y', True))
    self.modeSelectory.setToolTip('Set scale of y axis')
    
    self.lowerLimity = QLineEditClick()
    self.lowerLimity.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.lowerLimity.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.lowerLimity.setValidator(self.validFloat)
    self.lowerLimity.setText(str(self.parent.formatNumber(self.minY)))
    self.lowerLimity.setToolTip('Lower limit of y axis')
    self.lowerLimity.editingFinished.connect(partial(self.changeAxisLimits, 'y', 'plot', True))
    self.LayoutYControlsPlotContainer.addStretch()
    self.LayoutYControlsPlotContainer.addWidget(self.lowerLimity)

    self.flipAxisButtonY = QPushButtonCheckable()
    self.flipAxisButtonY.setText('Flip')
    self.flipAxisButtonY.setToolTip('Swap upper and lower limits of y axis')
    self.flipAxisButtonY.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonY.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonY.clicked.connect(partial(self.flipAxis, 'y', redraw=True))
    self.LayoutYControlsPlotContainer.addStretch()
    self.LayoutYControlsPlotContainer.addWidget(self.flipAxisButtonY)

    self.LayoutYControlsPlotContainer.addStretch()
    self.splitYButton = QPushButtonCheckable()
    self.splitYButton.setText('Split')
    self.splitYButton.setToolTip('Toggle split y axis')
    self.splitYButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.splitYButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.splitYButton.setChecked(self.splitY)
    self.LayoutYControlsPlotContainer.addWidget(self.splitYButton)
    self.splitYButton.clicked.connect(self.toggleSplitYCallback)
    blah = HLine()
    self.LayoutYControlsPlotContainer.addWidget(blah)

    # middle box
    self.mainMiddleBox = QWidgetMac(self)
    self.hLayout.addWidget(self.mainMiddleBox)
    self.vLayout_mainMiddleBox = QtWidgets.QVBoxLayout(self.mainMiddleBox)
    self.vLayout_mainMiddleBox.setContentsMargins(0, 0, 0, 0)
    self.vLayout_mainMiddleBox.setSpacing(1)
    
    # horizontal ruler
    self.horizontalRulerBox = QWidgetMac(self)
    self.horizontalRulerBox.setMinimumHeight(int(BASE_SIZE))
    self.horizontalRulerBox.setMaximumHeight(int(BASE_SIZE))
    self.vLayout_mainMiddleBox.addWidget(self.horizontalRulerBox)
    self.LayoutHorizontalRulerBox = QtWidgets.QHBoxLayout(self.horizontalRulerBox)
    self.LayoutHorizontalRulerBox.setContentsMargins(0, 0, 0, 0)
    self.LayoutHorizontalRulerBox.setSpacing(1)
    
    self.horizontalRuler = Ruler(parent=self, mode=0)
    self.horizontalRuler.setMinimumHeight(int(BASE_SIZE))
    self.horizontalRuler.setMaximumHeight(int(BASE_SIZE))
    self.LayoutHorizontalRulerBox.addWidget(self.horizontalRuler)
    
    self.SpacerBox1 = QWidgetMac(self)
    self.SpacerBox1.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
    self.SpacerBox1.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
    self.LayoutHorizontalRulerBox.addWidget(self.SpacerBox1)

    # container for plot and residuals
    self.plotContainer = ShrinkoWidget(notify=self, parent=self)
    self.LayoutPlotContainer = QtWidgets.QHBoxLayout(self.plotContainer)
    self.LayoutPlotContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutPlotContainer.setSpacing(1)
    self.vLayout_mainMiddleBox.addWidget(self.plotContainer)
    
    # the actual matplotlib canvas
    self.matplot = plt.figure()
    self.originalPlotDPI = self.matplot.get_dpi()
    zoomDifference = [abs(int(self.originalPlotDPI) - i) for i in self.parent.zoomLevels]
    zoomIndex = zoomDifference.index(min(zoomDifference))
    self.matplot.set_dpi(self.parent.zoomLevels[zoomIndex])
    self.dataplotwidget = MyFigureCanvas(self.matplot, self, 'plot')
    self.LayoutPlotContainer.addWidget(self.dataplotwidget)

    # vertical ruler
    self.verticalRuler = Ruler(parent=self, mode=1)
    self.verticalRuler.setMinimumWidth(int(BASE_SIZE))
    self.verticalRuler.setMaximumWidth(int(BASE_SIZE))
    self.LayoutPlotContainer.addWidget(self.verticalRuler)
 
    # controls for resid plot
    self.residPlotBox = ShrinkoWidget(notify=self, notifyResid=True, parent=self)
    self.masterwidget.addWidget(self.residPlotBox)
    self.hLayout_resid = QtWidgets.QHBoxLayout(self.residPlotBox)
    self.hLayout_resid.setContentsMargins(0, 0, 0, 0)
    self.hLayout_resid.setSpacing(1)
    
    # on Windows and Linux add markers for splitter
    if(not (platform == 'darwin')):
      self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='horizontal', number=1, repeat=5)

    self.yControlsResidContainer = QWidgetMac(self)
    self.yControlsResidContainer.setMaximumWidth(int(50 * SCALEFONT))
    self.hLayout_resid.addWidget(self.yControlsResidContainer)
    self.LayoutYControlsResidContainer = QtWidgets.QVBoxLayout(self.yControlsResidContainer)
    self.LayoutYControlsResidContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutYControlsResidContainer.setSpacing(1)
    
    self.upperLimitResidy = QLineEditClick()
    self.upperLimitResidy.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.upperLimitResidy.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.upperLimitResidy.setValidator(self.validFloat)
    self.upperLimitResidy.setText(str(self.parent.formatNumber(self.maxResidY)))
    self.upperLimitResidy.setToolTip('Upper limit of residuals y axis')
    self.upperLimitResidy.editingFinished.connect(partial(self.changeAxisLimits, 'y', 'resid', True))
    self.LayoutYControlsResidContainer.addWidget(self.upperLimitResidy)
    
    self.autoScaleButtonResidY = QPushButtonMac()
    self.autoScaleButtonResidY.setText('Auto')
    self.autoScaleButtonResidY.setToolTip('Scale y axis to current residuals object')
    self.autoScaleButtonResidY.setMinimumHeight(int(BASE_SIZE))
    self.autoScaleButtonResidY.setMaximumHeight(int(BASE_SIZE))
    self.autoScaleButtonResidY.clicked.connect(partial(self.doAutoScale, 'resid', True))
    self.LayoutYControlsResidContainer.addWidget(self.autoScaleButtonResidY)

    self.lowerLimitResidy = QLineEditClick()
    self.lowerLimitResidy.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.lowerLimitResidy.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.lowerLimitResidy.setValidator(self.validFloat)
    self.lowerLimitResidy.setText(str(self.parent.formatNumber(self.minResidY)))
    self.lowerLimitResidy.setToolTip('Lower limit of residuals y axis')
    self.lowerLimitResidy.editingFinished.connect(partial(self.changeAxisLimits, 'y', 'resid', True))
    self.LayoutYControlsResidContainer.addWidget(self.lowerLimitResidy)

    # define the plot for the residuals
    plt.rcParams.update({'svg.fonttype': 'none'})
    self.residplot = plt.figure()
    self.residplot.set_dpi(self.parent.zoomLevels[zoomIndex])

    # residuals middle box
    self.residMiddleBox = QWidgetMac(self)
    self.hLayout_resid.addWidget(self.residMiddleBox)
    self.vLayout_residMiddleBox = QtWidgets.QVBoxLayout(self.residMiddleBox)
    self.vLayout_residMiddleBox.setContentsMargins(0, 0, 0, 0)
    self.vLayout_residMiddleBox.setSpacing(1)
    
    # set up canvas for the residuals
    self.residContainer = QWidgetMac()
    self.vLayout_residMiddleBox.addWidget(self.residContainer)
    self.LayoutResidContainer = QtWidgets.QHBoxLayout(self.residContainer)
    self.LayoutResidContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutResidContainer.setSpacing(1)
    self.residplotwidget = MyFigureCanvas(self.residplot, self, 'resid')
    self.LayoutResidContainer.addWidget(self.residplotwidget)
    
    # vertical ruler resid
    self.verticalRulerResid = Ruler(parent=self, mode=1, resid=1)
    self.verticalRulerResid.setMinimumWidth(int(BASE_SIZE))
    self.verticalRulerResid.setMaximumWidth(int(BASE_SIZE))
    self.hLayout_resid.addWidget(self.verticalRulerResid)
    
    # controls for x-axis
    self.xControlBox = QWidgetMac(self)
    self.xControlBox.setGeometry(QtCore.QRect(0, 0, int(500 * SCALEFONT), int(BASE_SIZE + 1)))
    self.xControlBox.setMaximumSize(QtCore.QSize(16777215, int(BASE_SIZE + 1)))
    self.xControlBox.setMinimumSize(QtCore.QSize(int(200 * SCALEFONT), int(BASE_SIZE + 1)))
    self.xControlBox.setContentsMargins(0, 0, 0, 0)
    self.vLayout_master.addWidget(self.xControlBox)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.xControlBox)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(2)

    self.SpacerBox_xctrl = QWidgetMac(self)
    self.SpacerBox_xctrl.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.SpacerBox_xctrl.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.SpacerBox_xctrl)

    self.clearButton = QPushButtonMac()
    self.clearButton.setText(' Clear Selection')
    self.clearButton.setToolTip('Clear data selection')
    self.clearButton.setMaximumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
    self.clearButton.setMinimumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
    self.clearButton.clicked.connect(self.clearSelection)
    self.clearButton.setIcon(FOM_ICON_DIALOG_CANCEL_BUTTON)
    self.hLayout2.addWidget(self.clearButton)
    self.hLayout2.addStretch()
    self.clearButton.setEnabled(False)

    self.autoScaleBoxX = QWidgetMac(self)
    self.autoScaleBoxX.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.autoScaleBoxX.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.autoScaleBoxX)
    self.Layout_ScaleBoxX = QtWidgets.QHBoxLayout(self.autoScaleBoxX)
    self.Layout_ScaleBoxX.setContentsMargins(0, 0, 0, 0)
    self.Layout_ScaleBoxX.setSpacing(1)
    self.Layout_ScaleBoxX.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.autoScaleButtonX = QPushButtonMac()
    self.autoScaleButtonX.setText('Auto')
    self.autoScaleButtonX.setToolTip('Scale x axis to current data set')
    self.autoScaleButtonX.setMinimumHeight(int(BASE_SIZE))
    self.autoScaleButtonX.setMaximumHeight(int(BASE_SIZE))
    self.autoScaleButtonX.clicked.connect(partial(self.doAutoScale, 'x', True))
    self.Layout_ScaleBoxX.addWidget(self.autoScaleButtonX)
    self.autoScaleCheckX = QPushButtonCheckable()
    self.autoScaleCheckX.setToolTip('Automatically scale x axis')
    self.autoScaleCheckX.setCheckMe(True)
    self.autoScaleCheckX.setChecked(self.autoScaleX)
    self.autoScaleCheckX.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.autoScaleCheckX.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.autoScaleCheckX.clicked.connect(partial(self.setAutoScale, 'x'))
    self.Layout_ScaleBoxX.addWidget(self.autoScaleCheckX)

    self.lowerLimitx = QLineEditClick()
    self.lowerLimitx.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.lowerLimitx.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.lowerLimitx.setValidator(self.validFloat)
    self.lowerLimitx.setText(str(self.parent.formatNumber(self.minX)))
    self.lowerLimitx.setToolTip('Lower limit of x axis')
    self.lowerLimitx.editingFinished.connect(partial(self.changeAxisLimits, 'x', 'plot', True))
    self.hLayout2.addWidget(self.lowerLimitx)

    self.modeSelectorx = QComboBoxMac()
    self.modeSelectorx.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.modeSelectorx.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.modeSelectorx)
    self.modeSelectorx.addItem('linear')
    self.modeSelectorx.addItem('log')
    self.modeSelectorx.currentIndexChanged.connect(partial(self.changeAxisMode, 'x', True))
    self.modeSelectorx.setToolTip('Set scale of x axis')
    
    self.upperLimitx = QLineEditClick()
    self.upperLimitx.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.upperLimitx.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.upperLimitx.setValidator(self.validFloat)
    self.upperLimitx.setText(str(self.parent.formatNumber(self.maxX)))
    self.upperLimitx.setToolTip('Upper limit of x axis')
    self.upperLimitx.editingFinished.connect(partial(self.changeAxisLimits, 'x', 'plot', True))
    self.hLayout2.addWidget(self.upperLimitx)
    
    self.flipAxisButtonX = QPushButtonCheckable()
    self.flipAxisButtonX.setText('Flip')
    self.flipAxisButtonX.setToolTip('Swap upper and lower limits of x axis')
    self.flipAxisButtonX.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonX.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonX.clicked.connect(partial(self.flipAxis, 'x', redraw=True))
    self.hLayout2.addWidget(self.flipAxisButtonX)

    self.firstSplitBox = QWidgetMac()
    self.firstSplitBox.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.firstSplitBox.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.firstSplitBox)
    self.LayoutFirstSplitBox = QtWidgets.QHBoxLayout(self.firstSplitBox)
    self.LayoutFirstSplitBox.setContentsMargins(0, 0, 0, 0)

    self.splitAxisButton = QPushButtonCheckable()
    self.splitAxisButton.setText('Split')
    self.splitAxisButton.setToolTip('Toggle split x axis')
    self.splitAxisButton.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.splitAxisButton.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.splitAxisButton.setChecked(self.splitShow)
    self.LayoutFirstSplitBox.addWidget(self.splitAxisButton)
    self.splitAxisButton.clicked.connect(partial(self.toggleSplit, True, True))
    
    # controls for split x-axis
    self.xSplitControlBox = QWidgetMac(self)
    self.xSplitControlBox.setGeometry(QtCore.QRect(0, 0, int(500 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitControlBox.setMaximumSize(QtCore.QSize(16777215, int(BASE_SIZE)))
    self.xSplitControlBox.setMinimumSize(QtCore.QSize(int(200 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitControlBox.setContentsMargins(0, 0, 0, 0)
    self.vLayout_master.addWidget(self.xSplitControlBox)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.xSplitControlBox)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(2)
    self.hLayout3.addStretch()
    
    self.xSplitLabel = QtWidgets.QLabel('split x')
    self.hLayout3.addWidget(self.xSplitLabel)
    
    self.xSplitAutoScaleBoxX = QWidgetMac(self)
    self.xSplitAutoScaleBoxX.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitAutoScaleBoxX.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.xSplitAutoScaleBoxX)
    self.Layout_xSplitScaleBoxX = QtWidgets.QHBoxLayout(self.xSplitAutoScaleBoxX)
    self.Layout_xSplitScaleBoxX.setContentsMargins(0, 0, 0, 0)
    self.Layout_xSplitScaleBoxX.setSpacing(1)
    self.Layout_xSplitScaleBoxX.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.xSplitAutoScaleButtonX = QPushButtonMac()
    self.xSplitAutoScaleButtonX.setText('Auto')
    self.xSplitAutoScaleButtonX.setToolTip('Scale split x axis to current data set')
    self.xSplitAutoScaleButtonX.setMinimumHeight(int(BASE_SIZE))
    self.xSplitAutoScaleButtonX.setMaximumHeight(int(BASE_SIZE))
    self.xSplitAutoScaleButtonX.clicked.connect(partial(self.doAutoScale, 'x2', True))
    self.Layout_xSplitScaleBoxX.addWidget(self.xSplitAutoScaleButtonX)
    self.xSplitAutoScaleCheckX = QPushButtonCheckable()
    self.xSplitAutoScaleCheckX.setToolTip('Automatically scale split x axis')
    self.xSplitAutoScaleCheckX.setCheckMe(True)
    self.xSplitAutoScaleCheckX.setChecked(self.autoScaleX_div)
    self.xSplitAutoScaleCheckX.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.xSplitAutoScaleCheckX.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.xSplitAutoScaleCheckX.clicked.connect(partial(self.setAutoScale, 'x2'))
    self.Layout_xSplitScaleBoxX.addWidget(self.xSplitAutoScaleCheckX)

    self.xSplitLowerLimitx = QLineEditClick()
    self.xSplitLowerLimitx.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitLowerLimitx.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitLowerLimitx.setValidator(self.validFloat)
    self.xSplitLowerLimitx.setText(str(self.parent.formatNumber(self.minX_div)))
    self.xSplitLowerLimitx.setToolTip('Lower limit of split x axis')
    self.xSplitLowerLimitx.editingFinished.connect(partial(self.changeAxisLimits, 'x2', 'plot', True))
    self.hLayout3.addWidget(self.xSplitLowerLimitx)

    self.xSplitModeSelectorx = QComboBoxMac()
    self.xSplitModeSelectorx.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitModeSelectorx.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.xSplitModeSelectorx)
    self.xSplitModeSelectorx.addItem('linear')
    self.xSplitModeSelectorx.addItem('log')
    self.xSplitModeSelectorx.currentIndexChanged.connect(partial(self.changeAxisMode, 'x2', True))
    self.xSplitModeSelectorx.setToolTip('Set scale of split x axis')
    
    self.xSplitUpperLimitx = QLineEditClick()
    self.xSplitUpperLimitx.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitUpperLimitx.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.xSplitUpperLimitx.setValidator(self.validFloat)
    self.xSplitUpperLimitx.setText(str(self.parent.formatNumber(self.maxX_div)))
    self.xSplitUpperLimitx.setToolTip('Upper limit of split x axis')
    self.xSplitUpperLimitx.editingFinished.connect(partial(self.changeAxisLimits, 'x2', 'plot', True))
    self.hLayout3.addWidget(self.xSplitUpperLimitx)

    self.flipAxisButtonX2 = QPushButtonCheckable()
    self.flipAxisButtonX2.setText('Flip')
    self.flipAxisButtonX2.setToolTip('Swap upper and lower limits of split x axis')
    self.flipAxisButtonX2.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonX2.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonX2.clicked.connect(partial(self.flipAxis, 'x2', redraw=True))
    self.hLayout3.addWidget(self.flipAxisButtonX2)

    self.altSplitBox = QWidgetMac()
    self.altSplitBox.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.altSplitBox.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.altSplitBox)
    self.LayoutAltSplitBox = QtWidgets.QHBoxLayout(self.altSplitBox)
    self.LayoutAltSplitBox.setContentsMargins(0, 0, 0, 0)

    # controls for secondAxes
    self.secondAxesContainer = QWidgetMac(self)
    self.secondAxesContainer.setGeometry(QtCore.QRect(0, 0, int(50 * SCALEFONT + 1), 500))
    self.secondAxesContainer.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT + 1), 16777215))
    self.secondAxesContainer.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT + 1), 200))
    self.hLayout.addWidget(self.secondAxesContainer)
    self.LayoutSecondAxesContainer = QtWidgets.QVBoxLayout(self.secondAxesContainer)
    self.LayoutSecondAxesContainer.setContentsMargins(0, 1, 0, 0)
    self.LayoutSecondAxesContainer.setSpacing(1)

    self.SpacerBox3 = QWidgetMac(self)
    self.SpacerBox3.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.SpacerBox3.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.LayoutSecondAxesContainer.addWidget(self.SpacerBox3)
    
    self.secondAxesControlContainer = QWidgetMac(self)
    self.LayoutSecondAxesContainer.addWidget(self.secondAxesControlContainer)
    self.LayoutSecondAxesControlContainer = QtWidgets.QVBoxLayout(self.secondAxesControlContainer)
    self.LayoutSecondAxesControlContainer.setContentsMargins(0, 0, 0, 0)
    
    self.LayoutSecondAxesControlContainer.addStretch()
    self.secondAutoScaleBoxY = QWidgetMac(self)
    self.secondAutoScaleBoxY.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.secondAutoScaleBoxY.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_secondScaleBoxY = QtWidgets.QHBoxLayout(self.secondAutoScaleBoxY)
    self.Layout_secondScaleBoxY.setContentsMargins(0, 0, 0, 0)
    self.Layout_secondScaleBoxY.setSpacing(1)
    self.Layout_secondScaleBoxY.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.secondAutoScaleButtonY = QPushButtonMac()
    self.secondAutoScaleButtonY.setText('Auto')
    self.secondAutoScaleButtonY.setToolTip('Scale second y axis to current data set and/or curve')
    self.secondAutoScaleButtonY.setMinimumHeight(int(BASE_SIZE))
    self.secondAutoScaleButtonY.setMaximumHeight(int(BASE_SIZE))
    self.secondAutoScaleButtonY.clicked.connect(partial(self.doAutoScale, 'y2', True))
    self.Layout_secondScaleBoxY.addWidget(self.secondAutoScaleButtonY)
    self.secondAutoScaleCheckY = QPushButtonCheckable()
    self.secondAutoScaleCheckY.setToolTip('Automatically scale second y axis')
    self.secondAutoScaleCheckY.setCheckMe(True)
    self.secondAutoScaleCheckY.setChecked(self.autoScaleY2)
    self.secondAutoScaleCheckY.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.secondAutoScaleCheckY.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.secondAutoScaleCheckY.clicked.connect(partial(self.setAutoScale, 'y2'))
    self.Layout_secondScaleBoxY.addWidget(self.secondAutoScaleCheckY)
    self.LayoutSecondAxesControlContainer.addWidget(self.secondAutoScaleBoxY)

    self.secondUpperLimit = QLineEditClick()
    self.secondUpperLimit.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.secondUpperLimit.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.secondUpperLimit.setValidator(self.validFloat)
    self.secondUpperLimit.setText(str(self.parent.formatNumber(self.maxY2)))
    self.secondUpperLimit.setToolTip('Upper limit of second y axis')
    self.secondUpperLimit.editingFinished.connect(partial(self.changeAxisLimits, 'y2', 'plot', True))
    self.LayoutSecondAxesControlContainer.addStretch()
    self.LayoutSecondAxesControlContainer.addWidget(self.secondUpperLimit)

    self.secondModeSelector = QComboBoxMac()
    self.secondModeSelector.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.secondModeSelector.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.LayoutSecondAxesControlContainer.addStretch()
    self.LayoutSecondAxesControlContainer.addWidget(self.secondModeSelector)
    self.secondModeSelector.addItem('linear')
    self.secondModeSelector.addItem('log')
    self.secondModeSelector.currentIndexChanged.connect(partial(self.changeAxisMode, 'y2', True))
    self.secondModeSelector.setToolTip('Set scale of second y axis')
    
    self.secondLowerLimit = QLineEditClick()
    self.secondLowerLimit.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.secondLowerLimit.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.secondLowerLimit.setValidator(self.validFloat)
    self.secondLowerLimit.setText(str(self.parent.formatNumber(self.minY2)))
    self.secondLowerLimit.setToolTip('Lower limit of second y axis')
    self.secondLowerLimit.editingFinished.connect(partial(self.changeAxisLimits, 'y2', 'plot', True))
    self.LayoutSecondAxesControlContainer.addStretch()
    self.LayoutSecondAxesControlContainer.addWidget(self.secondLowerLimit)
    
    self.flipAxisButtonY2 = QPushButtonCheckable()
    self.flipAxisButtonY2.setText('Flip')
    self.flipAxisButtonY2.setToolTip('Swap upper and lower limits of second y axis')
    self.flipAxisButtonY2.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonY2.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.flipAxisButtonY2.clicked.connect(partial(self.flipAxis, 'y2', redraw=True))
    self.LayoutSecondAxesControlContainer.addStretch()
    self.LayoutSecondAxesControlContainer.addWidget(self.flipAxisButtonY2)
    
    self.LayoutSecondAxesControlContainer.addStretch()
    self.splitYSpacer = QWidgetMac(self)
    self.splitYSpacer.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.splitYSpacer.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.LayoutSecondAxesControlContainer.addWidget(self.splitYSpacer)
    blah = HLine()
    self.LayoutSecondAxesControlContainer.addWidget(blah)    

    # controls for display toggle buttons of data, curve, and extra
    self.toggleDataButtons, self.toggleFitButtons, self.toggleExtraButtons = [], [], []
    self.currentActiveData, self.currentActiveFit = 0, 0
    ###divider = VLine()
    ###self.hLayout.addWidget(divider)
    self.toggloContainer = QtWidgets.QScrollArea()
    self.toggloContainer.setWidgetResizable(True)
    self.hLayout.addWidget(self.toggloContainer)

    self.toggloContainerInner = QWidgetMac()
    self.LayoutToggloContainerInner = QtWidgets.QHBoxLayout(self.toggloContainerInner)
    self.LayoutToggloContainerInner.setContentsMargins(0, 1, 0, 0)
    self.LayoutToggloContainerInner.setSpacing(1)
    self.LayoutToggloContainerInner.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.toggloContainer.setWidget(self.toggloContainerInner)

    self.toggloDataContainer = QWidgetMac()
    self.toggloDataContainer.setMaximumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 16777215))
    self.toggloDataContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 200))
    self.LayoutToggloContainerInner.addWidget(self.toggloDataContainer)
    self.LayoutToggloDataContainer = QtWidgets.QVBoxLayout(self.toggloDataContainer)
    self.LayoutToggloDataContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutToggloDataContainer.setSpacing(1)
    self.LayoutToggloDataContainer.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)

    self.toggloFitContainer = QWidgetMac()
    self.toggloFitContainer.setMaximumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 16777215))
    self.toggloFitContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 200))
    self.LayoutToggloContainerInner.addWidget(self.toggloFitContainer)
    self.LayoutToggloFitContainer = QtWidgets.QVBoxLayout(self.toggloFitContainer)
    self.LayoutToggloFitContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutToggloFitContainer.setSpacing(1)
    self.LayoutToggloFitContainer.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)
    
    self.toggloExtraContainer = QWidgetMac()
    self.toggloExtraContainer.setMaximumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 16777215))
    self.toggloExtraContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 200))
    self.LayoutToggloContainerInner.addWidget(self.toggloExtraContainer)
    self.LayoutToggloExtraContainer = QtWidgets.QVBoxLayout(self.toggloExtraContainer)
    self.LayoutToggloExtraContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutToggloExtraContainer.setSpacing(1)
    self.LayoutToggloExtraContainer.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)

    # spacer and toggle controls for residuals
    self.pseudoContainer = QWidgetMac(self)
    self.pseudoContainer.setMaximumWidth(int(50 * SCALEFONT))
    self.hLayout_resid.addWidget(self.pseudoContainer)
    self.LayoutPseudoContainer = QtWidgets.QVBoxLayout(self.pseudoContainer)
    self.LayoutPseudoContainer.setContentsMargins(0, 1, 0, 0)

    spacerBox5 = QWidgetMac(self)
    spacerBox5.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), 25))
    spacerBox5.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), 25))
    self.LayoutPseudoContainer.addWidget(spacerBox5)

    # controls for display toggle buttons of data, curve, and extra
    self.toggleResidButtons = []
    self.toggloContainerResid = QtWidgets.QScrollArea()
    self.toggloContainerResid.setWidgetResizable(True)
    self.hLayout_resid.addWidget(self.toggloContainerResid)

    self.toggloContainerInnerResid = QWidgetMac()
    self.LayoutToggloContainerInnerResid = QtWidgets.QHBoxLayout(self.toggloContainerInnerResid)
    self.LayoutToggloContainerInnerResid.setContentsMargins(0, 0, 0, 0)
    self.LayoutToggloContainerInnerResid.setSpacing(1)
    self.LayoutToggloContainerInnerResid.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.toggloContainerResid.setWidget(self.toggloContainerInnerResid)

    self.toggloResidContainer = QWidgetMac(self)
    self.toggloResidContainer.setMaximumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 16777215))
    self.toggloResidContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), 200))
    self.LayoutToggloContainerInnerResid.addWidget(self.toggloResidContainer)
    self.LayoutToggloResidContainer = QtWidgets.QVBoxLayout(self.toggloResidContainer)
    self.LayoutToggloResidContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutToggloResidContainer.setSpacing(1)
    self.LayoutToggloResidContainer.setAlignment(QtCore.Qt.AlignmentFlag.AlignHCenter|QtCore.Qt.AlignmentFlag.AlignTop)

    self.toggloExtraContainerResid = QWidgetMac()
    self.toggloExtraContainerResid.setMaximumWidth(int(11 * SCALEFONT + 1))
    self.toggloExtraContainerResid.setMinimumWidth(int(11 * SCALEFONT + 1))
    self.LayoutToggloContainerInnerResid.addWidget(self.toggloExtraContainerResid)

    # introduce master buttons that control the display of all current objects
    self.toggleDataMaster = QPushButtonCheckableMini(parent=self, kind='master_data')
    self.toggleDataMaster.alignMeRight = True
    self.toggleDataMaster.setChecked(True)
    self.toggleDataMaster.setText('\u2115')
    self.toggleDataMaster.setToolTip('- Click left to set visibility of data objects\n- Click middle to vary data objects\n- Click right to hide all data objects')
    self.LayoutToggloDataContainer.addWidget(self.toggleDataMaster)
    self.toggleDataMaster.clicked.connect(partial(self.toggloCallbackMaster, 'data', self.toggleDataMaster))
    
    self.toggleFitMaster = QPushButtonCheckableMini(parent=self, kind='master_fit')
    self.toggleFitMaster.alignMeRight = True
    self.toggleFitMaster.setChecked(True)
    self.toggleFitMaster.setText('\u2115')
    self.toggleFitMaster.setToolTip('- Click left to set visibility of curve objects\n- Click middle to vary curve objects\n- Click right to hide all curve objects')
    self.LayoutToggloFitContainer.addWidget(self.toggleFitMaster)
    self.toggleFitMaster.clicked.connect(partial(self.toggloCallbackMaster, 'fit', self.toggleFitMaster))
    
    self.toggleExtraMaster = QPushButtonCheckableMini(parent=self, kind='master_extra')
    self.toggleExtraMaster.alignMeRight = True
    self.toggleExtraMaster.setChecked(True)
    self.toggleExtraMaster.setText('\u2115')
    self.toggleExtraMaster.setToolTip('- Click left to set visibility of extra objects\n- Click right to hide all extra objects')
    self.LayoutToggloExtraContainer.addWidget(self.toggleExtraMaster)
    self.toggleExtraMaster.clicked.connect(partial(self.toggloCallbackMaster, 'extra', self.toggleExtraMaster))
    
    self.toggleResidMaster = QPushButtonCheckableMini(parent=self, kind='master_resid')
    self.toggleResidMaster.alignMeRight = True
    self.toggleResidMaster.setChecked(True)
    self.toggleResidMaster.setText('\u2115')
    self.toggleResidMaster.setToolTip('- Click left to set visibility of residuals objects\n- Click middle to vary residuals objects\n- Click right to hide all residuals objects')
    self.LayoutToggloResidContainer.addWidget(self.toggleResidMaster)
    self.toggleResidMaster.clicked.connect(partial(self.toggloCallbackMaster, 'resid', self.toggleResidMaster))

    self.updateToggloContainer()

    # initially turn off the second axes controls
    self.toggleSecondAxes(state=False)

    # set initial size ratio of QSplitter()
    # this call of no consequence as widget will be resized after creation
    # => readjust in main ui, see below
    ###self.masterwidget.setSizes([4, 1])

  def updateToggloContainer(self):
    # updates push buttons in toggloContainer
    # keep track on whether we need to allow space for display of scrollbars
    needScrollbar = False
    
    # set visbility of data buttons
    for index, entry in enumerate(self.toggleDataButtons):
      if(index < len(self.parent.data)):
        self.toggleDataButtons[index].show()
      else:
        self.toggleDataButtons[index].hide()
    # expand list of data buttons as needed
    while(len(self.toggleDataButtons) < len(self.parent.data)):
      self.toggleDataButtons.append(QPushButtonCheckableMini(parent=self, kind='data'))
      # make button identifiable
      self.toggleDataButtons[-1].alignMeRight = True
      self.LayoutToggloDataContainer.addWidget(self.toggleDataButtons[-1])
      # connect new slot
      self.toggleDataButtons[-1].clicked.connect(partial(self.toggloCallback, 'data', self.toggleDataButtons[-1]))
    # cycle over all buttons
    for index, item in enumerate(self.toggleDataButtons[:len(self.parent.data)]):
      item.setText(str(index + 1))
      item.setChecked(self.parent.data[index].visibility)
      ttstring = '- Click left to set visibility of data object ' + str(index + 1)
      ttstring += '\n- Click middle to modify data object ' + str(index + 1)
      ttstring += '\n- Click right to make data object ' + str(index + 1) + ' active'
      item.setToolTip(ttstring)
    # need to manually adjust size of container as PyQt is not doing it ...
    size = self.toggloDataContainer.sizeHint()
    left, top, right, bottom = self.LayoutToggloDataContainer.getContentsMargins()
    spacing = self.LayoutToggloDataContainer.spacing()
    useHeight = size.height() + top + bottom + spacing * (len(self.parent.data) - 1)
    self.toggloDataContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), useHeight))
    if(useHeight > self.toggloContainer.viewport().height()):
      needScrollbar = True
      
    # set visbility of fit buttons
    for index, entry in enumerate(self.toggleFitButtons):
      if(index < len(self.parent.fit)):
        self.toggleFitButtons[index].show()
      else:
        self.toggleFitButtons[index].hide()
    # expand list of fit buttons as needed
    while(len(self.toggleFitButtons) < len(self.parent.fit)):
      self.toggleFitButtons.append(QPushButtonCheckableMini(parent=self, kind='fit'))
      # make button identifiable
      self.toggleFitButtons[-1].alignMeRight = True
      self.LayoutToggloFitContainer.addWidget(self.toggleFitButtons[-1])
      # connect new slot
      self.toggleFitButtons[-1].clicked.connect(partial(self.toggloCallback, 'fit', self.toggleFitButtons[-1]))
    # cycle over all buttons
    for index, item in enumerate(self.toggleFitButtons[:len(self.parent.fit)]):
      item.setText(str(index + 1))
      item.setChecked(self.parent.fit[index].visibility)
      ttstring = '- Click left to set visibility of curve object ' + str(index + 1)
      ttstring += '\n- Click middle to modify curve object ' + str(index + 1)
      ttstring += '\n- Click right to make curve object ' + str(index + 1) + ' active'
      item.setToolTip(ttstring)
    # need to manually adjust size of container as PyQt is not doing it ...
    size = self.toggloFitContainer.sizeHint()
    left, top, right, bottom = self.LayoutToggloFitContainer.getContentsMargins()
    spacing = self.LayoutToggloFitContainer.spacing()
    useHeight = size.height() + top + bottom + spacing * (len(self.parent.fit) - 1)
    self.toggloFitContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), useHeight))
    if(useHeight > self.toggloContainer.viewport().height()):
      needScrollbar = True

    # set visbility of extra buttons
    for index, entry in enumerate(self.toggleExtraButtons):
      if(index < len(self.parent.extras)):
        self.toggleExtraButtons[index].show()
      else:
        self.toggleExtraButtons[index].hide()
    # expand list of extra buttons as needed
    while(len(self.toggleExtraButtons) < len(self.parent.extras)):
      self.toggleExtraButtons.append(QPushButtonCheckableMini(parent=self, kind='extra'))
      # make button identifiable
      self.toggleExtraButtons[-1].alignMeRight = True
      self.LayoutToggloExtraContainer.addWidget(self.toggleExtraButtons[-1])
      # connect new slot
      self.toggleExtraButtons[-1].clicked.connect(partial(self.toggloCallback, 'extra', self.toggleExtraButtons[-1]))
    # cycle over all buttons
    for index, item in enumerate(self.toggleExtraButtons[:len(self.parent.extras)]):
      item.setText(str(index + 1))
      item.setChecked(self.parent.extras[index].visibility)
      ttstring = '- Click left to set visibility of extra object ' + str(index + 1)
      ttstring += '\n- Click middle to modify extra object ' + str(index + 1)
      item.setToolTip(ttstring)
    # need to manually adjust size of container as PyQt is not doing it ...
    size = self.toggloExtraContainer.sizeHint()
    left, top, right, bottom = self.LayoutToggloExtraContainer.getContentsMargins()
    spacing = self.LayoutToggloExtraContainer.spacing()
    # deal with situation when no extras exist
    useHeight = max(1, size.height() + top + bottom + spacing * (len(self.parent.extras) - 1))
    self.toggloExtraContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), useHeight))
    if(useHeight > self.toggloContainer.viewport().height()):
      needScrollbar = True
    
    # adjust of togglo sidebar as needed
    if(needScrollbar):
      # have to impose max size of scrollbar as geometry returns insanely large value before first display of scollbar
      addon = min(15, self.toggloContainer.verticalScrollBar().geometry().width())
    else:
      addon = 0
    if(len(self.parent.extras)):
      self.toggloExtraContainer.show()
      self.toggloExtraContainerResid.show()
      # have to hard-code the size as layout updating by Qt does not work
      self.toggloContainer.setMaximumWidth(addon + int(40 * SCALEFONT + 1))
      self.toggloContainerResid.setMaximumWidth(addon + int(40 * SCALEFONT + 1))
    else:
      self.toggloExtraContainer.hide()
      self.toggloExtraContainerResid.hide()
      self.toggloContainer.setMaximumWidth(addon + int(26 * SCALEFONT + 1))
      self.toggloContainerResid.setMaximumWidth(addon + int(26 * SCALEFONT + 1))

    # set visbility of resid buttons
    for index, entry in enumerate(self.toggleResidButtons):
      if(index < len(self.parent.data) + 1):
        self.toggleResidButtons[index].show()
      else:
        self.toggleResidButtons[index].hide()
    # expand list of resid buttons as needed
    while(len(self.toggleResidButtons) < len(self.parent.data) + 1):
      self.toggleResidButtons.append(QPushButtonCheckableMini(parent=self, kind='resid'))
      # make button identifiable
      self.toggleResidButtons[-1].alignMeRight = True
      self.LayoutToggloResidContainer.addWidget(self.toggleResidButtons[-1])
      # connect new slot
      self.toggleResidButtons[-1].clicked.connect(partial(self.toggloCallback, 'resid', self.toggleResidButtons[-1]))
    # cycle over all buttons
    for index, item in enumerate(self.toggleResidButtons[:len(self.parent.data) + 1]):
      item.setText(str(index))
      if(index):
        item.setChecked(self.parent.data[index - 1].visibilityResid)
      else:
        item.setChecked(self.visibilityResidLine)
      if(index):
        ttstring = '- Click left to set visibility of residuals object ' + str(index)
        ttstring += '\n- Click middle to modify residuals object ' + str(index)
      else:
        ttstring = '- Click left to set visibility of zero line object'
        ttstring += '\n- Click middle to modify zero line object'
      item.setToolTip(ttstring)

    # need to manually adjust size of container as PyQt is not doing it ...
    size = self.toggloResidContainer.sizeHint()
    left, top, right, bottom = self.LayoutToggloResidContainer.getContentsMargins()
    spacing = self.LayoutToggloResidContainer.spacing()
    useHeight = size.height() + top + bottom + spacing * len(self.parent.data)
    self.toggloResidContainer.setMinimumSize(QtCore.QSize(int(11 * SCALEFONT + 1), useHeight))
    if(useHeight > self.toggloContainerResid.viewport().height()):
      needScrollbar = True

    # update labels
    self.updateToggloActive(activeData=self.parent.activeData, activeFit=self.parent.activeFit)
    
    # update master buttons
    for kind in ['data', 'fit', 'resid']:
      self.updateToggloMaster(kind=kind)
    if(len(self.parent.extras)):
      self.updateToggloMaster(kind='extra')
    
  def updateToggloActive(self, activeData=None, activeFit=None):
    # highlights currently active data, fit and resid objects
    if((activeData != None) and (activeData < len(self.parent.data))):
      # cycle over all data sets and adjust highlight
      for index in range(len(self.parent.data)):
        self.toggleDataButtons[index].setHigh(index==activeData)
        self.toggleResidButtons[index + 1].setHigh(index==activeData)
      
      self.currentActiveData = activeData
    
    if((activeFit != None) and (activeFit < len(self.parent.fit))):
      # cycle over all curves and adjust highlight
      for index in range(len(self.parent.fit)):
        self.toggleFitButtons[index].setHigh(index==activeFit)
      
      self.currentActiveFit = activeFit
      
  def toggloCallback(self, kind='data', button=None):
    # call back function for visibility push buttons
    if(button != None):
      index = int(button.text()) - 1
      if(kind == 'data'):
        if(index < len(self.parent.data)):
          state = self.toggleDataButtons[index].isChecked()
          self.parent.objectsarea.dataSetTable.cellWidget(index, 0).setChecked(state)
          self.parent.objectsarea.toggleVisibilityData(index=index, redraw=True)
      elif(kind == 'fit'):
        if(index < len(self.parent.fit)):
          state = self.toggleFitButtons[index].isChecked()
          self.parent.objectsarea.curvesTable.cellWidget(index, 0).setChecked(state)
          self.parent.objectsarea.toggleVisibilityCurve(index=index, redraw=True)
      elif(kind == 'extra'):
        if(index < len(self.parent.extras)):
          state = self.toggleExtraButtons[index].isChecked()
          self.parent.objectsarea.extrasTable.cellWidget(index, 0).setChecked(state)
          self.parent.objectsarea.toggleVisibilityExtras(index=index, redraw=True)
      elif(kind == 'resid'):
        index += 1
        if(index < len(self.parent.data) + 1):
          state = self.toggleResidButtons[index].isChecked()
          self.parent.objectsarea.residTable.cellWidget(index, 0).setChecked(state)
          self.parent.objectsarea.toggleVisibilityResid(index=index, redraw=True)

  def toggloCallbackMaster(self, kind='data', button=None):
    # call back function for visibility push buttons
    if(button != None):
      flag = False
      state = button.isChecked()
      if(kind == 'data'):
        for index in range(len(self.parent.data)):
          state2 = self.toggleDataButtons[index].isChecked()
          if(state != state2):
            flag = True
            self.parent.objectsarea.dataSetTable.cellWidget(index, 0).setChecked(state)
            self.parent.objectsarea.toggleVisibilityData(index=index, redraw=False)
        if(flag):
          self.dataplotwidget.myRefresh()
      elif(kind == 'fit'):
        for index in range(len(self.parent.fit)):
          state2 = self.toggleFitButtons[index].isChecked()
          if(state != state2):
            flag = True
            self.parent.objectsarea.curvesTable.cellWidget(index, 0).setChecked(state)
            self.parent.objectsarea.toggleVisibilityCurve(index=index, redraw=False)
        if(flag):
          self.dataplotwidget.myRefresh()
      elif(kind == 'extra'):
        for index in range(len(self.parent.extras)):
          state2 = self.toggleExtraButtons[index].isChecked()
          if(state != state2):
            flag = True
            self.parent.objectsarea.extrasTable.cellWidget(index, 0).setChecked(state)
            self.parent.objectsarea.toggleVisibilityExtras(index=index, redraw=False)
        if(flag):
          self.dataplotwidget.myRefresh()
      elif(kind == 'resid'):
        for index in range(len(self.parent.data)):
          state2 = self.toggleResidButtons[index + 1].isChecked()
          if(state != state2):
            flag = True
            self.parent.objectsarea.residTable.cellWidget(index + 1, 0).setChecked(state)
            self.parent.objectsarea.toggleVisibilityResid(index=index + 1, redraw=False)
        if(flag):
          self.residplotwidget.myRefresh()

  def checkToggloItem(self, kind='data', index=0, state=True):
    # used to keep track of visibility changes
    if(kind == 'data'):
      if(index < len(self.parent.data)):
        self.toggleDataButtons[index].setChecked(state)
    elif(kind == 'fit'):
      if(index < len(self.parent.fit)):
        self.toggleFitButtons[index].setChecked(state)
    elif(kind == 'extra'):
      if(index < len(self.parent.extras)):
        self.toggleExtraButtons[index].setChecked(state)
    elif(kind == 'resid'):
      if(index < len(self.parent.data) + 1):
        self.toggleResidButtons[index].setChecked(state)
        
    # update master buttons
    self.updateToggloMaster(kind=kind)
    
  def updateToggloMaster(self, kind='data'):
    # helper function that adjusts master button checked status appropriately according to state of indivdual buttons
    if(kind in ['data', 'fit', 'extra', 'resid']):
      if(kind == 'data'):
        checko = True
        for i in range(len(self.parent.data)):
          if(i):
            checko = checko and (self.toggleDataButtons[0].isChecked() == self.toggleDataButtons[i].isChecked())
        # do all buttons have the same status?
        # if so, make sure that master button has the same status
        if(checko):
          self.toggleDataMaster.setChecked(self.toggleDataButtons[0].isChecked())
      elif(kind == 'fit'):
        checko = True
        for i in range(len(self.parent.fit)):
          if(i):
            checko = checko and (self.toggleFitButtons[0].isChecked() == self.toggleFitButtons[i].isChecked())
        # do all buttons have the same status?
        # if so, make sure that master button has the same status
        if(checko):
          self.toggleFitMaster.setChecked(self.toggleFitButtons[0].isChecked())
      elif(kind == 'extra'):
        checko = True
        for i in range(len(self.parent.extras)):
          if(i):
            checko = checko and (self.toggleExtraButtons[0].isChecked() == self.toggleExtraButtons[i].isChecked())
        # do all buttons have the same status?
        # if so, make sure that master button has the same status
        if(checko):
          self.toggleExtraMaster.setChecked(self.toggleExtraButtons[0].isChecked())
      elif(kind == 'resid'):
        checko = True
        for i in range(len(self.parent.data)):
          if(i):
            checko = checko and (self.toggleResidButtons[1].isChecked() == self.toggleResidButtons[i + 1].isChecked())
        # do all buttons have the same status?
        # if so, make sure that master button has the same status
        if(checko):
          self.toggleResidMaster.setChecked(self.toggleResidButtons[1].isChecked())

  def configureMeHere(self, kind='data', index=0, callButton=None):
    # call back to bring up config menu
    if(kind == 'data'):
      self.parent.objectsarea.changeStyle(self.parent.data[index], True, False, callButton=callButton)
    elif(kind == 'fit'):
      self.parent.objectsarea.changeStyle(self.parent.fit[index], False, False, callButton=callButton)
    elif(kind == 'extra'):
      self.parent.objectsarea.changeStyleExtra(index, callButton=callButton)
    elif(kind == 'resid'):
      index += 1
      if(index > -1):
        self.parent.objectsarea.changeStyle(self.parent.data[index], False, True, callButton=callButton)
      else:
        self.parent.objectsarea.changeResidZeroStyle(callButton=callButton)

  def configureMeHereMyMaster(self, kind='data', callButton=None):
    # call back to bring up config menu
    self.parent.objectsarea.changeStyleMyMaster(callButton=callButton, kind=kind)

  def makeMeActive(self, kind='data', index=0):
    # call back to make item active
    field = None
    if((kind == 'data') and (index != self.parent.activeData)):
      field = self.parent.objectsarea.dataSetTable.cellWidget(index, 1)
    elif((kind == 'fit') and (index != self.parent.activeFit)):
      field = self.parent.objectsarea.curvesTable.cellWidget(index, 1)
    
    if(field != None):
      field.setChecked(True)
      if(kind == 'data'):
        self.updateToggloActive(activeData=index)
      else:
        self.updateToggloActive(activeFit=index)

  def addSplitterHandleSymbols(self, parentHandle=None, direction='horizontal', number=1, repeat=5):
    # adds spacer symbols to splitter handle
    if(parentHandle != None):
      if(direction in ['horizontal', 'vertical']):
        parentHandle.setHandleWidth(5)
        handli = parentHandle.handle(number)
        if(handli != None):
          if(direction == 'horizontal'):
            layoutli = QtWidgets.QHBoxLayout(handli)
          else:
            layoutli = QtWidgets.QVBoxLayout(handli)
          layoutli.addStretch()
          for repeat in range(repeat):
            bogus = QtWidgets.QWidget()
            bogus.setStyleSheet('QWidget {background-color: #888888;border-radius: 2px;}')
            bogus.setMinimumSize(4, 4)
            bogus.setMaximumSize(4, 4)
            layoutli.addWidget(bogus)
          layoutli.addStretch()
          layoutli.setSpacing(3)
          layoutli.setContentsMargins(1, 0, 0, 0)

  def clearSelection(self):
    # callback for clear selection button
    if(hasattr(self.parent, 'resultsarea')):
      self.parent.resultsarea.clearSelection()

  def toggleSecondAxes(self, state=False):
    # toggles visibility of control elements for second axes
    ###if(state):
    if(True):
      self.secondAxesContainer.setVisible(state)
      self.pseudoContainer.setVisible(state)
      if((hasattr(self.parent, 'tabWidget')) and (hasattr(self.parent, 'tab_7'))):
        if(state):
          if(self.parent.tabWidget.indexOf(self.parent.tab_7) < 0):
            self.parent.tabWidget.addTab(self.parent.tab_7, "2nd Axes")
            # need to set again the tool tip upon recreation of tab
            toolText = 'This tab contains options for configuring the second axes graphics.\n(Ctrl-7)'
            if(myapp.toolTipsShow):
              self.parent.tabWidget.setTabToolTip(self.parent.tabWidget.indexOf(self.parent.tab_7), toolText)
            elif(len(self.parent.tabWidget.storedToolTips) <= self.parent.tabWidget.indexOf(self.parent.tab_7)):
              self.parent.tabWidget.storedToolTips.append(toolText)
        else:
          if(self.parent.tabWidget.indexOf(self.parent.tab_7) > -1):
            self.parent.tabWidget.removeTab(self.parent.tabWidget.indexOf(self.parent.tab_7))
      # toggle visibility of plot elements
      if(hasattr(self.parent, 'plotArea')):
        self.toggleSecondAxesItems(state=state)
    # need to solve inner situation -- but check whether plot object is already instantiated
    if(hasattr(self, 'ax')):
      self.updateInnerSituation()
      
  def toggleSecondAxesItems(self, state=False):
    # turns on/off all relevant plot elements of self.ax2
#    if(state):
    if(True):
      self.ax2.spines['right'].set_visible(state and self.axisVisible['right2'])
      self.ax2.yaxis.set_visible(state)
  
      self.ax2_div.spines['left'].set_visible(state and self.axisVisible['left2'])
      self.ax2_div.spines['right'].set_visible(state and self.axisVisible['right2'])
      self.ax2_div.yaxis.set_visible(state)
    
    # delete all grid lines on the second axes
    if(state):
      if(self.gridVisible['y2']):
        self.drawAxisGrid(axis='y2', redraw=False, target='plot')
    else:
      for line in self.gridLinesStore['y2']:
        line.remove()
      self.gridLinesStore['y2'] = []
      
    # deal with axis arrows
    if(state):
      if(self.arrowVisible['y2']):
        # need to redraw arrow as it will likely have been destroyed since last display of 2nd axes
        self.setAxisArrow(state=True, axis='y2', item='all', redraw=False, target='plot')
    else:
      if(self.handleArrow['y2'] != None):
        self.handleArrow['y2'].remove()
        self.handleArrow['y2'] = None
      if(self.handleArrow2['y2'] != None):
        self.handleArrow2['y2'].remove()
        self.handleArrow2['y2'] = None
    
  def isSecondAxesActive(self):
    # checks whether any items are on second axis
    onSecondItems = [1 if (i.onSecondAxes) else 0 for i in (self.parent.data + self.parent.fit + self.parent.extras)]
    if(sum(onSecondItems) or self.splitY):
      return True

    return False

  def toggleSplit(self, redraw=True, toggled=False):
    # toggles on/off split x axis
    state = self.splitAxisButton.isChecked()
    self.splitShow = state
    self.ax_div.set_visible(state)
    self.ax2_div.set_visible(state)
    self.ax_resid_div.set_visible(state)
    if(state):
      # implement check for too large split fraction
      maxSplit = 1 - self.padSize['left'] - (1 - self.padSize['right']) - 0.1
      if(self.splitPad > maxSplit):
        if(hasattr(self.parent, 'statusbar')):
          self.parent.statusbar.showMessage('Had to reduce split x axis padding to ' + self.parent.formatNumber(maxSplit) + ', rather than ' + self.parent.formatNumber(self.splitPad) + '!', self.parent.STATUS_TIME)
        self.splitPad = maxSplit

      if(hasattr(self.parent, 'graphicsarea')):
        self.parent.graphicsarea.configInnerBox.show()
        self.parent.graphicsarea.configInnerTickXBox.show()
        self.parent.graphicsarea.outerBox4b.show()
        self.parent.graphicsarea.configGridBox['x2'].show()
        # check whether advanced graphics enabled
        if(self.parent.graphicsarea.advancedExport['advancedGraphics']):
          if(hasattr(self.parent.graphicsarea, 'configInnerBox2')):
            self.parent.graphicsarea.configInnerBox2.show()
          if(hasattr(self.parent.graphicsarea, 'configInnerBox3')):
            self.parent.graphicsarea.configInnerBox3.show()
          if(hasattr(self.parent.graphicsarea, 'configInnerBox5')):
            self.parent.graphicsarea.configInnerBox5.show()
          if(hasattr(self.parent.graphicsarea, 'configInnerBox6')):
            self.parent.graphicsarea.configInnerBox6.show()
          if(hasattr(self.parent.graphicsarea, 'configInnerMinorTickBox')):
            self.parent.graphicsarea.configInnerMinorTickBox.show()
          self.parent.graphicsarea.configGridBox2['x2'].show()
          self.parent.graphicsarea.configGridBox3['x2'].show()
      
      xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
      # since we abandoned make_axes_locatable, we also need to implement check for split on y axis
      if(self.splitY):
        ybreak = (self.padSize['top'] - self.padSize['bottom'] - self.splitYPad) * self.splitYFraction / (1.0 + self.splitYFraction)
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['left'] + xbreak,  self.padSize['top']]]))
        self.ax_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['right'],  self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['bottom'] + ybreak]]))
        self.ax2_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['bottom'] + ybreak]]))
      else:
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak,  self.padSize['top']]]))
        self.ax_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'],  self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['top']]]))
        self.ax2_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
      self.ax_resid.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['top']]]))
      self.ax_resid_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))

      # if called further up, the following command produces a warning under Linux
      # b/c the underlying call to axis._get_pixel_distance_along_axis() fails
      # => work around: move it here
      self.xSplitControlBox.show()
      self.LayoutAltSplitBox.addWidget(self.splitAxisButton)
      
      # need to ensure correct positioning of second axis label
      if(self.isSecondAxesActive()):
        self.ax2.yaxis.label.set_visible(False)
        self.ax2_div.yaxis.label.set_visible(self.labelY2Show)
      else:
        # hopefully, this kills the mysterious black line
        self.ax2.spines['right'].set_visible(False)
        self.ax2_div.spines['left'].set_visible(False)
      
      # set visibility of inner items correctly
      self.updateInnerSituation()
      
      # set axis mode and limits to match those of first axes
      if(toggled):
        self.xSplitModeSelectorx.blockSignals(True)
        index = self.xSplitModeSelectorx.findText(self.modeX)
        self.xSplitModeSelectorx.setCurrentIndex(index)
        self.xSplitModeSelectorx.blockSignals(False)
        self.changeAxisMode('x2', redraw=False)
      # set axes limits for split effect
      if(toggled):
        # only do this upon first display of split x axes
        if(self.modeX == 'linear'):
          splitXAt = self.minX + 1.0 / (self.splitFraction + 1.0) * (self.maxX - self.minX)
        else:
          splitXAt = np.exp(np.log(self.minX) + 1.0 / (self.splitFraction + 1.0) * (np.log(self.maxX) - np.log(self.minX)))
        self.setAxisLimits(lower=splitXAt, upper=self.maxX, axis='x2', updateLabel=False, target='plot', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.setAxisLimits(lower=splitXAt, upper=self.maxX, axis='x2', updateLabel=False, target='resid', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.xSplitLowerLimitx.setText(self.parent.formatNumber(splitXAt))
        self.xSplitUpperLimitx.setText(self.upperLimitx.text())
        self.setAxisLimits(lower=self.minX, upper=splitXAt, axis='x', updateLabel=False, target='plot', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.setAxisLimits(lower=self.minX, upper=splitXAt, axis='x', updateLabel=False, target='resid', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.upperLimitx.setText(self.parent.formatNumber(splitXAt))

      # need to update tick formatting if split axis is turned on
      for target in ['plot', 'resid']:
        self.setTickOne4All(axis='x', redraw=False, target=target)
    else:
      self.xSplitControlBox.hide()
      self.LayoutFirstSplitBox.addWidget(self.splitAxisButton)
      if(hasattr(self.parent, 'graphicsarea')):
        self.parent.graphicsarea.configInnerBox.hide()
        self.parent.graphicsarea.configInnerTickXBox.hide()
        self.parent.graphicsarea.outerBox4b.hide()
        self.parent.graphicsarea.configGridBox['x2'].hide()
        if(hasattr(self.parent.graphicsarea, 'configInnerBox2')):
          self.parent.graphicsarea.configInnerBox2.hide()
        if(hasattr(self.parent.graphicsarea, 'configInnerBox3')):
          self.parent.graphicsarea.configInnerBox3.hide()
        if(hasattr(self.parent.graphicsarea, 'configInnerBox5')):
          self.parent.graphicsarea.configInnerBox5.hide()
          if(hasattr(self.parent.graphicsarea, 'configInnerBox6')):
            self.parent.graphicsarea.configInnerBox6.hide()
        if(hasattr(self.parent.graphicsarea, 'configInnerMinorTickBox')):
          self.parent.graphicsarea.configInnerMinorTickBox.hide()
        self.parent.graphicsarea.configGridBox2['x2'].hide()
        self.parent.graphicsarea.configGridBox3['x2'].hide()
      
      # since we abandoned make_axes_locatable, we also need to implement check for split on y axis
      if(self.splitY):
        ybreak = (self.padSize['top'] - self.padSize['bottom'] - self.splitYPad) * self.splitYFraction / (1.0 + self.splitYFraction)
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['right'], self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['bottom'] + ybreak]]))
        # this should take care of the obnoxious line randomly popping up in the the middle of the screen
        # not sure why, suspect Matplotlib bug
        # but we can workaround by moving second axes out of screen (in addition to not displaying them)
        # awful fix, very ugly, but cannot be helped
        self.ax_div.set_position(matplotlib.transforms.Bbox([[1.5, self.padSize['bottom'] + ybreak + self.splitYPad], [2.0,  self.padSize['top']]]))
        self.ax2_div.set_position(matplotlib.transforms.Bbox([[1.5, self.padSize['bottom']], [2.0, self.padSize['bottom'] + ybreak]]))
      else:
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
        # and here goes the ugly fix again
        self.ax_div.set_position(matplotlib.transforms.Bbox([[1.5, self.padSize['bottom']], [2.0,  self.padSize['top']]]))
        self.ax2_div.set_position(matplotlib.transforms.Bbox([[1.5, self.padSize['bottom']], [2.0, self.padSize['top']]]))
      self.ax_resid.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))

      # clean up the mess that may have been caused by toggling visibility of inner items
      self.setAxisVisibility(value=self.axisVisible['right'], axis='right', redraw=False, target='plot')
      self.ax2.spines['right'].set_visible(self.axisVisible['right2'] and self.isSecondAxesActive())
      self.setAxisVisibility(value=self.axisVisible_resid['right'], axis='right', redraw=False, target='resid')

      self.setTickMarkVisibility(value=self.ticksVisible['right'], axis='right', redraw=False, target='plot', initMode=True)
      self.setTickMarkVisibility(value=self.ticksVisible['left2'], axis='left2', redraw=False, target='plot', initMode=True)
      self.setTickMarkVisibility(value=self.ticksVisible_resid['right'], axis='right', redraw=False, target='resid', initMode=True)
      
      # need to ensure correct positioning of second axis label
      if(self.isSecondAxesActive()):
        self.ax2.yaxis.label.set_visible(self.labelY2Show)
        self.ax2_div.yaxis.label.set_visible(False)
      else:
        # hopefully, this kills the mysterious black line
        self.ax2.spines['right'].set_visible(False)
        self.ax2_div.spines['left'].set_visible(False)
        
      # reset axis limits to nonsplit
      if(self.modeX == self.modeX_div):
        self.setAxisLimits(lower=self.minX, upper=self.maxX_div, axis='x', updateLabel=False, target='plot', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.setAxisLimits(lower=self.minX, upper=self.maxX_div, axis='x', updateLabel=False, target='resid', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.upperLimitx.setText(self.parent.formatNumber(self.maxX_div))

      # also need to update tick formatting if split axis is turned off
      for target in ['plot', 'resid']:
        self.setTickOne4All(axis='x', redraw=False, target=target)
      if(self.isSecondAxesActive()):
        self.setTickOne4All(axis='y2', redraw=False, target='plot')

    # draw plot and resid line (again) to ensure coverage of entire x range
    if(toggled):
      # only do this upon first display of split x axes
      if(hasattr(self.parent, 'plotArea')):
        self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
          fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot,\
          handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False, doAutoZoom=False)
        self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
          fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div,\
          handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True, doAutoZoom=False)
        # copy in case split axes are shown
        curve = self.parent.fit[self.parent.activeFit]
        if(self.parent.plotArea.splitY and curve.onBothAxes):
          curve.duplicateForSplit()
          if(not curve.visibility):
            curve.setVisibility(state=False, redraw=False)
        self.parent.plotArea.handleResidZero = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero, redraw=False)
        self.parent.plotArea.handleResidZero_div = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero_div, redraw=False, splitX=True)

    # refresh plots
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

      # finally we have to take care of the crosshair cursor b/c as of matplotlib 3.1.2 we can only initialize
      # it upon first display of div plot --- bummer!
      if(state and (self.cursor != None) and (self.cursor_div == None)):
        self.cursor_div = MyCursor(self.ax_div, useblit=True, color='black', linewidth=1)
        self.cursor_div.setParent(self)
        self.cursor_div.setAx2(self.ax2_div)
        
        # link cursors
        self.cursor.setTwin(self.cursor_div)
        self.cursor_div.setTwin(self.cursor)
        
        # toggle visibility
        self.cursor_div.toggleVisibility(self.cursorVisible, None)

      if(state and self.splitY and (self.cursor_div != None) and (self.cursor2_div == None)):
        self.cursor2_div = MyCursor(self.ax2_div, useblit=True, color='black', linewidth=0.5)
        self.cursor2_div.setParent(self)
        self.cursor2_div.setAx2(self.ax2_div)
        
        # link cursors
        if(self.cursor_div != None):
          self.cursor_div.setTwinVertical(self.cursor2_div)
          self.cursor2_div.setTwinVertical(self.cursor_div)
        if(self.cursor2 != None):
          self.cursor2.setTwin(self.cursor2_div)
          self.cursor2_div.setTwin(self.cursor2)

    # also need to update tick entry fields
    self.updateTickEntryField(axis='x')
    self.updateTickEntryField(axis='x2')

    # for some strange reason we should reset the axis ticks here b/c minor autor locator gets confused
    # reason unclear, could be matplotlib bug??
    ###if(self.splitShow):
    if(True):
      redrawAgain = False
      if(self.ticksXAuto):
        self.setAutoTicks(axis='x', redraw=False, target='plot')
      else:
        # need to preserve axis limits before calling setAxisTicks()
        minX, maxX, ticksXLabel = self.minX, self.maxX, self.ticksXLabel
        self.setAxisTicks(value=self.ticksX, axis='x', redraw=False, target='plot')
        # restore tick labels
        self.ticksXLabel = ticksXLabel
        if(len(self.ticksXLabel) and (len(self.ticksXLabel) == len(self.ticksX))):
          for axisobject in [self.ax2, self.ax_resid]:
            axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksX))
            axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksXLabel))

        # now restore the original limits and (alas!) issue another redraw
        self.setAxisLimits(lower=minX, upper=maxX, axis='x', updateLabel=False, target='plot', redraw=False, updateTicks=False, updateGrid=True, preserveInverted=True)
        self.setAxisLimits(lower=minX, upper=maxX, axis='x', updateLabel=False, target='resid', redraw=False, updateTicks=False, updateGrid=True, preserveInverted=True)
        redrawAgain = True
      if(self.ticksXAuto_div):
        self.setAutoTicks(axis='x2', redraw=False, target='plot')
      else:
        # need to preserve axis limits before calling setAxisTicks()
        minX, maxX, ticksXLabel = self.minX_div, self.maxX_div, self.ticksXLabel_div
        self.setAxisTicks(value=self.ticksX_div, axis='x2', redraw=False, target='plot')
        # restore tick labels
        self.ticksXLabel_div = ticksXLabel
        if(len(self.ticksXLabel_div) and (len(self.ticksXLabel_div) == len(self.ticksX_div))):
          for axisobject in [self.ax2_div, self.ax_resid_div]:
            axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksX_div))
            axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksXLabel_div))
        # if split displayed, restore the original limits and (alas!) issue another redraw
        if(state):
          self.setAxisLimits(lower=minX, upper=maxX, axis='x2', updateLabel=False, target='plot', redraw=False, updateTicks=False, updateGrid=True, preserveInverted=True)
          self.setAxisLimits(lower=minX, upper=maxX, axis='x2', updateLabel=False, target='resid', redraw=False, updateTicks=False, updateGrid=True, preserveInverted=True)
          redrawAgain = True
      # do we need to redraw?
      if(redrawAgain):
        self.dataplotwidget.myRefresh()
        self.residplotwidget.myRefresh()
      
  def updateSplitDivider(self, param='color', value=[0.2, 0.2, 0.2, 1.0], redraw=True):
    # update parameter for split x axis inner divider line
    items = {'color': 'splitDividerColor', 'length': 'splitDividerLength', 'width': 'splitDividerWidth',\
             'dash': 'splitDividerDashStyle', 'angle': 'splitDividerAngle', 'location': 'splitDividerLocation'}
    if(param in items):
      if(value == self.__dict__[items[param]]):
        redraw = False
      self.__dict__[items[param]] = value

    # refresh plots
    if(redraw and self.splitShow and self.splitDivider):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

  def setSplitDivider(self, value=True, redraw=True):
    # toggles display of split divider lines
    if(value == self.splitDivider):
      redraw = False
    self.splitDivider = value

    # refresh plots
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

  def changeSplitFraction(self, value=1.0, redraw=True):
    # update parameters
    if (value == self.splitFraction):
      redraw = False
    self.splitFraction = value
    # call toggleSplit fxn
    self.toggleSplit(redraw=redraw, toggled=False)

  def changeSplitPad(self, value=0.02, redraw=True):
    # update parameters
    if (value == self.splitPad):
      redraw = False
    # implement check for too large split fraction
    maxSplit = 1 - self.padSize['left'] - (1 - self.padSize['right']) - 0.1
    if(value > maxSplit):
      if(hasattr(self.parent, 'statusbar')):
        self.parent.statusbar.showMessage('Cannot set split padding to ' + self.parent.formatNumber(value) + ', using ' + self.parent.formatNumber(maxSplit) + ' instead!', self.parent.STATUS_TIME)
      value, redraw = maxSplit, True
    self.splitPad = value
    # call toggleSplit fxn
    self.toggleSplit(redraw=redraw, toggled=False)

  def toggleSplitYCallback(self):
    # callback function splitY button
    state = self.splitYButton.isChecked()
    # toggle button on 2nd axes tab
    if(hasattr(self.parent, 'graphicsarea2')):
      self.parent.graphicsarea2.configSplitYLabel.setChecked(state)
    # do the actual toggling
    self.setSplitY(value=state, redraw=True)

  def updateSplitYDivider(self, param='color', value=[0.2, 0.2, 0.2, 1.0], redraw=True):
    # update parameter for split x axis inner divider line
    items = {'color': 'splitYDividerColor', 'length': 'splitYDividerLength', 'width': 'splitYDividerWidth',\
             'dash': 'splitYDividerDashStyle', 'angle': 'splitYDividerAngle', 'location': 'splitYDividerLocation'}
    if(param in items):
      if(value == self.__dict__[items[param]]):
        redraw = False
      self.__dict__[items[param]] = value

    # refresh plots
    if(redraw and self.splitY and self.splitYDivider):
      self.dataplotwidget.myRefresh()

  def setSplitYDivider(self, value=True, redraw=True):
    # toggles display of split y divider lines
    if(value == self.splitYDivider):
      redraw = False
    self.splitYDivider = value

    # refresh plots
    if(redraw):
      self.dataplotwidget.myRefresh()

  def setSplitY(self, value=True, redraw=True):
    # toggles splitting of y axes
    if(value == self.splitY):
      redraw = False
    self.splitY = value
    # update button in y control menu
    self.splitYButton.setChecked(self.splitY)
    # call toggleSplitY fxn
    self.toggleSplitY(redraw=redraw)

  def changeSplitYFraction(self, value=1.0, redraw=True):
    # update parameters
    if (value == self.splitYFraction):
      redraw = False
    self.splitYFraction = value
    # call toggleSplitY fxn
    self.toggleSplitY(redraw=redraw, toggled=False)

  def changeSplitYPad(self, value=0.02, redraw=True):
    # update parameters
    if (value == self.splitYPad):
      redraw = False
    # implement check for too large split fraction
    maxSplit = 1 - self.padSize['bottom'] - (1 - self.padSize['top']) - 0.1
    if(value > maxSplit):
      if(hasattr(self.parent, 'statusbar')):
        self.parent.statusbar.showMessage('Cannot set split padding to ' + self.parent.formatNumber(value) + ', using ' + self.parent.formatNumber(maxSplit) + ' instead!', self.parent.STATUS_TIME)
      value, redraw = maxSplit, True
    self.splitYPad = value
    # call toggleSplit fxn
    self.toggleSplitY(redraw=redraw, toggled=False)

  def setInnerYParameter(self, param='axes', state=True, redraw=True):
    # changes parameters for display of items at split y axis interface
    if(param in ['axes', 'ticks']):
      if(param == 'axes'):
        self.innerAxesY = state
      else:
        self.innerTicksY = state
        
      # apply these settings
      self.updateInnerYSituation()

      # issue plot refreshs
      if(redraw):
        self.dataplotwidget.myRefresh()

  def updateInnerYSituation(self):
    # deals with axis and tick settings at inner boundary of split y axis
    if(self.splitY):
      # deal with axis spines at interface
      for axis in [self.ax, self.ax_div]:
        axis.spines['bottom'].set_visible(self.innerAxesY and self.axisVisible['bottom'])
        axis.spines['bottom'].set_linewidth(self.axisWidth['bottom'])
        axis.spines['bottom'].set_linestyle(self.axisStyle['bottom'])
        axis.spines['bottom'].set_capstyle(self.axisDashStyle['bottom'])
        axis.spines['bottom'].set_color(self.axisColor['bottom'])
        if(self.axisBoundaryCheck['bottom']):
          lower, upper = self.axisBoundary['bottom']
          axis.spines['bottom'].set_bounds(lower, upper)
        else:
          axis.spines['bottom']._bounds = None

      # deal with axis on secondary plot
      for axis in [self.ax, self.ax_div, self.ax2, self.ax2_div]:
        if(axis in [self.ax, self.ax_div]):
          axis.spines['top'].set_visible(self.axisVisible['top'])
        else:
          axis.spines['top'].set_visible(self.innerAxesY and self.axisVisible['top'])
        axis.spines['top'].set_linewidth(self.axisWidth['top'])
        axis.spines['top'].set_linestyle(self.axisStyle['top'])
        axis.spines['top'].set_capstyle(self.axisDashStyle['top'])
        axis.spines['top'].set_color(self.axisColor['top'])
        if(self.axisBoundaryCheck['top']):
          lower, upper = self.axisBoundary['top']
          axis.spines['top'].set_bounds(lower, upper)
        else:
          axis.spines['top']._bounds = None
      
      # now deal with ticks
      for axis in [self.ax, self.ax_div]:
        # need to enable axis visibility to show ticks
        axis.xaxis.set_visible(True)
        axis.xaxis.set_tick_params(which='both', top=self.ticksVisible['top'], bottom=self.ticksVisible['bottom'] and self.innerTicksY,\
                                   width=self.ticksWidth['top'], color=self.ticksColor['top'], direction=self.ticksDirection['top'])
        axis.xaxis.set_tick_params(which='major', length=self.ticksLength['top'])
        axis.xaxis.set_tick_params(which='minor', length=self.ticksLength['top'] * self.ticksXMinorRelativeLength)

      for axis in [self.ax2, self.ax2_div]:
        axis.xaxis.set_tick_params(which='both', top=self.ticksVisible['top'] and self.innerTicksY, bottom=self.ticksVisible['bottom'])

      # ensure that no tick labels displayed at split
      self.ax.xaxis.set_tick_params(which='both', labeltop=self.ticksLabelShow['top'] and self.ticksXShow, labelbottom=False)
      self.ax_div.xaxis.set_tick_params(which='both', labeltop=self.ticksLabelShow['top'] and self.ticksXShow, labelbottom=False)
      self.ax2.xaxis.set_tick_params(which='both', labeltop=False, labelbottom=self.ticksLabelShow['bottom'] and self.ticksXShow)
      self.ax2_div.xaxis.set_tick_params(which='both', labeltop=False, labelbottom=self.ticksLabelShow['bottom'] and self.ticksXShow)

  def toggleSplitYHelper(self, state=True):
    # helper function called by toggleSplitY and loadState to deal with split cursors
    if(state):
      # update coords for rectangle selector
      self.storeCoord = []
  
      if((self.cursor != None) and (self.cursor2 == None)):
        self.cursor2 = MyCursor(self.ax2, useblit=True, color='black', linewidth=0.5)
        self.cursor2.setParent(self)
        self.cursor2.setAx2(self.ax2)
        
        # link cursors
        self.cursor.setTwinVertical(self.cursor2)
        self.cursor2.setTwinVertical(self.cursor)
        
        # toggle visibility
        self.cursor2.toggleVisibility(self.cursorVisible, None)
        
      if(self.splitShow and (self.cursor_div != None) and (self.cursor2_div == None)):
        self.cursor2_div = MyCursor(self.ax2_div, useblit=True, color='black', linewidth=0.5)
        self.cursor2_div.setParent(self)
        self.cursor2_div.setAx2(self.ax2_div)
        
        # link cursors
        if(self.cursor_div != None):
          self.cursor_div.setTwinVertical(self.cursor2_div)
          self.cursor2_div.setTwinVertical(self.cursor_div)
        if(self.cursor2 != None):
          self.cursor2.setTwin(self.cursor2_div)
          self.cursor2_div.setTwin(self.cursor2)
        
        # toggle visibility
        self.cursor2_div.toggleVisibility(self.cursorVisible, None)
    else:
      # update coords for rectangle selector
      self.storeCoord = []
      
      if(self.cursor != None):
        self.cursor.setTwinVertical(None)
      if(self.cursor_div != None):
        self.cursor_div.setTwinVertical(None)
      if(self.cursor2 != None):
        del self.cursor2
        self.cursor2 = None
      if(self.cursor2_div != None):
        del self.cursor2_div
        self.cursor2_div = None

  def toggleSplitY(self, redraw=True, toggled=True):
    # toggle second axes
    self.toggleSecondAxes(self.isSecondAxesActive())
    
    # helper function for split y axes
    if(self.splitY):
      # implement check for too large split fraction
      maxSplit = 1 - self.padSize['bottom'] - (1 - self.padSize['top']) - 0.1
      if(self.splitYPad > maxSplit):
        if(hasattr(self.parent, 'statusbar')):
          self.parent.statusbar.showMessage('Had to reduce split y axis padding to ' + self.parent.formatNumber(maxSplit) + ', rather than ' + self.parent.formatNumber(self.splitYPad) + '!', self.parent.STATUS_TIME)
        self.splitYPad = maxSplit

      # since we abandoned make_axes_locatable, we also need to implement check for split on x axis
      ybreak = (self.padSize['top'] - self.padSize['bottom'] - self.splitYPad) * self.splitYFraction / (1.0 + self.splitYFraction)
      if(self.splitShow):
        xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['left'] + xbreak,  self.padSize['top']]]))
        self.ax_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['right'],  self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['bottom'] + ybreak]]))
        self.ax2_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['bottom'] + ybreak]]))
      else:
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['right'], self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['bottom'] + ybreak]]))
    else:
      if(self.splitShow):
        xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak,  self.padSize['top']]]))
        self.ax_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'],  self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['top']]]))
        self.ax2_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
      else:
        self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
        self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))

    # adjust some graphic settings if y axes are split/unsplit
    if(self.splitY):
      if(toggled):
        if(self.canvasFill):
          # deal with patch background
          self.ax.patch.set_visible(True)
          self.ax_div.patch.set_visible(True)
          if(self.canvasGradientVisible):
            self.ax.patch.set_facecolor('none')
            self.ax_div.patch.set_facecolor('none')
          else:
            self.ax.patch.set_facecolor(self.canvasColor)
            self.ax_div.patch.set_facecolor(self.canvasColor)
  
        # store some settings
        items = 'axisVisible,axisWidth,axisStyle,axisDashStyle,axisColor,axisPosition,axisPositionValue'.split(',')
        items.extend('ticksVisible,ticksWidth,ticksLength,ticksColor,ticksDirection,ticksLabelShow'.split(','))
        for item in items:
          self.splitYBackup[item] = {}
          for item2 in ['left', 'right']:
            self.splitYBackup[item][item2] = self.__dict__[item][item2 + '2']
            
        items = 'ticksY2Show,ticksY2Color,ticksY2Size,ticksY2Weight,ticksY2Style,ticksY2Angle'.split(',')
        items.extend('ticksY2Alignment,ticksY2AlignmentVertical,ticksY2Pad,ticksY2Pad2,labelY2Show'.split(','))
        items.extend('ticksY2Minor,ticksY2MinorRelativeLength,ticksY2Format,ticksY2FormatPrecision,ticksY2FormatTrailZero'.split(','))
        items.extend('ticksY2FormatSeparator,ticksY2FormatComma,ticksY2FormatPrefix,ticksY2FormatPostfix'.split(','))
        for item in items:
          self.splitYBackup[item] = self.__dict__[item]
  
        items = 'tickFont,gridVisible,gridWidth,gridStyle,gridDashStyle,gridColor,gridOrder'.split(',')
        for item in items:
          self.splitYBackup[item] = self.__dict__[item]['y2']
          
        # adjust display of axis
        for item in ['left', 'right']:
          self.setAxisVisibility(value=self.axisVisible[item], axis=item + '2', redraw=False, target='plot')
          self.setAxisWidth(value=self.axisWidth[item], axis=item + '2', redraw=False, target='plot')
          self.setAxisStyle(value=self.axisStyle[item], axis=item + '2', redraw=False, target='plot')
          self.setAxisDashStyle(value=self.axisDashStyle[item], axis=item + '2', redraw=False, target='plot')
          self.setAxisColor(value=self.axisColor[item], axis=item + '2', redraw=False, target='plot')
          # also need to copy axis position settings
          self.setAxisPosition(value=self.axisPosition[item], axis=item + '2', redraw=False, target='plot')
          self.setAxisPositionValue(value=self.axisPositionValue[item], axis=item + '2', redraw=False, target='plot')
          
        # also need to turn on shifted top and bottom axis
        for item in ['top', 'bottom']:
          # phew, we will need to update setAxisPositionHelper() to accommodate this option
          self.setAxisPositionHelper(axis=item, plotobject=self.dataplotwidget, axisobject=self.ax, target='plot', secondAxes=False, splitX=False, splitY=True)
          self.setAxisPositionHelper(axis=item, plotobject=self.dataplotwidget, axisobject=self.ax_div, target='plot', secondAxes=False, splitX=False, splitY=True)
  
        # adjust display of ticks
        for item in ['left', 'right']:
          self.setTickMarkVisibility(value=self.ticksVisible[item], axis=item + '2', redraw=False, target='plot')
          self.setTickMarkWidth(value=self.ticksWidth[item], axis=item + '2', redraw=False, target='plot')
          self.setTickMarkLength(value=self.ticksLength[item], axis=item + '2', redraw=False, target='plot')
          self.setTickMarkColor(value=self.ticksColor[item], axis=item + '2', redraw=False, target='plot')
          self.setTickMarkDirection(value=self.ticksDirection[item], axis=item + '2', redraw=False, target='plot')
          self.setTickMarkLabelShow(value=self.ticksLabelShow[item], axis=item + '2', redraw=False, target='plot')
  
        # tick marks
        self.toggleTicksLabel(value=self.ticksYShow, axis='y2', redraw=False, target='plot')
        self.setTickLabelColor(value=self.ticksYColor, axis='y2', redraw=False, target='plot')
        self.setTickLabelSize(value=self.ticksYSize, axis='y2', redraw=False, target='plot')
        self.setTickLabelBold(value=self.ticksYWeight, axis='y2', redraw=False, target='plot')
        self.setTickLabelItalic(value=self.ticksYStyle, axis='y2', redraw=False, target='plot')
        self.setTickLabelAngle(value=self.ticksYAngle, axis='y2', redraw=False, target='plot')
        self.setTickLabelAlignment(value=self.ticksYAlignment, axis='y2', redraw=False, target='plot')
        self.setTickLabelAlignmentVertical(value=self.ticksYAlignmentVertical, axis='y2', redraw=False, target='plot')
        self.setTickLabelPad(value=self.ticksYPad, axis='y2', redraw=False, target='plot')
        self.setTickLabelPad2(value=self.ticksYPad2, axis='y2', redraw=False, target='plot')
        self.setTickFont(value=self.tickFont['y'], axis='y2', redraw=False, target='plot')
  
        # grid and axis labels
        self.toggleAxisLabel(value=False, axis='y2', redraw=False, target='plot')
        self.setGridVisibility(value=self.gridVisible['y'], axis='y2', redraw=False, target='plot')
        self.setGridWidth(value=self.gridWidth['y'], axis='y2', redraw=False, target='plot')
        self.setGridStyle(value=self.gridStyle['y'], axis='y2', redraw=False, target='plot')
        self.setGridDashStyle(value=self.gridDashStyle['y'], axis='y2', redraw=False, target='plot')
        self.setGridColor(value=self.gridColor['y'], axis='y2', redraw=False, target='plot')
        self.setGridOrder(value=self.gridOrder['y'], axis='y2', redraw=False, target='plot')
        ###consider also updating minor grid (although should be slaved to major ticks)
  
        # deal with the innner situation
        self.updateInnerYSituation()
  
        # update graphiscarea 2
        if(hasattr(self.parent, 'graphicsarea2')):
          self.parent.graphicsarea2.updateFields()
  
          self.parent.graphicsarea2.outerBox8.show()
          ###self.parent.graphicsarea2.configSplitYBox.show()
          ###self.parent.graphicsarea2.splitYSeparator.show()
          # check whether advanced graphics enabled
          if(self.parent.graphicsarea.advancedExport['advancedGraphics']):
            if(hasattr(self.parent.graphicsarea2, 'configSplitYBox2')):
              self.parent.graphicsarea2.configSplitYBox2.show()
            if(hasattr(self.parent.graphicsarea2, 'configSplitYBox3')):
              self.parent.graphicsarea2.configSplitYBox3.show()
  
        # draw all items on split axes and check them
        # change of plan -- duplicate all items but hide the ones that are not visible
        for data in self.parent.data:
          data.onBothAxes = True
          data.duplicateForSplit()
          if(not data.visibility):
            data.setVisibility(state=False, redraw=False)
  
        for curve in self.parent.fit:
          curve.onBothAxes = True
          curve.duplicateForSplit()
          if(not curve.visibility):
            curve.setVisibility(state=False, redraw=False)
          
        for extra in self.parent.extras:
          extra.onBothAxes = True
          extra.duplicateForSplit()
          if(not extra.visibility):
            extra.setVisibility(state=False, redraw=False)
            
        # also draw selected data on both axes
        if(self.parent.selectedData.isLive):
          self.parent.selectedData.duplicateForSplit()
          self.parent.selectedData.style.update(self.parent.selectedData.theseStyles)
        
        # set axis mode and limits to match those of first axes
        self.secondModeSelector.blockSignals(True)
        index = self.secondModeSelector.findText(self.modeY)
        self.secondModeSelector.setCurrentIndex(index)
        self.secondModeSelector.blockSignals(False)
        self.changeAxisMode('y2', redraw=False)
        # set axes limits for split effect
        if(self.modeY == 'linear'):
          splitYAt = self.minY + self.splitYFraction / (self.splitYFraction + 1) * (self.maxY - self.minY)
        else:
          splitYAt = np.exp(np.log(self.minY) + self.splitYFraction / (self.splitYFraction + 1) * (np.log(self.maxY) - np.log(self.minY)))
        # discard flipped state of second axis
        # need to check whether main axis is inverted or not
        if(self.minY < self.maxY):
          lower, upper = sorted([self.minY, splitYAt])
        else:
          upper, lower = sorted([self.minY, splitYAt])
        self.setAxisLimits(lower=lower, upper=upper, axis='y2', updateLabel=False, target='plot', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=False)
        self.secondLowerLimit.setText(self.lowerLimity.text())
        self.secondUpperLimit.setText(self.parent.formatNumber(splitYAt))
        self.setAxisLimits(lower=splitYAt, upper=self.maxY, axis='y', updateLabel=False, target='plot', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.lowerLimity.setText(self.parent.formatNumber(splitYAt))

        # also need to copy custom tick formatting (if axis modes on y axes are the same)
        # => but we just copied the mode, so this is always the case
        self.setMinorTick(value=self.ticksYMinor, axis='y2', redraw=False, target='plot')
        self.setMinorTickRelativeLength(value=self.ticksYMinorRelativeLength, axis='y2', redraw=False, target='plot')
        self.setTickFormat(axis='y2', value=self.ticksYFormat, redraw=False)
        self.setTickFormatPrecision(axis='y2', value=self.ticksYFormatPrecision, redraw=False)
        self.setFormatTrailZero(axis='y2', value=self.ticksYFormatTrailZero, redraw=False)
        self.setFormatSeparator(axis='y2', value=self.ticksYFormatSeparator, redraw=False)
        self.setFormatComma(axis='y2', value=self.ticksYFormatComma, redraw=False)
        self.setFormatFix(axis='y2', value=self.ticksYFormatPrefix, redraw=False, prefix=True)
        self.setFormatFix(axis='y2', value=self.ticksYFormatPostfix, redraw=False, prefix=False)
        
        # activate rectangle selectors and generate new cursor if needed
        self.toggleSplitYHelper(state=True)
    else:
      # deal with patch background
      self.ax.patch.set_visible(False)
      self.ax_div.patch.set_visible(False)
      
      # adjust display of axis
      for item in ['left', 'right']:
        if(('axisVisible' in self.splitYBackup) and (item in self.splitYBackup['axisVisible'])):
          if(self.splitShow and (item == 'right')):
            self.setAxisVisibility(value=self.innerAxes and self.axisVisible['right2'], axis='right2', redraw=False, target='plot')
          else:
            if(self.isSecondAxesActive()):
              self.setAxisVisibility(value=self.splitYBackup['axisVisible'][item], axis=item + '2', redraw=False, target='plot')
            else:
              # hide axes of second plot in case no items exist on second axes
              self.setAxisVisibility(value=False, axis=item + '2', redraw=False, target='plot')
        if(('axisWidth' in self.splitYBackup) and (item in self.splitYBackup['axisWidth'])):
          self.setAxisWidth(value=self.splitYBackup['axisWidth'][item], axis=item + '2', redraw=False, target='plot')
        if(('axisStyle' in self.splitYBackup) and (item in self.splitYBackup['axisStyle'])):
          self.setAxisStyle(value=self.splitYBackup['axisStyle'][item], axis=item + '2', redraw=False, target='plot')
        if(('axisDashStyle' in self.splitYBackup) and (item in self.splitYBackup['axisDashStyle'])):
          self.setAxisDashStyle(value=self.splitYBackup['axisDashStyle'][item], axis=item + '2', redraw=False, target='plot')
        if(('axisColor' in self.splitYBackup) and (item in self.splitYBackup['axisColor'])):
          self.setAxisColor(value=self.splitYBackup['axisColor'][item], axis=item + '2', redraw=False, target='plot')
        if(('axisPosition' in self.splitYBackup) and (item in self.splitYBackup['axisPosition'])):
          self.setAxisPosition(value=self.splitYBackup['axisPosition'][item], axis=item + '2', redraw=False, target='plot')
        if(('axisPositionValue' in self.splitYBackup) and (item in self.splitYBackup['axisPositionValue'])):
          self.setAxisPositionValue(value=self.splitYBackup['axisPositionValue'][item], axis=item + '2', redraw=False, target='plot')

      # adjust display of ticks
      for item in ['left', 'right']:
        if(('ticksVisible' in self.splitYBackup) and (item in self.splitYBackup['ticksVisible'])):
          self.setTickMarkVisibility(value=self.splitYBackup['ticksVisible'][item], axis=item + '2', redraw=False, target='plot')
        if(('ticksWidth' in self.splitYBackup) and (item in self.splitYBackup['ticksWidth'])):
          self.setTickMarkWidth(value=self.splitYBackup['ticksWidth'][item], axis=item + '2', redraw=False, target='plot')
        if(('ticksLength' in self.splitYBackup) and (item in self.splitYBackup['ticksLength'])):
          self.setTickMarkLength(value=self.splitYBackup['ticksLength'][item], axis=item + '2', redraw=False, target='plot')
        if(('ticksColor' in self.splitYBackup) and (item in self.splitYBackup['ticksColor'])):
          self.setTickMarkColor(value=self.splitYBackup['ticksColor'][item], axis=item + '2', redraw=False, target='plot')
        if(('ticksDirection' in self.splitYBackup) and (item in self.splitYBackup['ticksDirection'])):
          self.setTickMarkDirection(value=self.splitYBackup['ticksDirection'][item], axis=item + '2', redraw=False, target='plot')
        if(('ticksLabelShow' in self.splitYBackup) and (item in self.splitYBackup['ticksLabelShow'])):
          self.setTickMarkLabelShow(value=self.splitYBackup['ticksLabelShow'][item], axis=item + '2', redraw=False, target='plot')

      # tick marks
      if('ticksY2Show' in self.splitYBackup):
        self.toggleTicksLabel(value=self.splitYBackup['ticksY2Show'], axis='y2', redraw=False, target='plot')
      if('ticksY2Color' in self.splitYBackup):
        self.setTickLabelColor(value=self.splitYBackup['ticksY2Color'], axis='y2', redraw=False, target='plot')
      if('ticksY2Size' in self.splitYBackup):
        self.setTickLabelSize(value=self.splitYBackup['ticksY2Size'], axis='y2', redraw=False, target='plot')
      if('ticksY2Weight' in self.splitYBackup):
        self.setTickLabelBold(value=self.splitYBackup['ticksY2Weight'], axis='y2', redraw=False, target='plot')
      if('ticksY2Style' in self.splitYBackup):
        self.setTickLabelItalic(value=self.splitYBackup['ticksY2Style'], axis='y2', redraw=False, target='plot')
      if('ticksY2Angle' in self.splitYBackup):
        self.setTickLabelAngle(value=self.splitYBackup['ticksY2Angle'], axis='y2', redraw=False, target='plot')
      if('ticksY2Alignment' in self.splitYBackup):
        self.setTickLabelAlignment(value=self.splitYBackup['ticksY2Alignment'], axis='y2', redraw=False, target='plot')
      if('ticksY2AlignmentVertical' in self.splitYBackup):
        self.setTickLabelAlignmentVertical(value=self.splitYBackup['ticksY2AlignmentVertical'], axis='y2', redraw=False, target='plot')
      if('ticksY2Pad' in self.splitYBackup):
        self.setTickLabelPad(value=self.splitYBackup['ticksY2Pad'], axis='y2', redraw=False, target='plot')
      if('ticksY2Pad2' in self.splitYBackup):
        self.setTickLabelPad2(value=self.splitYBackup['ticksY2Pad2'], axis='y2', redraw=False, target='plot')
      if(('tickFont' in self.splitYBackup) and ('y2' in self.splitYBackup['tickFont'])):
        self.setTickFont(value=self.splitYBackup['tickFont']['y2'], axis='y2', redraw=False, target='plot')
        
      # custom tick formatting
      if('ticksY2Minor' in self.splitYBackup):
        self.setMinorTick(value=self.splitYBackup['ticksY2Minor'], axis='y2', redraw=False, target='plot')
      if('ticksY2MinorRelativeLength' in self.splitYBackup):
        self.setMinorTickRelativeLength(value=self.splitYBackup['ticksY2MinorRelativeLength'], axis='y2', redraw=False, target='plot')
      if('ticksY2Format' in self.splitYBackup):
        self.setTickFormat(axis='y2', value=self.splitYBackup['ticksY2Format'], redraw=False)
      if('ticksY2FormatPrecision' in self.splitYBackup):
        self.setTickFormatPrecision(axis='y2', value=self.splitYBackup['ticksY2FormatPrecision'], redraw=False)
      if('ticksY2FormatTrailZero' in self.splitYBackup):
        self.setFormatTrailZero(axis='y2', value=self.splitYBackup['ticksY2FormatTrailZero'], redraw=False)
      if('ticksY2FormatSeparator' in self.splitYBackup):
        self.setFormatSeparator(axis='y2', value=self.splitYBackup['ticksY2FormatSeparator'], redraw=False)
      if('ticksY2FormatComma' in self.splitYBackup):
        self.setFormatComma(axis='y2', value=self.splitYBackup['ticksY2FormatComma'], redraw=False)
      if('ticksY2FormatPrefix' in self.splitYBackup):
        self.setFormatFix(axis='y2', value=self.splitYBackup['ticksY2FormatPrefix'], redraw=False, prefix=True)
      if('ticksYFormatPostfix' in self.splitYBackup):
        self.setFormatFix(axis='y2', value=self.splitYBackup['ticksYFormatPostfix'], redraw=False, prefix=False)

      # grid and axis labels
      if('labelY2Show' in self.splitYBackup):
        self.toggleAxisLabel(value=self.splitYBackup['labelY2Show'], axis='y2', redraw=False, target='plot')
      if('gridVisible' in self.splitYBackup):
        self.setGridVisibility(value=self.splitYBackup['gridVisible'], axis='y2', redraw=False, target='plot')
      if('gridWidth' in self.splitYBackup):
        self.setGridWidth(value=self.splitYBackup['gridWidth'], axis='y2', redraw=False, target='plot')
      if('gridStyle' in self.splitYBackup):
        self.setGridStyle(value=self.splitYBackup['gridStyle'], axis='y2', redraw=False, target='plot')
      if('gridDashStyle' in self.splitYBackup):
        self.setGridDashStyle(value=self.splitYBackup['gridDashStyle'], axis='y2', redraw=False, target='plot')
      if('gridColor' in self.splitYBackup):
        self.setGridColor(value=self.splitYBackup['gridColor'], axis='y2', redraw=False, target='plot')
      if('gridOrder' in self.splitYBackup):
        self.setGridOrder(value=self.splitYBackup['gridOrder'], axis='y2', redraw=False, target='plot')
      ###consider also updating minor grid (although should be slaved to major ticks)

      # deal with axis on secondary plot
      for axis in [self.ax, self.ax_div]:
        axis.spines['top'].set_visible(False)
      for axis in [self.ax2, self.ax2_div]:
        axis.spines['top'].set_visible(self.axisVisible['top'])

      # update graphiscarea 2
      if(hasattr(self.parent, 'graphicsarea2')):
        self.parent.graphicsarea2.updateFields()

        ###self.parent.graphicsarea2.configSplitYBox.hide()
        self.parent.graphicsarea2.outerBox8.hide()
        self.parent.graphicsarea2.configSplitYBox2.hide()
        self.parent.graphicsarea2.configSplitYBox3.hide()
        ###self.parent.graphicsarea2.splitYSeparator.hide()

      # clear all duplicated graphics
      for data in self.parent.data:
        data.removeFromSplit()

      for curve in self.parent.fit:
        curve.removeFromSplit()

      for extra in self.parent.extras:
        extra.removeFromSplit()

      # remove selected data from both axes
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.removeFromSplit()
          
      # clear stored info
      self.splitYBackup = {}
      
      # reset axis limits to nonsplit
      if((self.modeY == 'linear') or (self.minY2 > 0)):
        self.setAxisLimits(lower=self.minY2, upper=self.maxY, axis='y', updateLabel=False, target='plot', redraw=False, updateTicks=True, updateGrid=True, preserveInverted=True)
        self.lowerLimity.setText(self.parent.formatNumber(self.minY2))
      
      # deactivate cursors
      self.toggleSplitYHelper(state=False)

    # refresh tables in objects tab
    self.parent.objectsarea.refreshDataTable()
    self.parent.objectsarea.refreshCurvesTable()
    self.parent.objectsarea.refreshExtrasTable()

    # refresh plots
    if(redraw):
      self.dataplotwidget.myRefresh()

    # for some strange reason we should reset the axis ticks here b/c minor autor locator gets confused
    # reason unclear, could be matplotlib bug??
    ###if(self.splitY):
    if(True):
      redrawAgain = False
      if(self.ticksYAuto):
        self.setAutoTicks(axis='y', redraw=False, target='plot')
      else:
        # need to preserve axis limits before calling setAxisTicks()
        minY, maxY = self.minY, self.maxY
        self.setAxisTicks(value=self.ticksY, axis='y', redraw=False, target='plot')

        # now restore the original limits and (alas!) issue another redraw
        self.setAxisLimits(lower=minY, upper=maxY, axis='y', updateLabel=True, target='plot', redraw=False, updateTicks=False, updateGrid=True, preserveInverted=True)
        redrawAgain = True
      if(self.ticksY2Auto):
        self.setAutoTicks(axis='y2', redraw=False, target='plot')
      else:
        # need to preserve axis limits before calling setAxisTicks()
        minY, maxY = self.minY2, self.maxY2
        self.setAxisTicks(value=self.ticksY, axis='y2', redraw=False, target='plot')

        # now restore the original limits and (alas!) issue another redraw
        self.setAxisLimits(lower=minY, upper=maxY, axis='y2', updateLabel=True, target='plot', redraw=False, updateTicks=False, updateGrid=True, preserveInverted=True)
        redrawAgain = True

      # do we need to redraw?
      if(redrawAgain):
        self.dataplotwidget.myRefresh()

  def reportState(self):
    # returns current settings for save state function
    reportItems = ['minX', 'maxX', 'minY', 'maxY', 'minResidY', 'maxResidY', 'modeX', 'modeY', 'autoScaleX', 'autoScaleY']
    reportItems.extend(['minY2', 'maxY2', 'modeY2', 'autoScaleY2'])
    reportItems.extend(['splitShow', 'splitY', 'minX_div', 'maxX_div', 'modeX_div', 'autoScaleX_div'])
    retv = {}

    for entry in reportItems:
      if(hasattr(self, entry)):
        value = getattr(self, entry)
        retv[entry] = value
        
    # also add current zoom level (which, however, belongs to parent object)
    currZoomIndex = self.parent.zoomSelector.currentIndex()
    retv['zoomLevel'] = self.parent.zoomLevels[::-1][currZoomIndex]
    
    return retv
  
  def restoreState(self, settings, updateTicks=True):
    # restores settings from load state function
    for entry in settings:
      if(hasattr(self, entry)):
        self.__dict__[entry] = settings[entry]

    # remember desired axes limits
    orig_minX, orig_maxX = self.minX, self.maxX
    orig_minY, orig_maxY = self.minY, self.maxY
    orig_minResidY, orig_maxResidY = self.minResidY, self.maxResidY
    orig_minY2, orig_maxY2 = self.minY2, self.maxY2
    orig_minX_div, orig_maxX_div = self.minX_div, self.maxX_div
    
    # apply these settings
    # autoscale
    self.autoScaleCheckX.setChecked(self.autoScaleX)
    self.autoScaleCheckY.setChecked(self.autoScaleY)
    self.xSplitAutoScaleCheckX.setChecked(self.autoScaleX_div)

    # axes modes
    index = self.modeSelectorx.findText(self.modeX)
    if((index + 1) and (index != self.modeSelectorx.currentIndex())):
      self.modeSelectorx.blockSignals(True)
      self.modeSelectorx.setCurrentIndex(index)
      self.modeSelectorx.blockSignals(False)
      self.changeAxisMode('x', redraw=False)
    index = self.modeSelectory.findText(self.modeY)
    if((index + 1) and (index != self.modeSelectory.currentIndex())):
      self.modeSelectory.blockSignals(True)
      self.modeSelectory.setCurrentIndex(index)
      self.modeSelectory.blockSignals(False)
      self.changeAxisMode('y', redraw=False)
    index = self.secondModeSelector.findText(self.modeY2)
    if((index + 1) and (index != self.secondModeSelector.currentIndex())):
      self.secondModeSelector.blockSignals(True)
      self.secondModeSelector.setCurrentIndex(index)
      self.secondModeSelector.blockSignals(False)
      self.changeAxisMode('y2', redraw=False)
    index = self.xSplitModeSelectorx.findText(self.modeX_div)
    if((index + 1) and (index != self.xSplitModeSelectorx.currentIndex())):
      self.xSplitModeSelectorx.blockSignals(True)
      self.xSplitModeSelectorx.setCurrentIndex(index)
      self.xSplitModeSelectorx.blockSignals(False)
      self.changeAxisMode('x2', redraw=False)
    
    # need to counteract resetting of axis ticks when adjusting axis mode
    minX, maxX = self.minX, self.maxX
    minY, maxY = self.minY, self.maxY
    minResidY, maxResidY = self.minResidY, self.maxResidY
    minY2, maxY2 = self.minY2, self.maxY2
    minX_div, maxX_div = self.minX_div, self.maxX_div
    
    # apply tick formatting
    self.ticksXDataSet = -1
    if(self.ticksXAuto):
      self.setAutoTicks(axis='x', redraw=False, target='plot')
      self.setAutoTicks(axis='x', redraw=False, target='resid')
    else:
      ticksXLabel = self.ticksXLabel
      self.setAxisTicks(value=self.ticksX, axis='x', redraw=False, target='plot')
      self.setAxisTicks(value=self.ticksX, axis='x', redraw=False, target='resid')
      # apply axis labels?
      self.ticksXLabel = ticksXLabel
      if(len(self.ticksXLabel) and (len(self.ticksXLabel) == len(self.ticksX))):
        for axisobject in [self.ax, self.ax_resid]:
          axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksX))
          axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksXLabel))
    if(self.ticksXAuto_div):
      self.setAutoTicks(axis='x2', redraw=False, target='plot')
      self.setAutoTicks(axis='x2', redraw=False, target='resid')
    else:
      ticksXLabel = self.ticksXLabel_div
      self.setAxisTicks(value=self.ticksX_div, axis='x2', redraw=False, target='plot')
      self.setAxisTicks(value=self.ticksX_div, axis='x2', redraw=False, target='resid')
      # apply axis labels?
      self.ticksXLabel_div = ticksXLabel
      if(len(self.ticksXLabel_div) and (len(self.ticksXLabel_div) == len(self.ticksX_div))):
        for axisobject in [self.ax2_div, self.ax_resid_div]:
          axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksX_div))
          axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksXLabel_div))
      
    if(self.ticksYAuto):
      self.setAutoTicks(axis='y', redraw=False, target='plot')
    else:
      self.setAxisTicks(value=self.ticksY, axis='y', redraw=False, target='plot')
      
    if(self.ticksYAuto):
      self.setAutoTicks(axis='resid', redraw=False, target='resid')
    else:
      self.setAxisTicks(value=self.ticksResidY, axis='resid', redraw=False, target='resid')

    if(self.ticksY2Auto):
      self.setAutoTicks(axis='y2', redraw=False, target='plot')
    else:
      self.setAxisTicks(value=self.ticksY2, axis='y2', redraw=False, target='plot')

    # retrieve settings
    self.minX, self.maxX = minX, maxX
    self.minY, self.maxY = minY, maxY
    self.minResidY, self.maxResidY = minResidY, maxResidY
    self.minY2, self.maxY2 = minY2, maxY2
    self.minX_div, self.maxX_div = minX_div, maxX_div
    
    # check wether we can go to original values specified in state
    if(self.modeX == 'linear'):
      self.minX, self.maxX = orig_minX, orig_maxX
    else:
      self.minX, self.maxX = [i if (i > 0) else j for i, j in zip([orig_minX, orig_maxX], [self.minX, self.maxX])]
    if(self.modeY == 'linear'):
      self.minY, self.maxY = orig_minY, orig_maxY
    else:
      self.minY, self.maxY = [i if (i > 0) else j for i, j in zip([orig_minY, orig_maxY], [self.minY, self.maxY])]
    if(self.modeY2 == 'linear'):
      self.minY2, self.maxY2 = orig_minY2, orig_maxY2
    else:
      self.minY2, self.maxY2 = [i if (i > 0) else j for i, j in zip([orig_minY2, orig_maxY2], [self.minY2, self.maxY2])]
    self.minResidY, self.maxResidY = orig_minResidY, orig_maxResidY
    if(self.modeX_div == 'linear'):
      self.minX_div, self.maxX_div = orig_minX_div, orig_maxX_div
    else:
      self.minX_div, self.maxX_div = [i if (i > 0) else j for i, j in zip([orig_minX_div, orig_maxX_div], [self.minX_div, self.maxX_div])]
    
    # adjust zoom level if specified
    if('zoomLevel' in settings):
      # set actual zoom level
      self.setZoomLevel(settings['zoomLevel'], redraw=False)
      # adjust selection dialog
      zoomDifference = [abs(settings['zoomLevel'] - i) for i in self.parent.zoomLevels[::-1]]
      zoomIndex = zoomDifference.index(min(zoomDifference))
      self.parent.zoomSelector.blockSignals(True)
      self.parent.zoomSelector.setCurrentIndex(zoomIndex)
      self.parent.zoomSelector.blockSignals(False)
      
    # toggle split x axis
    if(self.splitAxisButton.isChecked() != self.splitShow):
      self.splitAxisButton.setChecked(self.splitShow)
      self.toggleSplit(redraw=False, toggled=False)
      
    # toggle split y axis
    if(self.splitYButton.isChecked() != self.splitY):
      self.splitYButton.setChecked(self.splitY)
      self.toggleSplitY(redraw=False, toggled=False)

    # axes limits
    self.setAxisLimits(lower=self.minX, upper=self.maxX, axis='x', updateLabel=True, target='plot', redraw=False, updateTicks=updateTicks, updateGrid=True)
    self.setAxisLimits(lower=self.minY2, upper=self.maxY2, axis='y2', updateLabel=True, target='plot', redraw=False, updateTicks=updateTicks, updateGrid=True)
    self.setAxisLimits(lower=self.minY, upper=self.maxY, axis='y', updateLabel=True, target='plot', redraw=False, updateTicks=updateTicks, updateGrid=True)
    self.setAxisLimits(lower=self.minX, upper=self.maxX, axis='x', updateLabel=True, target='resid', redraw=False, updateTicks=updateTicks, updateGrid=True)
    self.setAxisLimits(lower=self.minResidY, upper=self.maxResidY, axis='y', updateLabel=True, target='resid', redraw=False, updateTicks=updateTicks, updateGrid=True)

    self.setAxisLimits(lower=self.minX_div, upper=self.maxX_div, axis='x2', updateLabel=True, target='plot', redraw=False, updateTicks=updateTicks, updateGrid=True)
    self.setAxisLimits(lower=self.minX_div, upper=self.maxX_div, axis='x2', updateLabel=True, target='resid', redraw=False, updateTicks=updateTicks, updateGrid=True)
    
    # draw zero resid line over (updated) x range
    self.handleResidZero = self.plotResidZero(self.handleResidZero, redraw=False)
    self.handleResidZero_div = self.plotResidZero(self.handleResidZero_div, redraw=False, splitX=True)

    # update tick entry fields
    for axis in ['x', 'x2', 'y', 'y2', 'resid']:
      self.updateTickEntryField(axis=axis)

  def incZoomLevel(self, increment=1):
    # used by mouse wheel to adjust zoom level
    currZoomIndex = self.parent.zoomSelector.currentIndex()
    # alter zoom level but restrain in bounds
    nuZoomIndex = currZoomIndex + increment
    nuZoomIndex = max(0, nuZoomIndex)
    nuZoomIndex = min(len(self.parent.zoomLevels) -1, nuZoomIndex)
    # do we need to change anything?
    if(currZoomIndex != nuZoomIndex):
      self.parent.zoomSelector.setCurrentIndex(nuZoomIndex)

  def setZoomLevel(self, zoomLevel=100, redraw=True):
    # sets zoom level of plots
    if(self.matplot.get_dpi() != zoomLevel):
      # get current size and scale factor
      scaleFactor = self.matplot.get_dpi() / zoomLevel
      currSize = self.matplot.get_size_inches()
      # change canvas DPI
      self.matplot.set_dpi(zoomLevel)
      # adjust size accordingly
      nuSize = [i * scaleFactor for i in currSize]
      self.matplot.set_size_inches(nuSize)
      self.horizontalRuler.updateRuler()
      self.verticalRuler.updateRuler()
      # check for presence of data selection (which would need to be adjusted)
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.adjustMarkerSize(resid=False)
      # issue plot refresh
      if(redraw):
        self.dataplotwidget.myRefresh()
    
    if(self.residplot.get_dpi() != zoomLevel):
      # get current size and scale factor
      scaleFactor = self.residplot.get_dpi() / zoomLevel
      currSize = self.residplot.get_size_inches()
      # change canvas DPI
      self.residplot.set_dpi(zoomLevel)
      # adjust size accordingly
      nuSize = [i * scaleFactor for i in currSize]
      self.residplot.set_size_inches(nuSize)
      self.verticalRulerResid.updateRuler()
      # check for presence of data selection (which would need to be adjusted)
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.adjustMarkerSize(resid=True)
      # issue plot refresh
      if(redraw):
        self.residplotwidget.myRefresh()

  def setTickFormatHelper(self, axis='x', redraw=True):
    # accessory function to format tick labels
    if(axis in ['x', 'residx']):
      useAxisobject, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, useFallback =\
        self.ax2, self.ticksXFormat, self.ticksXFormatPrecision, self.ticksXFormatTrailZero, self.ticksXFormatSeparator, self.ticksXFormatComma, self.fallback_ticksXFormat
      usePrefix, usePostfix = self.ticksXFormatPrefix, self.ticksXFormatPostfix
      if(axis == 'residx'):
        useAxisobject, item = self.ax_resid, 'resid'
      else:
        item = 'plot'
    elif(axis in ['x2', 'residx2']):
      useAxisobject, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, useFallback =\
        self.ax2_div, self.ticksXFormat_div, self.ticksXFormatPrecision_div, self.ticksXFormatTrailZero_div, self.ticksXFormatSeparator_div, self.ticksXFormatComma_div, self.fallback_ticksXFormat_div
      usePrefix, usePostfix = self.ticksXFormatPrefix_div, self.ticksXFormatPostfix_div
      if(axis == 'residx2'):
        useAxisobject, item = self.ax_resid_div, 'resid'
      else:
        item = 'plot'
    elif(axis == 'y'):
      useAxisobject, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, useFallback =\
        self.ax, self.ticksYFormat, self.ticksYFormatPrecision, self.ticksYFormatTrailZero, self.ticksYFormatSeparator, self.ticksYFormatComma, self.fallback_ticksYFormat
      usePrefix, usePostfix = self.ticksYFormatPrefix, self.ticksYFormatPostfix
      item = 'plot'
    elif(axis == 'y2'):
      useAxisobject, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, useFallback =\
        self.ax2, self.ticksY2Format, self.ticksY2FormatPrecision, self.ticksY2FormatTrailZero, self.ticksY2FormatSeparator, self.ticksY2FormatComma, self.fallback_ticksY2Format
      usePrefix, usePostfix = self.ticksY2FormatPrefix, self.ticksY2FormatPostfix
      item = 'plot'
    elif(axis == 'resid'):
      useAxisobject, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, useFallback =\
        self.ax_resid, self.ticksResidYFormat, self.ticksResidYFormatPrecision, self.ticksResidYFormatTrailZero, self.ticksResidYFormatSeparator, self.ticksResidYFormatComma, self.fallback_ticksResidYFormat
      usePrefix, usePostfix = self.ticksResidYFormatPrefix, self.ticksResidYFormatPostfix
      item = 'resid'
    elif(axis in ['xs', 'residxs']):
      useAxisobject, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, useFallback =\
        None, self.slavedXFormat, self.slavedXFormatPrecision, self.slavedXFormatTrailZero, self.slavedXFormatSeparator, self.slavedXFormatComma, self.fallback_slavedXFormat
      usePrefix, usePostfix = self.slavedXFormatPrefix, self.slavedXFormatPostfix
      if(axis == 'residxs'):
        useAxisobject, item = None, 'resid'
      else:
        item = 'plot'
    else:
      useTicksFormat = 'tilt'
      item = 'none'
    
    if(item in ['plot', 'resid']):
      # now apply the altered tick formatting
      # this should be easy as long as we turn on custom formatting
      if(useTicksFormat != 'default'):
        autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, useTicksFormat, useTicksFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, usePrefix, usePostfix))
      elif(useFallback != None):
        autolabels = useFallback
      else:
        # scalar formatter should be safe (except when using data ticks but then fallback should exist)
        autolabels = matplotlib.ticker.ScalarFormatter()
      if(axis in ['x', 'x2', 'residx', 'residx2']):
        useAxisobject.xaxis.set_major_formatter(autolabels)
      elif(axis in ['xs', 'residxs']):
        self.slavedXFormatter = autolabels
      else:
        useAxisobject.yaxis.set_major_formatter(autolabels)
      
      # issue plot redraw
      if(redraw):
        if(item == 'plot'):
          self.dataplotwidget.myRefresh()
        else:
          self.residplotwidget.myRefresh()    

  def setTickFormatPrecision(self, axis='x', value=2, redraw=True):
    # changes precision in custom tick label formatting
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'xs', 'residx', 'residx2', 'residxs']):
      useTicksFormat = 'default'
      if(axis in ['x', 'residx']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.ticksXFormatPrecision) or (axis == 'residx')):
          self.ticksXFormatPrecision, useTicksFormat = value, self.ticksXFormat
        else:
          redraw = False
      elif(axis in ['xs', 'residxs']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.slavedXFormatPrecision) or (axis == 'residxs')):
          self.slavedXFormatPrecision, useTicksFormat = value, self.slavedXFormat
        else:
          redraw = False
      elif(axis in ['x2', 'residx2']):
        if((value != self.ticksXFormatPrecision_div) or (axis == 'residx2')):
          self.ticksXFormatPrecision_div, useTicksFormat = value, self.ticksXFormat_div
        else:
          redraw = False
      elif(axis == 'y'):
        if(value != self.ticksYFormatPrecision):
          self.ticksYFormatPrecision, useTicksFormat = value, self.ticksYFormat
        else:
          redraw = False
      elif(axis == 'y2'):
        if(value != self.ticksY2FormatPrecision):
          self.ticksY2FormatPrecision, useTicksFormat = value, self.ticksY2Format
        else:
          redraw = False
      elif(axis == 'resid'):
        if(value != self.ticksResidYFormatPrecision):
          self.ticksResidYFormatPrecision, useTicksFormat = value, self.ticksResidYFormat
        else:
          redraw = False
      else:
        redraw = False

      # don't redraw when in default mode as changes won't be visible anyhow
      if(useTicksFormat == 'default'):
        redraw = False
      self.setTickFormatHelper(axis=axis, redraw=redraw)
          
  def setTickFormat(self, axis='x', value='float', redraw=True):
    # changes type of custom tick label formatting
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'residx', 'residx2', 'xs', 'residxs']):
      if(axis in ['x', 'residx']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.ticksXFormat) or (axis == 'residx')):
          self.ticksXFormat = value
        else:
          redraw = False
      elif(axis in ['x2', 'residx2']):
        if((value != self.ticksXFormat_div) or (axis == 'residx2')):
          self.ticksXFormat_div = value
        else:
          redraw = False
      elif(axis == 'y'):
        if(value != self.ticksYFormat):
          self.ticksYFormat = value
        else:
          redraw = False
      elif(axis == 'y2'):
        if(value != self.ticksY2Format):
          self.ticksY2Format = value
        else:
          redraw = False
      elif(axis == 'resid'):
        if(value != self.ticksResidYFormat):
          self.ticksResidYFormat = value
        else:
          redraw = False
      elif(axis in ['xs', 'residxs']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.slavedXFormat) or (axis == 'residxs')):
          self.slavedXFormat = value
        else:
          redraw = False
      else:
        redraw = False

      self.setTickFormatHelper(axis=axis, redraw=redraw)

  def setFormatTrailZero(self, axis='x', value=True, redraw=True):
    # set trailing zeros of tick labels in custom tick label formatting
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'residx', 'residx2', 'xs', 'residxs']):
      useTicksFormat = 'default'
      if(axis in ['x', 'residx']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.ticksXFormatTrailZero) or (axis == 'residx')):
          self.ticksXFormatTrailZero, useTicksFormat = value, self.ticksXFormat
        else:
          redraw = False
      elif(axis in ['xs', 'residxs']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.slavedXFormatTrailZero) or (axis == 'residxs')):
          self.slavedXFormatTrailZero, useTicksFormat = value, self.slavedXFormat
        else:
          redraw = False
      elif(axis in ['x2', 'residx2']):
        if((value != self.ticksXFormatTrailZero_div) or (axis == 'residx2')):
          self.ticksXFormatTrailZero_div, useTicksFormat = value, self.ticksXFormat_div
        else:
          redraw = False
      elif(axis == 'y'):
        if(value != self.ticksYFormatTrailZero):
          self.ticksYFormatTrailZero, useTicksFormat = value, self.ticksYFormat
        else:
          redraw = False
      elif(axis == 'y2'):
        if(value != self.ticksY2FormatTrailZero):
          self.ticksY2FormatTrailZero, useTicksFormat = value, self.ticksY2Format
        else:
          redraw = False
      elif(axis == 'resid'):
        if(value != self.ticksResidYFormatTrailZero):
          self.ticksResidYFormatTrailZero, useTicksFormat = value, self.ticksResidYFormat
        else:
          redraw = False
      else:
        redraw = False

      # don't redraw when in default mode as changes won't be visible anyhow
      if(useTicksFormat == 'default'):
        redraw = False
      self.setTickFormatHelper(axis=axis, redraw=redraw)

  def setFormatComma(self, axis='x', value=True, redraw=True):
    # set comma as decimal sign in tick labels in custom tick label formatting
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'residx', 'residx2', 'xs', 'residxs']):
      useTicksFormat = 'default'
      if(axis in ['x', 'residx']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.ticksXFormatComma) or (axis == 'residx')):
          self.ticksXFormatComma, useTicksFormat = value, self.ticksXFormat
        else:
          redraw = False
      elif(axis in ['xs', 'residxs']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.slavedXFormatComma) or (axis == 'residxa')):
          self.slavedXFormatComma, useTicksFormat = value, self.slavedXFormat
        else:
          redraw = False
      elif(axis in ['x2', 'residx2']):
        if((value != self.ticksXFormatComma_div) or (axis == 'residx2')):
          self.ticksXFormatComma_div, useTicksFormat = value, self.ticksXFormat_div
        else:
          redraw = False
      elif(axis == 'y'):
        if(value != self.ticksYFormatComma):
          self.ticksYFormatComma, useTicksFormat = value, self.ticksYFormat
        else:
          redraw = False
      elif(axis == 'y2'):
        if(value != self.ticksY2FormatComma):
          self.ticksY2FormatComma, useTicksFormat = value, self.ticksY2Format
        else:
          redraw = False
      elif(axis == 'resid'):
        if(value != self.ticksResidYFormatComma):
          self.ticksResidYFormatComma, useTicksFormat = value, self.ticksResidYFormat
        else:
          redraw = False
      else:
        redraw = False

      # don't redraw when in default mode as changes won't be visible anyhow
      if(useTicksFormat == 'default'):
        redraw = False
      self.setTickFormatHelper(axis=axis, redraw=redraw)

  def setFormatSeparator(self, axis='x', value=True, redraw=True):
    # set separate 1000s of tick labels in custom tick label formatting
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'residx', 'residx2', 'xs', 'residxs']):
      useTicksFormat = 'default'
      if(axis in ['x', 'residx']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.ticksXFormatSeparator) or (axis == 'residx')):
          self.ticksXFormatSeparator, useTicksFormat = value, self.ticksXFormat
        else:
          redraw = False
      elif(axis in ['xs', 'residxs']):
        # always update and redraw for resid plot unless you implement a better check
        if((value != self.slavedXFormatSeparator) or (axis == 'residxs')):
          self.slavedXFormatSeparator, useTicksFormat = value, self.slavedXFormat
        else:
          redraw = False
      elif(axis in ['x2', 'residx2']):
        if((value != self.ticksXFormatSeparator_div) or (axis == 'residx2')):
          self.ticksXFormatSeparator_div, useTicksFormat = value, self.ticksXFormat_div
        else:
          redraw = False
      elif(axis == 'y'):
        if(value != self.ticksYFormatSeparator):
          self.ticksYFormatSeparator, useTicksFormat = value, self.ticksYFormat
        else:
          redraw = False
      elif(axis == 'y2'):
        if(value != self.ticksY2FormatSeparator):
          self.ticksY2FormatSeparator, useTicksFormat = value, self.ticksY2Format
        else:
          redraw = False
      elif(axis == 'resid'):
        if(value != self.ticksResidYFormatSeparator):
          self.ticksResidYFormatSeparator, useTicksFormat = value, self.ticksResidYFormat
        else:
          redraw = False
      else:
        redraw = False

      # don't redraw when in default mode as changes won't be visible anyhow
      if(useTicksFormat != 'float'):
        redraw = False
      self.setTickFormatHelper(axis=axis, redraw=redraw)

  def setFormatFix(self, axis='x', value=True, redraw=True, prefix=True):
    # sets pre/postfix of tick labels in custom tick label formatting
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'xs', 'residx', 'residx2', 'residxs']):
      useTicksFormat = 'default'
      if(axis in ['x', 'residx']):
        # always update and redraw for resid plot unless you implement a better check
        if(prefix):
          if((value != self.ticksXFormatPrefix) or (axis == 'residx')):
            self.ticksXFormatPrefix, useTicksFormat = value, self.ticksXFormat
          else:
            redraw = False
        else:
          if((value != self.ticksXFormatPostfix) or (axis == 'residx')):
            self.ticksXFormatPostfix, useTicksFormat = value, self.ticksXFormat
          else:
            redraw = False
      elif(axis in ['xs', 'residxs']):
        if(prefix):
          if((value != self.slavedXFormatPrefix) or (axis == 'residxs')):
            self.slavedXFormatPrefix, useTicksFormat = value, self.slavedXFormat
          else:
            redraw = False
        else:
          if((value != self.slavedXFormatPostfix) or (axis == 'residx')):
            self.slavedXFormatPostfix, useTicksFormat = value, self.slavedXFormat
          else:
            redraw = False
      elif(axis in ['x2', 'residx2']):
        if(prefix):
          if((value != self.ticksXFormatPrefix_div) or (axis == 'residx2')):
            self.ticksXFormatPrefix_div, useTicksFormat = value, self.ticksXFormat_div
          else:
            redraw = False
        else:
          if((value != self.ticksXFormatPostfix_div) or (axis == 'residx2')):
            self.ticksXFormatPostfix_div, useTicksFormat = value, self.ticksXFormat_div
          else:
            redraw = False
      elif(axis == 'y'):
        if(prefix):
          if(value != self.ticksYFormatPrefix):
            self.ticksYFormatPrefix, useTicksFormat = value, self.ticksYFormat
          else:
            redraw = False
        else:
          if(value != self.ticksYFormatPostfix):
            self.ticksYFormatPostfix, useTicksFormat = value, self.ticksYFormat
          else:
            redraw = False
      elif(axis == 'y2'):
        if(prefix):
          if(value != self.ticksY2FormatPrefix):
            self.ticksY2FormatPrefix, useTicksFormat = value, self.ticksY2Format
          else:
            redraw = False
        else:
          if(value != self.ticksY2FormatPostfix):
            self.ticksY2FormatPostfix, useTicksFormat = value, self.ticksY2Format
          else:
            redraw = False
      elif(axis == 'resid'):
        if(prefix):
          if(value != self.ticksResidYFormatPrefix):
            self.ticksResidYFormatPrefix, useTicksFormat = value, self.ticksResidYFormat
          else:
            redraw = False
        else:
          if(value != self.ticksResidYFormatPostfix):
            self.ticksResidYFormatPostfix, useTicksFormat = value, self.ticksResidYFormat
          else:
            redraw = False
      else:
        redraw = False

      # don't redraw when in default mode as changes won't be visible anyhow
      if(useTicksFormat == 'default'):
        redraw = False
      self.setTickFormatHelper(axis=axis, redraw=redraw)

  def setInnerParameter(self, param='axes', state=True, redraw=True):
    # changes parameters for display of items at split axis interface
    if(param in ['axes', 'ticks']):
      if(param == 'axes'):
        self.innerAxes = state
      else:
        self.innerTicks = state
        
      # apply these settings
      self.updateInnerSituation()

      # issue plot refreshs
      if(redraw):
        self.dataplotwidget.myRefresh()
        self.residplotwidget.myRefresh()

  def updateInnerSituation(self):
    # updates graphic items at interface between split axes
    # do this only when split is activated as otherwise right axis and tick marks will be messed up
    if(self.splitShow):
      # deal with axis spines
      secondAxes = self.isSecondAxesActive()
      self.ax.spines['right'].set_visible(self.innerAxes and self.axisVisible['right'])
      self.ax_div.spines['left'].set_visible(self.innerAxes and self.axisVisible['left'])
      self.ax_resid.spines['right'].set_visible(self.innerAxes and self.axisVisible['right'])
      self.ax_resid_div.spines['left'].set_visible(self.innerAxes and self.axisVisible['left'])
      if(secondAxes):
        self.ax2.spines['right'].set_visible(self.innerAxes and self.axisVisible['right2'])
        self.ax2_div.spines['left'].set_visible(self.innerAxes and self.axisVisible['left2'])
      else:
        # hopefully, this kills the mysterious black line
        self.ax2.spines['right'].set_visible(False)
        self.ax2_div.spines['left'].set_visible(False)
      
      # now deal with ticks
      self.ax.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'], right=self.ticksVisible['right'] and self.innerTicks)
      self.ax_div.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'] and self.innerTicks, right=self.ticksVisible['right'])
      self.ax_resid.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'], right=self.ticksVisible['right'] and self.innerTicks)
      self.ax_resid_div.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'] and self.innerTicks, right=self.ticksVisible['right'])
  
      # ensure that no tick labels displayed on self.ax_div nor on self.ax_resid_div
      self.ax.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left'] and self.ticksYShow, labelright=False)
      self.ax_div.yaxis.set_tick_params(which='both', labelleft=False, labelright=self.ticksLabelShow['right'] and self.ticksYShow)
      self.ax_resid.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left'] and self.ticksResidYShow, labelright=False)
      self.ax_resid_div.yaxis.set_tick_params(which='both', labelleft=False, labelright=self.ticksLabelShow['right'] and self.ticksResidYShow)
  
      # now do the same for second axes
      if(secondAxes):
        # now deal with ticks
        self.ax2.yaxis.set_tick_params(which='both', left=self.ticksVisible['left2'], right=self.ticksVisible['right2'] and self.innerTicks)
        self.ax2_div.yaxis.set_tick_params(which='both', left=self.ticksVisible['left2'] and self.innerTicks, right=self.ticksVisible['right2'])
  
        # ensure that no tick labels displayed on self.ax_div nor on self.ax_resid_div
        self.ax2.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left2'] and self.ticksY2Show, labelright=False)
        self.ax2_div.yaxis.set_tick_params(which='both', labelleft=False, labelright=self.ticksLabelShow['right2'] and self.ticksY2Show)

  def setZOrderResidLine(self, zorder=0, redraw=True):
    # updates z order of residuals
    if(self.zorderResidLine != zorder):
      self.zorderResidLine = zorder
      # update plot if necessary
      plotUpdate = False
      if(self.handleResidZero != None):
        self.handleResidZero.set_zorder(self.zorderResidLine + self.parent.zOffset)
        plotUpdate = True
      if(self.handleResidZero_div != None):
        self.handleResidZero_div.set_zorder(self.zorderResidLine + self.parent.zOffset)
        plotUpdate = True

      # update plot
      if(redraw and plotUpdate):
        self.residplotwidget.myRefresh()

  def setVisibilityResidLine(self, state=True, redraw=True):
    # toggles visibility of residual zero line
    if(self.visibilityResidLine != state):
      self.visibilityResidLine = state
      # update plot if necessary
      plotUpdate = False
      if(self.handleResidZero != None):
        self.handleResidZero.set_visible(state)
        plotUpdate = True
      if(self.handleResidZero_div != None):
        self.handleResidZero_div.set_visible(state)
        plotUpdate = True

      # update plot
      if(redraw and plotUpdate):
        self.residplotwidget.myRefresh()

  def legendHelper(self, axisobject=None):
    # helper function called by legend formatters
    if(axisobject == None):
      axisobject = self.ax
    
    # build axis legend objects
    items = []
    for entry in self.parent.data:
      if((entry.handleData != None) and (entry.visibility)):
        # manually process escape characters
        name = entry.name.replace('\n', '\\n')
        name = name.replace('\t', '\\t')
        name = '\n'.join([i for i in name.split('\\n') if (len(i.strip()))])
        name = '\t'.join([i for i in name.split('\\t') if (len(i.strip()))])
        # now also eliminate double $$ sign b/c mathtext freaks out
        name = name.replace('$$', '')
        # test for potential Mathttext errors by creating a dummy text label
        tempText = axisobject.text(1, 1, name)
        try:
          tempText._get_layout(self.matplot.canvas.renderer)
        except:
          # some kind of problem with item label
          self.parent.statusbar.showMessage('Problems with data set label ' + name, self.parent.STATUS_TIME)
          name = name.replace('$', '')
        tempText.remove()
        if(not name.startswith('_')):
          if((entry.handleData != None) and ((not entry.style['marker'] in ['None', None, '', ' ']) or (entry.style['linestyle'] != 'None'))):
            useHandle = entry.handleData
          elif((entry.handleScatter != None) and entry.Scatterstyle['showScatter']):
            useHandle = entry.handleScatter
          elif((entry.handleBar != None) and entry.Barstyle['showBar']):
            useHandle = entry.handleBar
          elif((entry.handleStack != None) and entry.Stackstyle['showStack']):
            useHandle = entry.handleStack
          else:
            useHandle = entry.handleData
          items.append([useHandle, name, entry.zorder])
    for entry in self.parent.fit:
      if((entry.handlePlot != None) and (entry.visibility)):
        # manually process escape characters
        name = entry.name.replace('\n', '\\n')
        name = name.replace('\t', '\\t')
        name = '\n'.join([i for i in name.split('\\n') if (len(i.strip()))])
        name = '\t'.join([i for i in name.split('\\t') if (len(i.strip()))])
        # now also eliminate double $$ sign b/c mathtext freaks out
        name = name.replace('$$', '')
        # test for potential Mathttext errors by creating a dummy text label
        tempText = axisobject.text(1, 1, name)
        try:
          tempText._get_layout(self.matplot.canvas.renderer)
        except:
          # some kind of problem with item label
          self.parent.statusbar.showMessage('Problems with curve label ' + name, self.parent.STATUS_TIME)
          name = name.replace('$', '')
        tempText.remove()
        if(not name.startswith('_')):
          items.append([entry.handlePlot, name, entry.zorder])
    # order according to zorder
    items = sorted(items, key=lambda k: k[2])
    handles = [i[0] for i in items]
    labels = [i[1] for i in items]
    bbox_to_anchor = (self.padSize['left'], self.padSize['bottom'],\
                      self.padSize['right'] - self.padSize['left'], self.padSize['top'] - self.padSize['bottom'])
    
    # have to discriminate for Linux versions b/c markerfirst may be unknown
    if(self.legendPlacement == 'custom'):
      location = (self.legendX, self.legendY)
    else:
      location = self.legendPlacement
    try:
      self.legendHandle = self.createCustomLegend(axisobject, handles, labels, loc=location, shadow=self.legendShadow,\
        numpoints=self.legendNumPoints, markerfirst=self.legendMarkerFirst, ncol=self.legendNumCol, markerscale=self.legendMarkerScale,\
        borderpad=self.legendBorderPad, labelspacing=self.legendLabelSpacing, columnspacing=self.legendColumnSpacing,\
        handlelength=2.0 * self.legendMarkerScale, handletextpad=self.legendLabelPad)#, bbox_to_anchor=bbox_to_anchor)
    except:
      self.legendHandle = self.createCustomLegend(axisobject, handles, labels, loc=location, shadow=self.legendShadow,\
        numpoints=self.legendNumPoints, ncol=self.legendNumCol, markerscale=self.legendMarkerScale,\
        borderpad=self.legendBorderPad, labelspacing=self.legendLabelSpacing, columnspacing=self.legendColumnSpacing,\
        handlelength=2.0 * self.legendMarkerScale, handletextpad=self.legendLabelPad, bbox_to_anchor=bbox_to_anchor)
    
    if(self.legendHandle != None):
      # go via frame properties for enhanced controls
      frame = self.legendHandle.get_frame()
      if(frame != None):
        frame.set_linewidth(self.legendEdgeWidth)
        if(self.legendEdge):
          frame.set_edgecolor(self.legendColor['edge'])
        else:
          frame.set_edgecolor('none')
        if(self.legendFill):
          frame.set_facecolor(self.legendColor['face'])
        else:
          frame.set_facecolor('none')
        frame.set_alpha(self.legendColor['face'][-1])
      
      # set text properties
      texts = self.legendHandle.texts
      for entry in texts:
        entry.set_color(self.legendLabelColor)
        entry.set_fontsize(self.legendLabelSize)
        entry.set_fontweight(self.legendLabelWeight)
        entry.set_fontstyle(self.legendLabelStyle)
        entry.set_fontname(self.legendLabelFont)
        entry.set_linespacing(self.legendLabelLinespacing)
        
      # set additional config settings (brought to you by AM)
      self.legendHandle.additionalConfig(shadowDeltaX=self.legendShadowDeltaX, shadowDeltaY=self.legendShadowDeltaY,\
                                         shadowFaceColor=self.legendShadowFaceColor, legendHatch=self.legendHatch,\
                                         legendHatchMultiply=self.legendHatchMultiply, legendHatchLinewidth=self.legendHatchLinewidth,\
                                         legendEdgeFallbackColor=self.legendColor['edge'],\
                                         legendRounding=self.legendRounding)
      
      # set z-order to display in front (note that aboutLogo is at 1000)
      self.legendHandle.set_zorder(999)
      
  def createCustomLegend(self, axisobject, handles, labels, *args, **kwargs):
    # adapter function to allow generation of custom legend
    # the proper way throws an error under Linux due to matplotlib versions
    # => better avoid this call, which we can as we have a fairly good idea which arguments we pass
    ###handles, labels, extra_args, kwargs = Legend._parse_legend_args([axisobject], *args, **kwargs)
    ###self.legend_ = MyLegend(axisobject, handles, labels, **kwargs)
    # put the legend on the figure, not on the axes => better for split plots and the likes
    if(('loc' in kwargs) and (not (type(kwargs['loc']) is tuple)) and (not (kwargs['loc'] in self.placementStyles))):
      kwargs['loc'] = self.placementStyles[0]
    if(('loc' in kwargs) and (kwargs['loc'] == 'best')):
      # in case of 'best' location, have to use axis object not figure (makes kinda sense)
      self.legend_ = MyLegend(axisobject, handles, labels, **kwargs)
    else:
      self.legend_ = MyLegend(self.matplot, handles, labels, **kwargs)
    # trick axisobject into accepting MyLegend
    try:
      self.legend_._remove_method = axisobject._remove_legend
    except:
      self.legend_._remove_method = lambda h: setattr(self, 'legend_', None)

    axisobject.legend_ = self.legend_
    return self.legend_

  def toggleLegendEdge(self, value=True, redraw=True, target='plot'):
    # toggles whether legend is boxed or not
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      # sets canvas color
      if(self.legendEdge == value):
        redraw = False

      self.legendEdge = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def toggleLegendFill(self, value=True, redraw=True, target='plot'):
    # toggles whether legend is filled or not
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      # sets canvas color
      if(self.legendFill == value):
        redraw = False

      self.legendFill = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendMarkerFirst(self, value=True, redraw=True, target='plot'):
    # sets marker location in legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      if(self.legendMarkerFirst == value):
        redraw = False
        
      self.legendMarkerFirst= value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendMarkerScale(self, value=1.0, redraw=True, target='plot'):
    # sets marker scale in legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      if(self.legendMarkerScale == value):
        redraw = False
        
      self.legendMarkerScale = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendNCol(self, value=1, redraw=True, target='plot'):
    # sets number of columns in legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      if(self.legendNumCol == value):
        redraw = False
        
      self.legendNumCol = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendMarkerNumPoints(self, value=1, redraw=True, target='plot'):
    # sets number of marker points in legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      if(self.legendNumPoints == value):
        redraw = False
        
      self.legendNumPoints = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()
          
  def setLegendPlacement(self, value='upper right', redraw=True, target='plot'):
    # sets placement of legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      if(self.legendPlacement == value):
        redraw = False
        
      self.legendPlacement = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendShadow(self, value=False, redraw=True, target='plot'):
    # sets placement of legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      if(self.legendShadow == value):
        redraw = False
        
      self.legendShadow = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegend(self, value=True, redraw=True, target='plot'):
    # sets legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
      
      self.legendVisible = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
      else:
        legend = self.legendHandle
        if(legend != None):
          try:
            legend.remove()
          except:
            pass
          self.ax.legend_ = None
          self.legendHandle = None
          
      if(redraw):
        plotobject.myRefresh()

  def setLegendColor(self, value=[0.5, 0.5, 0.5, 0.5], prop='face', redraw=True, target='plot'):
    # sets color of legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
    else:
      prop = 'abort'

    # update color
    if(prop in ['face', 'edge']):
      if(self.legendColor[prop] == value):
        redraw=False
      if((prop == 'face') and (not self.legendFill)):
        redraw = False
      if((prop == 'edge') and (not self.legendEdge) and (self.legendHatch == '')):
        redraw = False
        
      self.legendColor[prop] = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendRounding(self, value=0.2, redraw=True, target='plot'):
    # alters rounding for legend box
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      if(self.legendRounding == value):
        redraw = False
      self.legendRounding = value

      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendPosition(self, axis='x', value=0.2, redraw=True, target='plot'):
    # alters position for legend box
    if(axis in ['x', 'y']):
      if(target in ['plot', 'resid']):
        if(target == 'plot'):
          plotobject, axisobject = self.dataplotwidget, self.ax
        else:
          plotobject, axisobject = self.residplotwidget, self.ax_resid
  
        if(axis == 'x'):
          if(self.legendX == value):
            redraw = False
          else:
            # switch to custom placement when this value changes
            self.legendPlacement = 'custom'
          self.legendX = value
        else:
          if(self.legendY == value):
            redraw = False
          else:
            # switch to custom placement when this value changes
            self.legendPlacement = 'custom'
          self.legendY = value
  
        if(self.legendVisible):
          self.legendHelper(axisobject)
          if(redraw):
            plotobject.myRefresh()

  def setLegendHatchParameter(self, item='hatch', value='', redraw=True, target='plot'):
    # alters parameter for legend hatch
    if(item in ['hatch', 'multiply', 'linewidth']):
      if(target in ['plot', 'resid']):
        if(target == 'plot'):
          plotobject, axisobject = self.dataplotwidget, self.ax
        else:
          plotobject, axisobject = self.residplotwidget, self.ax_resid

        if(item == 'linewidth'):
          if(self.legendHatchLinewidth == value):
            redraw = False
          self.legendHatchLinewidth = value
        elif(item == 'hatch'):
          if(self.legendHatch == value):
            redraw = False
          self.legendHatch = value
        else:
          if(self.legendHatchMultiply == value):
            redraw = False
          self.legendHatchMultiply = value
          
        if(self.legendVisible):
          self.legendHelper(axisobject)
          if(redraw):
            plotobject.myRefresh()

  def setLegendShadowParameter(self, item='color', value=[0.5, 0.5, 0.5, 1.0], redraw=True, target='plot'):
    # alters parameter for legend shadow
    if(item in ['color', 'x', 'y']):
      if(target in ['plot', 'resid']):
        if(target == 'plot'):
          plotobject, axisobject = self.dataplotwidget, self.ax
        else:
          plotobject, axisobject = self.residplotwidget, self.ax_resid

        if(item == 'color'):
          if(self.legendShadowFaceColor == value):
            redraw = False
          self.legendShadowFaceColor = value
        elif(item == 'x'):
          if(self.legendShadowDeltaX == value):
            redraw = False
          self.legendShadowDeltaX = value
        else:
          if(self.legendShadowDeltaY == value):
            redraw = False
          self.legendShadowDeltaY = value
          
        if(self.legendVisible and self.legendShadow):
          self.legendHelper(axisobject)
          if(redraw):
            plotobject.myRefresh()

  def setLegendLabelColor(self, value=[0.5, 0.5, 0.5, 0.5], redraw=True, target='plot'):
    # sets color of legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # update color
      if(self.legendLabelColor == value):
        redraw = False
        
      self.legendLabelColor = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendLabelBold(self, value='normal', redraw=True, target='plot'):
    # sets formatting of legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # update color
      if(self.legendLabelWeight == value):
        redraw = False
        
      self.legendLabelWeight = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendLabelLinespacing(self, value=1., redraw=True, target='plot'):
    # sets linescpacing of legend entries
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # update color
      if(self.legendLabelLinespacing == value):
        redraw = False
        
      self.legendLabelLinespacing = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendLabelItalic(self, value='normal', redraw=True, target='plot'):
    # sets formatting of legend
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # update color
      if(self.legendLabelStyle == value):
        redraw = False
        
      self.legendLabelStyle = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendEdgeWidth(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      if((self.legendEdgeWidth == value) or (not self.legendEdge)):
        redraw = False
        
      self.legendEdgeWidth = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendPadBorder(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      if(self.legendBorderPad == value):
        redraw = False
        
      self.legendBorderPad = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendPadRow(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      if(self.legendLabelSpacing == value):
        redraw = False
        
      self.legendLabelSpacing = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendPadCol(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      if(self.legendColumnSpacing == value):
        redraw = False
        
      self.legendColumnSpacing = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendPadLabel(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      if(self.legendLabelPad == value):
        redraw = False
        
      self.legendLabelPad = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendLabelSize(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      if(self.legendLabelSize == value):
        redraw = False
        
      self.legendLabelSize = value
      if(self.legendVisible):
        self.legendHelper(axisobject)
        if(redraw):
          plotobject.myRefresh()

  def setLegendLabelFont(self, value='DejaVu Sans', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject = self.dataplotwidget, self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid

      # sets legend edge width
      prevFont = self.legendLabelFont
      if(self.legendLabelFont == value):
        redraw = False
        
      self.legendLabelFont = value
      self.legendHelper(axisobject)
      
      # have to capture errors in case a strange font is set
      try:
        if(redraw):
          plotobject.myRefresh()
      except:
        self.parent.statusbar.showMessage('Experiencing problems setting font ' + self.legendLabelFont + ' -- reverting to ' + prevFont, self.parent.STATUS_TIME)

        self.legendLabelFont = prevFont
        self.legendHelper(axisobject)
        # also capture errors with previous font (can happen if selecting two bad fonts in a row)
        try:
          if(redraw):
            plotobject.myRefresh()
        except:
          ###safeFont = 'DejaVu Sans'
          self.parent.statusbar.showMessage('Also experiencing problems setting font ' + self.legendLabelFont + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
          self.legendLabelFont = SAFE_FONT
          self.legendHelper(axisobject)

  def setDataAxisYTicks(self, dataSet=0, redraw=True, target='plot', axis='y'):
    # set y ticks to label values
    useData, roles = self.parent.data[dataSet].getData_n_Fit()
    if(('y' in roles) and (target == 'plot')):
      ycol = roles.index('y')
      yval = list(useData[:, ycol])
      labels2 = list(self.parent.data[self.parent.activeData].getLabels(labels2=True))
      minLength = np.min((len(yval), len(labels2)))
      if(minLength):
        # we have some labels to place
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          axisobject, useModeY, useMinY, useMaxY, useTicksYMinor = self.ax2, self.modeY, self.minY, self.maxY, self.ticksYMinor
          if(axis == 'y'):
            axisobject = self.ax
        else:
          # y labels on resid plot not implemented yet
          pass

        flag = False
        labels2 = labels2[:minLength]
        yval = yval[:minLength]

        # manually process labels for escape characters
        for index, entry in enumerate(labels2):
          # ensure that labels are string
          if(type(entry) != type('tse')):
            entry = str(entry)
          name = entry.replace('\n', '\\n')
          name = name.replace('\t', '\\t')
          name = '\n'.join([i for i in name.split('\\n') if (len(i.strip()))])
          name = '\t'.join([i for i in name.split('\\t') if (len(i.strip()))])
          # now also eliminate double $$ sign b/c mathtext freaks out
          name = name.replace('$$', '')
          labels2[index] = name
        
        # first set new ticks
        axisobject.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(yval))
        if(1):
          minorAutoticks = MyAutoMinorLocator(useTicksYMinor)
          axisobject.yaxis.set_minor_locator(minorAutoticks)
          axisobject.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        lower, upper = useMinY, useMaxY
        if(len(yval)):
          # check for empty list
          if(np.min(yval) < np.min((useMinY, useMaxY))):
            flag = True
          if(np.max(yval) > np.max((useMinY, useMaxY))):
            flag = True
            
        # now set new tick labels
        axisobject.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(labels2))
        self.ticksYDataSet = self.parent.activeData
        
        # store settings
        if(axis == 'y'):
          self.ticksY, self.ticksYLabel, self.ticksYAuto = yval, labels2, False
          self.parent.graphicsarea.configTickYAuto.setChecked(False)
        else:
          self.ticksY2, self.ticksY2Label, self.ticksY2Auto = yval, labels2, False
          self.parent.graphicsarea2.configTickYAuto.setChecked(False)

        # check whether the new ticks necessitate axis rescaling
        if(flag):
          self.setAxisLimits(lower=lower, upper=upper, axis=axis, updateLabel=True, target=target, redraw=False, updateGrid=True)
        else:
          # update axis grid lines
          if(self.gridVisible[axis]):
            self.drawAxisGrid(axis=axis, redraw=False, target=target)
        
          if(redraw):
            plotobject.myRefresh()
      else:
        self.parent.statusbar.showMessage('Data set ' + str(dataSet) + ' contains no labels (use labels2 for y axis)!', self.parent.STATUS_TIME)
    else:
      self.parent.statusbar.showMessage('Current data set ' + str(dataSet) + ' is empty!', self.parent.STATUS_TIME)

  def setDataAxisTicks(self, dataSet=0, redraw=True, target='plot', splitX=False):
    # set x ticks to label values
    useData, roles = self.parent.data[dataSet].getData_n_Fit()
    if('x' in roles):
      xcol = roles.index('x')
      xval = list(useData[:, xcol])
      labels = list(self.parent.data[self.parent.activeData].getLabels())
      minLength = np.min((len(xval), len(labels)))
      if(minLength):
        # we have some labels to place
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(splitX):
            axisobject, useModeX, useMinX, useMaxX, useTicksXMinor = self.ax2_div, self.modeX_div, self.minX_div, self.maxX_div, self.ticksXMinor_div
            axisItems = [self.ax_div, self.ax2_div]
          else:
            axisobject, useModeX, useMinX, useMaxX, useTicksXMinor = self.ax2, self.modeX, self.minX, self.maxX, self.ticksXMinor
            axisItems = [self.ax, self.ax2]
        else:
          plotobject = self.residplotwidget
          if(splitX):
            axisobject, useModeX, useMinX, useMaxX, useTicksXMinor = self.ax_resid_div, self.modeX_div, self.minX_div, self.maxX_div, self.ticksXMinor_div
            axisItems = [self.ax_resid_div]
          else:
            axisobject, useModeX, useMinX, useMaxX, useTicksXMinor = self.ax_resid, self.modeX, self.minX, self.maxX, self.ticksXMinor
            axisItems = [self.ax_resid]

        flag = False
        labels = labels[:minLength]
        xval = xval[:minLength]

        # manually process labels for escape characters
        for index, entry in enumerate(labels):
          # ensure that labels are string
          if(type(entry) != type('tse')):
            entry = str(entry)
          name = entry.replace('\n', '\\n')
          name = name.replace('\t', '\\t')
          name = '\n'.join([i for i in name.split('\\n') if (len(i.strip()))])
          name = '\t'.join([i for i in name.split('\\t') if (len(i.strip()))])
          # now also eliminate double $$ sign b/c mathtext freaks out
          name = name.replace('$$', '')
          labels[index] = name
        
        # first set new ticks
        for useAxis in axisItems:
          useAxis.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(xval))
        ###if(useModeX == 'linear'):
        if(1):
          minorAutoticks = MyAutoMinorLocator(useTicksXMinor)
          for useAxis in axisItems:
            useAxis.xaxis.set_minor_locator(minorAutoticks)
            useAxis.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        lower, upper = useMinX, useMaxX
        if(len(xval)):
          # check for empty list
          if(np.min(xval) < np.min((useMinX, useMaxX))):
            flag = True
            # disable rescaling on assignment of axis labels
            ###lower = np.min(xval)
          if(np.max(xval) > np.max((useMinX, useMaxX))):
            flag = True
            # disable rescaling on assignment of axis labels
            ###upper = np.max(xval)
          # special treatment for resid plot
          if(target == 'resid'):
            flag = True
            
        # now set new tick labels
        axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(labels))
        self.ticksXDataSet = self.parent.activeData
        
        # store settings
        if(splitX):
          self.ticksX_div, self.ticksXLabel_div, self.ticksXAuto_div = xval, labels, False
          self.parent.graphicsarea.configInnerTickXAuto.setChecked(False)
        else:
          self.ticksX, self.ticksXLabel, self.ticksXAuto = xval, labels, False
          self.parent.graphicsarea.configTickXAuto.setChecked(False)

        # check whether the new ticks necessitate axis rescaling
        if(flag):
          if(splitX):
            self.setAxisLimits(lower=lower, upper=upper, axis='x2', updateLabel=True, target=target, redraw=False, updateGrid=True)
            if(target == 'plot'):
              # and we should redraw the fit function to cover new x-range
              self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
                fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div,\
                handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=splitX)
              # copy in case split axes are shown
              curve = self.parent.fit[self.parent.activeFit]
              if(self.parent.plotArea.splitY and curve.onBothAxes):
                curve.duplicateForSplit()
              if(redraw):
                self.parent.plotArea.dataplotwidget.myRefresh()
            else:
              # and we should update the resid plot (as x-axis will most likely have rescaled)
              self.parent.plotArea.handleResidZero_div = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero_div, redraw=redraw, splitX=splitX)
          else:
            self.setAxisLimits(lower=lower, upper=upper, axis='x', updateLabel=True, target=target, redraw=False, updateGrid=True)
            if(target == 'plot'):
              # and we should redraw the fit function to cover new x-range
              self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
                fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot,\
                handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
              # copy in case split axes are shown
              curve = self.parent.fit[self.parent.activeFit]
              if(self.parent.plotArea.splitY and curve.onBothAxes):
                curve.duplicateForSplit()
              if(redraw):
                self.parent.plotArea.dataplotwidget.myRefresh()
            else:
              # and we should update the resid plot (as x-axis will most likely have rescaled)
              self.parent.plotArea.handleResidZero = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero, redraw=redraw)
        else:
          # update axis grid lines
          if(splitX):
            if(self.gridVisible['x2']):
              self.drawAxisGrid(axis='x2', redraw=False, target=target)
          else:
            if(self.gridVisible['x']):
              self.drawAxisGrid(axis='x', redraw=False, target=target)
        
          if(redraw):
            plotobject.myRefresh()
      else:
        self.parent.statusbar.showMessage('Data set ' + str(dataSet) + ' contains no labels!', self.parent.STATUS_TIME)
    else:
      self.parent.statusbar.showMessage('Current data set ' + str(dataSet) + ' is empty!', self.parent.STATUS_TIME)

  def myFormatterFunction(self, mode='float', precision=2, trailZero=True, separator=True, comma=True, prefix='', postfix='', x=0, pos=None):
    # a custom formatter function
    prefix, postfix = prefix.replace('\\', '\\\\'), postfix.replace('\\', '\\\\')
    prefix, postfix = prefix.replace('$', '\\$'), postfix.replace('$', '\\$')
    if(mode == 'float'):
      # for float formatting consider separator for 1000s (by contrast, not relevant for scientific/mathdefault)
      if(separator):
        formatstr = '{:,.' + str(precision) + 'f}'
      else:
        formatstr = '{:.' + str(precision) + 'f}'
      formatstr = formatstr.format(x)
      # check whether commata should be used rather than decimal points
      if(comma):
        checkItem = ','
        formatstr = formatstr.replace('.', '|')
        formatstr = formatstr.replace(',', '.')
        formatstr = formatstr.replace('|', ',')
      else:
        checkItem = '.'
      # test at level of output string, as is_integer() runs into problems with precision of floats
      if((not trailZero) and (checkItem in formatstr)):
        while(formatstr.endswith('0')):
          formatstr = formatstr[:-1]
        if(formatstr.endswith(checkItem)):
          formatstr = formatstr[:-1]
      # need to enclose commata in curly brackets to prevent trailing space
      formatstr = formatstr.replace(',', '{,}')
      formatstr = '$\\mathdefault{' + formatstr + '}$'
      return prefix + formatstr + postfix
    elif(mode == 'scientific'):
      formatstr = '{:.' + str(precision) + 'e}'
      formatstr = formatstr.format(x)
      if(not trailZero):
        if('e' in formatstr.lower()):
          eString = formatstr[formatstr.lower().find('e')]
          preString, exponentString = formatstr.split(eString)
          while(preString.endswith('0')):
            preString = preString[:-1]
          if(preString.endswith('.')):
            preString = preString[:-1]
          formatstr = preString + eString + exponentString
      if(comma):
        formatstr = formatstr.replace('.', '{,}')
      formatstr = '$\\mathdefault{' + formatstr + '}$'
      return prefix + formatstr + postfix
    elif(mode == 'mathtext'):
      # calculate exponent
      if(x != 0.0):
        try:
          exponent = int(np.floor(np.log10(np.abs(x))))
        except:
          exponent = 0
        # calculate preexponent
        try:
          pre = x / (10 ** exponent)
        except:
          pre, exponent = x, 0
      # assemble string
      if(x == 0.0):
        retstr = '{:.' + str(precision) + 'f}'
        retstr = retstr.format(0.0)
        if(not trailZero):
          retstr = '0'
        if(comma):
          retstr = retstr.replace('.', '{,}')
        retstr = '$\\mathdefault{' + retstr + '}$' 
      elif(np.isclose(pre, 1.0)):
        retstr = '$\\mathdefault{10^{' + str(exponent) + '}}$'
      elif(np.isclose(pre, -1.0)):
        retstr = '$\\mathdefault{-10^{' + str(exponent) + '}}$'
      else:
        retstr = '{:.' + str(precision) + 'f}'
        retstr = retstr.format(pre)
        # testing with is_integer() struggles with float precision - test directly at level of string
        #if((not trailZero) and pre.is_integer()):
        if((not trailZero) and precision):
          front, trail = retstr.split('.')
          while(trail.endswith('0')):
            trail = trail[:-1]
          if(trail == ''):
            retstr = front
          else:
            retstr = front + '.' + trail
        if(comma):
          retstr = retstr.replace('.', '{,}')
        # should not use \times here as some fonts don't possess this symbol
        retstr = '$\\mathdefault{' + retstr + '\\ x\\ 10^{' + str(exponent) + '}}$' 
      # return value
      return prefix + retstr + postfix
    elif(mode == 'engineer'):
      # the following adapted from EngFormatter
      SI_PREFIXES = {-30: "q", -27: "r", -24: "y", -21: "z", -18: "a", -15: "f", -12: "p", -9: "n",
                     -6: "\N{MICRO SIGN}", -3: "m", 0: "", 3: "k", 6: "M", 9: "G", 12: "T", 15: "P",
                     18: "E", 21: "Z", 24: "Y", 27: "R", 30: "Q"}

      # calculate exponent
      if(x != 0.0):
        try:
          exponent = int(np.floor(np.log10(np.abs(x))))
          exponent = int(np.floor(exponent / 3.) * 3.)
        except:
          exponent = 0

        pow10 = np.clip(exponent, min(SI_PREFIXES), max(SI_PREFIXES))
        sistring = SI_PREFIXES[int(pow10)]

        # calculate preexponent
        try:
          # need to explicitly convert to int to avoid numpy type incompatibility
          pre = x / (10 ** int(pow10))
        except:
          pre, exponent = x, 0

      # assemble string
      if(x == 0.0):
        retstr = '{:.' + str(precision) + 'f}'
        retstr = retstr.format(0.0)
        if(not trailZero):
          retstr = '0'
      else:
        retstr = '{:.' + str(precision) + 'f}'
        if(np.isclose(pre, 1.0)):
          retstr = retstr.format(1)
        elif(np.isclose(pre, -1.0)):
          retstr = retstr.format(-1)
        else:
          retstr = retstr.format(pre)

        if((not trailZero) and precision):
          front, trail = retstr.split('.')
          while(trail.endswith('0')):
            trail = trail[:-1]
          if(trail == ''):
            retstr = front
          else:
            retstr = front + '.' + trail

        # deal with delimiter
        if(comma):
          retstr = retstr.replace('.', '{,}')
        # now append the si prefix
        retstr = '$\\mathdefault{' + retstr + '\\,' + SI_PREFIXES[pow10] + '}$'
      # return value
      return prefix + retstr + postfix
    else:
      return str(x)

  def setAutoTicks(self, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject, axisobject2 = self.dataplotwidget, self.ax2, self.ax2_div
        axisItems = [self.ax, self.ax2]
        if(axis in ['x2']):
          axisItems = [self.ax_div, self.ax2_div]
      else:
        plotobject, axisobject, axisobject2 = self.residplotwidget, self.ax_resid, self.ax_resid_div
        axisItems = [self.ax_resid]
        if(axis in ['x2']):
          axisItems = [self.ax_resid_div]
    else:
      axis = 'abort'
    # automatically sets axis ticks
    if(axis in ['x', 'y', 'y2', 'resid', 'x2']):
      if(axis in ['x', 'x2']):
        if(axis == 'x'):
          useModeX, useAxisobject, useTicksXFormat, useTicksXFormatPrecision, useTicksXMinor, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma =\
            self.modeX, axisobject, self.ticksXFormat, self.ticksXFormatPrecision, self.ticksXMinor, self.ticksXFormatTrailZero, self.ticksXFormatSeparator, self.ticksXFormatComma
          usePrefix, usePostfix = self.ticksXFormatPrefix, self.ticksXFormatPostfix
        else:
          useModeX, useAxisobject, useTicksXFormat, useTicksXFormatPrecision, useTicksXMinor, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma =\
            self.modeX_div, axisobject2, self.ticksXFormat_div, self.ticksXFormatPrecision_div, self.ticksXMinor_div, self.ticksXFormatTrailZero_div, self.ticksXFormatSeparator_div, self.ticksXFormatComma_div
          usePrefix, usePostfix = self.ticksXFormatPrefix_div, self.ticksXFormatPostfix_div

        if(useModeX == 'linear'):
          autoticks = matplotlib.ticker.AutoLocator()
          autolabels = matplotlib.ticker.ScalarFormatter()
          minorAutoticks = MyAutoMinorLocator(useTicksXMinor)
          for useAxis in axisItems:
            useAxis.xaxis.set_minor_locator(minorAutoticks)
            useAxis.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        else:
          autoticks = matplotlib.ticker.LogLocator()
          ###if(platform in ['linux', 'darwin']):
          try:
            autolabels = matplotlib.ticker.LogFormatterSciNotation()
          except:
            autolabels = matplotlib.ticker.ScalarFormatter()
          minorAutoticks = MyAutoMinorLocator(useTicksXMinor)
          for useAxis in axisItems:
            useAxis.xaxis.set_minor_locator(minorAutoticks)
            useAxis.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # apply custom formatting?
        if(useTicksXFormat != 'default'):
          # store fallback
          if(axis == 'x'):
            self.fallback_ticksXFormat = autolabels
          else:
            self.fallback_ticksXFormat_div = autolabels
          autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, useTicksXFormat, useTicksXFormatPrecision, useTicksFormatTrailZero, useTicksFormatSeparator, useTicksFormatComma, usePrefix, usePostfix))
        for useAxis in axisItems:
          useAxis.xaxis.set_major_locator(autoticks)
        useAxisobject.xaxis.set_major_formatter(autolabels)

        # store information
        if(axis == 'x'):
          self.ticksX = self.getAxisTicks(axis)
          nuticks = self.ticksX
          # clear labels
          self.ticksXLabel, self.ticksXAuto, self.ticksXDataSet = [], True, -1
        else:
          self.ticksX_div = self.getAxisTicks(axis)
          nuticks = self.ticksX_div
          # clear labels
          self.ticksXLabel_div, self.ticksXAuto_div = [], True
      elif(axis == 'y'):
        axisobject = self.ax; axisobject2 = self.ax_div
        for useAxisobject in [axisobject, axisobject2]:
          if(self.modeY == 'linear'):
            autoticks = matplotlib.ticker.AutoLocator()
            autolabels = matplotlib.ticker.ScalarFormatter()
            minorAutoticks = MyAutoMinorLocator(self.ticksYMinor)
            useAxisobject.yaxis.set_minor_locator(minorAutoticks)
            useAxisobject.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
          else:
            autoticks = matplotlib.ticker.LogLocator()
            ###if(platform in ['linux', 'darwin']):
            try:
              autolabels = matplotlib.ticker.LogFormatterSciNotation()
            except:
              autolabels = matplotlib.ticker.ScalarFormatter()
            minorAutoticks = MyAutoMinorLocator(self.ticksYMinor)
            useAxisobject.yaxis.set_minor_locator(minorAutoticks)
            useAxisobject.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
          # apply custom formatting?
          if(self.ticksYFormat != 'default'):
            # store fallback
            self.fallback_ticksYFormat = autolabels
            autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksYFormat, self.ticksYFormatPrecision, self.ticksYFormatTrailZero, self.ticksYFormatSeparator, self.ticksYFormatComma, self.ticksYFormatPrefix, self.ticksYFormatPostfix))
          useAxisobject.yaxis.set_major_locator(autoticks)
          useAxisobject.yaxis.set_major_formatter(autolabels)
        # store information
        self.ticksY = self.getAxisTicks(axis)
        nuticks = self.ticksY
        self.ticksYAuto = True
      elif(axis == 'y2'):
        for useAxisobject in [axisobject, axisobject2]:
          if(self.modeY2 == 'linear'):
            autoticks = matplotlib.ticker.AutoLocator()
            autolabels = matplotlib.ticker.ScalarFormatter()
            minorAutoticks = MyAutoMinorLocator(self.ticksY2Minor)
            useAxisobject.yaxis.set_minor_locator(minorAutoticks)
            useAxisobject.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
          else:
            autoticks = matplotlib.ticker.LogLocator()
            ###if(platform in ['linux', 'darwin']):
            try:
              autolabels = matplotlib.ticker.LogFormatterSciNotation()
            except:
              autolabels = matplotlib.ticker.ScalarFormatter()
            minorAutoticks = MyAutoMinorLocator(self.ticksY2Minor)
            useAxisobject.yaxis.set_minor_locator(minorAutoticks)
            useAxisobject.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
          # apply custom formatting?
          if(self.ticksY2Format != 'default'):
            # store fallback
            self.fallback_ticksY2Format = autolabels
            autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksY2Format, self.ticksY2FormatPrecision, self.ticksY2FormatTrailZero, self.ticksY2FormatSeparator, self.ticksY2FormatComma, self.ticksY2FormatPrefix, self.ticksY2FormatPostfix))
          useAxisobject.yaxis.set_major_locator(autoticks)
          useAxisobject.yaxis.set_major_formatter(autolabels)
        # store information
        self.ticksY2 = self.getAxisTicks(axis)
        nuticks = self.ticksY2
        self.ticksY2Auto = True
      else:
        for useAxisobject in [axisobject, axisobject2]:
          autoticks = matplotlib.ticker.AutoLocator()
          autolabels = matplotlib.ticker.ScalarFormatter()
          # apply custom formatting?
          if(self.ticksResidYFormat != 'default'):
            # store fallback
            self.fallback_ticksResidYFormat = autolabels
            autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksResidYFormat, self.ticksResidYFormatPrecision, self.ticksResidYFormatTrailZero, self.ticksResidYFormatSeparator, self.ticksResidYFormatComma, self.ticksResidYFormatPrefix, self.ticksResidYFormatPostfix))
          minorAutoticks = MyAutoMinorLocator(self.ticksResidYMinor)
          useAxisobject.yaxis.set_major_locator(autoticks)
          useAxisobject.yaxis.set_minor_locator(minorAutoticks)
          useAxisobject.yaxis.set_major_formatter(autolabels)
          useAxisobject.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # store information
        self.ticksResidY = self.getAxisTicks(axis)
        nuticks = self.ticksResidY
        self.ticksResidYAuto = True

      # update axis grid lines
      if(axis == 'resid'):
        item = 'y'
      else:
        item = axis
      if(self.gridVisible[item]):
        if((item != 'y2') or self.isSecondAxesActive()):
          self.drawAxisGrid(axis=item, redraw=False, target=target)
      
      if(redraw):
        plotobject.myRefresh()
      return nuticks

  def setAxisTicks(self, value=np.array([]), axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject, axisname, useTicksXMinor = self.dataplotwidget, self.ax2, 'ax2', self.ticksXMinor
        if(axis == 'x2'):
          axisobject, useTicksXMinor = self.ax2_div, self.ticksXMinor_div
          axisItems = [self.ax_div, self.ax2_div]
        elif(axis == 'y'):
          axisItems = [self.ax, self.ax_div]
        elif(axis == 'y2'):
          axisItems = [self.ax2, self.ax2_div]
        else:
          axisItems = [self.ax, self.ax2]
      else:
        plotobject, axisobject, axisname, useTicksXMinor = self.residplotwidget, self.ax_resid, 'ax_resid', self.ticksXMinor
        if(axis == 'x2'):
          axisobject, useTicksXMinor = self.ax_resid_div, self.ticksXMinor_div
          axisItems = [self.ax_resid_div]
        elif(axis == 'resid'):
          axisItems = [self.ax_resid, self.ax_resid_div]
        else:
          axisItems = [self.ax_resid]
    else:
      axis='abort'
    # sets axis ticks
    if(axis in ['x', 'y', 'y2', 'resid', 'x2']):
      flag = False
      if(axis == 'x'):
        if(self.modeX == 'linear'):
          autolabels = matplotlib.ticker.ScalarFormatter()
          formatterName = 'matplotlib.ticker.ScalarFormatter()'
        else:
          autolabels = matplotlib.ticker.LogFormatterSciNotation()
          ###if(platform in ['linux', 'darwin']):
          try:
            blah = matplotlib.ticker.LogFormatterSciNotation()
            formatterName = 'matplotlib.ticker.LogFormatterSciNotation()'
          except:
            formatterName = 'matplotlib.ticker.ScalarFormatter()'
        # apply custom formatting?
        if(self.ticksXFormat != 'default'):
          # store fallback
          self.fallback_ticksXFormat = autolabels
          autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksXFormat, self.ticksXFormatPrecision, self.ticksXFormatTrailZero, self.ticksXFormatSeparator, self.ticksXFormatComma, self.ticksXFormatPrefix, self.ticksXFormatPostfix))
        for useAxis in axisItems:
          useAxis.xaxis.set_major_formatter(autolabels)
          useAxis.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(value))
        ###if(self.modeX == 'linear'):
        if(1):
          minorAutoticks = MyAutoMinorLocator(useTicksXMinor)
          for useAxis in axisItems:
            useAxis.xaxis.set_minor_locator(minorAutoticks)
            useAxis.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # store settings
        self.ticksX = value
        lower, upper = self.minX, self.maxX
        if(len(value)):
          # check for empty list
          if(np.min(value) < np.min((self.minX, self.maxX))):
            flag = True
            lower = np.min(value)
          if(np.max(value) > np.max((self.minX, self.maxX))):
            flag = True
            upper = np.max(value)
          # special treatment for resid plot
          if(target == 'resid'):
            flag = True
        # clear labels
        self.ticksXLabel = []
        self.ticksXDataSet = -1
        self.ticksXAuto = False
        self.parent.graphicsarea.configTickXAuto.setChecked(False)
      elif(axis == 'x2'):
        if(self.modeX_div == 'linear'):
          autolabels = matplotlib.ticker.ScalarFormatter()
        else:
          autolabels = matplotlib.ticker.LogFormatterSciNotation()
        # apply custom formatting?
        if(self.ticksXFormat_div != 'default'):
          # store fallback
          self.fallback_ticksXFormat_div = autolabels
          autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksXFormat_div, self.ticksXFormatPrecision_div, self.ticksXFormatTrailZero_div, self.ticksXFormatSeparator_div, self.ticksXFormatComma_div, self.ticksXFormatPrefix_div, self.ticksXFormatPostfix_div))
        axisobject.xaxis.set_major_formatter(autolabels)
        axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(value))
        ###if(self.modeX_div == 'linear'):
        if(1):
          minorAutoticks = MyAutoMinorLocator(useTicksXMinor)
          axisobject.xaxis.set_minor_locator(minorAutoticks)
          axisobject.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # store settings
        self.ticksX_div = value
        lower, upper = self.minX_div, self.maxX_div
        if(len(value)):
          # check for empty list
          if(np.min(value) < np.min((self.minX_div, self.maxX_div))):
            flag = True
            lower = np.min(value)
          if(np.max(value) > np.max((self.minX_div, self.maxX_div))):
            flag = True
            upper = np.max(value)
          # special treatment for resid plot
          if(target == 'resid'):
            flag = True
        # clear labels
        self.ticksXLabel_div = []
        self.ticksXAuto_div = False
        self.parent.graphicsarea.configInnerTickXAuto.setChecked(False)
      elif(axis == 'y'):
        axisobject, axisname = self.ax, 'ax'
        if(self.modeY == 'linear'):
          autolabels = matplotlib.ticker.ScalarFormatter()
          formatterName = 'matplotlib.ticker.ScalarFormatter()'
        else:
          autolabels = matplotlib.ticker.LogFormatterSciNotation()
          ###if(platform in ['linux', 'darwin']):
          try:
            blah = matplotlib.ticker.LogFormatterSciNotation()
            formatterName = 'matplotlib.ticker.LogFormatterSciNotation()'
          except:
            formatterName = 'matplotlib.ticker.ScalarFormatter()'
        # apply custom formatting?
        if(self.ticksYFormat != 'default'):
          # store fallback
          self.fallback_ticksYFormat = autolabels
          autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksYFormat, self.ticksYFormatPrecision, self.ticksYFormatTrailZero, self.ticksYFormatSeparator, self.ticksYFormatComma, self.ticksYFormatPrefix, self.ticksYFormatPostfix))
        for useAxis in axisItems:
          useAxis.yaxis.set_major_formatter(autolabels)
          useAxis.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(value))
        ###if(self.modeY == 'linear'):
        if(1):
          minorAutoticks = MyAutoMinorLocator(self.ticksYMinor)
          for useAxis in axisItems:
            useAxis.yaxis.set_minor_locator(minorAutoticks)
            useAxis.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # store settings
        self.ticksY = value
        lower, upper = self.minY, self.maxY
        if(len(value)):
          # check for empty list
          if(np.min(value) < np.min((self.minY, self.maxY))):
            flag = True
            lower = np.min(value)
          if(np.max(value) > np.max((self.minY, self.maxY))):
            flag = True
            upper = np.max(value)
        self.ticksYAuto = False
        self.parent.graphicsarea.configTickYAuto.setChecked(False)
      elif(axis == 'y2'):
        if(self.modeY2 == 'linear'):
          autolabels = matplotlib.ticker.ScalarFormatter()
          formatterName = 'matplotlib.ticker.ScalarFormatter()'
        else:
          autolabels = matplotlib.ticker.LogFormatterSciNotation()
          ###if(platform in ['linux', 'darwin']):
          try:
            blah = matplotlib.ticker.LogFormatterSciNotation()
            formatterName = 'matplotlib.ticker.LogFormatterSciNotation()'
          except:
            formatterName = 'matplotlib.ticker.ScalarFormatter()'
        # apply custom formatting?
        if(self.ticksY2Format != 'default'):
          # store fallback
          self.fallback_ticksY2Format = autolabels
          autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksY2Format, self.ticksY2FormatPrecision, self.ticksY2FormatTrailZero, self.ticksY2FormatSeparator, self.ticksY2FormatComma, self.ticksY2FormatPrefix, self.ticksY2FormatPostfix))
        for useAxis in axisItems:
          useAxis.yaxis.set_major_formatter(autolabels)
          useAxis.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(value))
        ###if(self.modeY2 == 'linear'):
        if(1):
          minorAutoticks = MyAutoMinorLocator(self.ticksY2Minor)
          for useAxis in axisItems:
            useAxis.yaxis.set_minor_locator(minorAutoticks)
            useAxis.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # store settings
        self.ticksY2 = value
        lower, upper = self.minY2, self.maxY2
        if(len(value)):
          # check for empty list
          if(np.min(value) < np.min((self.minY2, self.maxY2))):
            flag = True
            lower = np.min(value)
          if(np.max(value) > np.max((self.minY2, self.maxY2))):
            flag = True
            upper = np.max(value)
        self.ticksY2Auto = False
        self.parent.graphicsarea2.configTickYAuto.setChecked(False)
      else:
        for useAxis in axisItems:
          useAxis.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(value))
        autolabels = matplotlib.ticker.ScalarFormatter()
        # apply custom formatting?
        if(self.ticksResidYFormat != 'default'):
          # store fallback
          self.fallback_ticksResidYFormat = autolabels
          autolabels = matplotlib.ticker.FuncFormatter(partial(self.myFormatterFunction, self.ticksResidYFormat, self.ticksResidYFormatPrecision, self.ticksResidYFormatTrailZero, self.ticksResidYFormatSeparator, self.ticksResidYFormatComma, self.ticksResidYFormatPrefix, self.ticksResidYFormatPostfix))
        if(1):
          minorAutoticks = MyAutoMinorLocator(self.ticksResidYMinor)
          for useAxis in axisItems:
            useAxis.yaxis.set_minor_locator(minorAutoticks)
            useAxis.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        formatterName = 'matplotlib.ticker.ScalarFormatter()'
        for useAxis in axisItems:
          useAxis.yaxis.set_major_formatter(autolabels)
        lower, upper = self.minResidY, self.maxResidY
        if(len(value)):
          # check for empty list
          if(np.min(value) < np.min((self.minResidY, self.maxResidY))):
            flag = True
            lower = np.min(value)
          if(np.max(value) > np.max((self.minResidY, self.maxResidY))):
            flag = True
            upper = np.max(value)
        self.ticksResidYAuto = False
        self.parent.graphicsarea.configTickResidYAuto.setChecked(False)
        
      # check whether the new ticks necessitate axis rescaling
      if(flag):
        if(axis in ['y', 'y2']):
          self.setAxisLimits(lower = lower, upper = upper, axis = axis, updateLabel = True, target=target, redraw=redraw, updateGrid=True)
        elif(axis == 'resid'):
          self.setAxisLimits(lower = lower, upper = upper, axis = 'y', updateLabel = True, target='resid', redraw=redraw, updateGrid=True)
        else:
          self.setAxisLimits(lower = lower, upper = upper, axis = axis, updateLabel = True, target=target, redraw=False, updateGrid=True)
          if(target == 'plot'):
            # and we should redraw the fit function to cover new x-range
            if(axis == 'x'):
              self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
                fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot,\
                handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
              # copy in case split axes are shown
              curve = self.parent.fit[self.parent.activeFit]
              if(self.parent.plotArea.splitY and curve.onBothAxes):
                curve.duplicateForSplit()
              if(redraw):
                self.parent.plotArea.dataplotwidget.myRefresh()
            else:
              self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
                fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div,\
                handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
              # copy in case split axes are shown
              curve = self.parent.fit[self.parent.activeFit]
              if(self.parent.plotArea.splitY and curve.onBothAxes):
                curve.duplicateForSplit()
              if(redraw):
                self.parent.plotArea.dataplotwidget.myRefresh()
          else:
            # and we should update the resid plot (as x-axis will most likely have rescaled)
            if(axis == 'x'):
              self.parent.plotArea.handleResidZero = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero, redraw=redraw)
            else:
              self.parent.plotArea.handleResidZero_div = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero_div, redraw=redraw, splitX=True)
      else:
        # update axis grid lines
        if(axis == 'resid'):
          item = 'y'
        else:
          item = axis
        if(self.gridVisible[item]):
          if((item != 'y2') or self.isSecondAxesActive()):
            self.drawAxisGrid(axis=item, redraw=False, target=target)
      
        if(redraw):
          plotobject.myRefresh()
    elif(axis in ['xs']):
      # wow, this is rather simple as all the hard work is done by draw2ndAxisTicks()
      self.slavedTicksX = value
      if(redraw):
        plotobject.myRefresh()

  def getAxisTicks(self, axis='x'):
    # reports back axis ticks
    if(axis in ['x', 'y', 'y2', 'resid', 'x2']):
      if(axis == 'x'):
        ticks = self.ax.xaxis.get_ticklocs()
      elif(axis == 'x2'):
        ticks = self.ax_div.xaxis.get_ticklocs()
      elif(axis == 'y'):
        ticks = self.ax.yaxis.get_ticklocs()
      elif(axis == 'y2'):
        ticks = self.ax2.yaxis.get_ticklocs()
      else:
        ticks = self.ax_resid.yaxis.get_ticklocs()
      return ticks
    else:
      return []

  def setTickLabelPad2(self, value=0.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname, plotfig = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2', self.matplot
          if((self.ticksXPad2 == value) or (not self.ticksXShow)):
            redraw = False
          self.ticksXPad2 = value
        else:
          plotobject, axisobject, axisobject2, axisname, plotfig = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid', self.residplot
          if((self.ticksXPad2_resid == value) or (not self.ticksXShow_resid)):
            redraw = False
          self.ticksXPad2_resid = value
        #
        tickLabels = axisobject.get_xticklabels(which='both')
        tickLabels.extend(axisobject2.get_xticklabels(which='both'))
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname, plotfig = self.dataplotwidget, self.ax, self.ax_div, 'ax', self.matplot
          if((self.ticksYPad2 == value) or (not self.ticksYShow)):
            redraw = False
          self.ticksYPad2 = value
        else:
          plotobject, axisobject, axisobject2, axisname, plotfig = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid', self.residplot
          if((self.ticksYPad2_resid == value) or (not self.ticksResidYShow)):
            redraw = False
          self.ticksYPad2_resid = value
        #
        tickLabels = axisobject.get_yticklabels(which='both')
        tickLabels.extend(axisobject2.get_yticklabels(which='both'))
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname, plotfig = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2', self.matplot
        if((self.ticksY2Pad2 == value) or (not self.ticksY2Show)):
          redraw = False
        self.ticksY2Pad2 = value
        #
        tickLabels = axisobject.get_yticklabels(which='both')
        tickLabels.extend(axisobject2.get_yticklabels(which='both'))
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXPad2 == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXPad2 = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXPad2_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXPad2_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      # set orthogonal padding using transforms
      if(axis == 'x'):
        offset = matplotlib.transforms.ScaledTranslation(value / 72.0, 0, plotfig.dpi_scale_trans)
      elif(axis in ['y', 'y2']):
        offset = matplotlib.transforms.ScaledTranslation(0, value / 72.0, plotfig.dpi_scale_trans)
      for entry in tickLabels:
        # strip transform down to inner CompositeGenericTransform
        transformA, transformB = entry.get_transform(), matplotlib.transforms.IdentityTransform()
        while(isinstance(transformA, matplotlib.transforms.CompositeGenericTransform)):
          transformA, transformB = transformA._a, transformA._b
        # compile new transform
        transAll = transformA + transformB + offset
        entry.set_transform(transAll)

      # take care of moved axes (otherwise padding set to zero)
      if(axis == 'x'):
        # adjust spine lines if needed
        for spine in ['top', 'bottom']:
          if(self.axisPosition[spine] in ['data', 'axes']):
            self.setAxisPositionHelper(axis=spine, plotobject=plotobject, axisobject=axisobject, target=target, secondAxes=False)
            self.setAxisPositionHelper(axis=spine, plotobject=plotobject, axisobject=axisobject2, target=target, secondAxes=False, splitX=True)
      elif(axis == 'y'):
        # adjust spine lines if needed
        for spine in ['left', 'right']:
          if(self.axisPosition[spine] in ['data', 'axes']):
            self.setAxisPositionHelper(axis=spine, plotobject=plotobject, axisobject=axisobject, target=target, secondAxes=False)
      elif(axis == 'y2'):
        for spine in ['left2', 'right2']:
          if(self.axisPosition[spine] in ['data', 'axes']):
            self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.dataplotwidget, axisobject=self.ax2, target='plot', secondAxes=True)
            self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.dataplotwidget, axisobject=self.ax2_div, target='plot', secondAxes=True)

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickLabelPad(self, value=4.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.ticksXPad == value) or (not self.ticksXShow)):
            redraw = False
          self.ticksXPad = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.ticksXPad_resid == value) or (not self.ticksXShow_resid)):
            redraw = False
          self.ticksXPad_resid = value
        #
        ticks = axisobject.xaxis.get_major_ticks()
        ticks.extend(axisobject.xaxis.get_minor_ticks())
        ticks.extend(axisobject2.xaxis.get_major_ticks())
        ticks.extend(axisobject2.xaxis.get_minor_ticks())
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.ticksYPad == value) or (not self.ticksYShow)):
            redraw = False
          self.ticksYPad = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.ticksYPad_resid == value) or (not self.ticksResidYShow)):
            redraw = False
          self.ticksYPad_resid = value
        #
        ticks = axisobject.yaxis.get_major_ticks()
        ticks.extend(axisobject.yaxis.get_minor_ticks())
        ticks.extend(axisobject2.yaxis.get_major_ticks())
        ticks.extend(axisobject2.yaxis.get_minor_ticks())
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.ticksY2Pad == value) or (not self.ticksY2Show)):
          redraw = False
        self.ticksY2Pad = value
        #
        ticks = axisobject.yaxis.get_major_ticks()
        ticks.extend(axisobject.yaxis.get_minor_ticks())
        ticks.extend(axisobject2.yaxis.get_major_ticks())
        ticks.extend(axisobject2.yaxis.get_minor_ticks())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXPad == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXPad = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXPad_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXPad_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        ticks = []
        
      # set padding
      for entry in ticks:
        entry.set_pad(value)
        
      # now we should call setTickLabelPad2 to prevent undoing of perpendicular shift (kind of ugly thing to do)
      # maybe in the future use a single handler function
      if(axis == 'x'):
        self.setTickLabelPad2(value=self.ticksXPad2, axis=axis, redraw=False, target=target)
      elif(axis == 'y'):
        if(target == 'plot'):
          self.setTickLabelPad2(value=self.ticksYPad2, axis=axis, redraw=False, target=target)
        else:
          self.setTickLabelPad2(value=self.ticksYPad2_resid, axis=axis, redraw=False, target=target)
      elif(axis == 'y2'):
        self.setTickLabelPad2(value=self.ticksY2Pad2, axis=axis, redraw=False, target=target)

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickLabelLinespacing(self, value='normal', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXLinespacing == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXLinespacing = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if((self.ticksYLinespacing == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYLinespacing = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif((axis == 'y2') or (not self.ticksY2Show)):
            if(self.ticksY2Linespacing == value):
              redraw = False
            self.ticksY2Linespacing = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(axis == 'x'):
            if((self.ticksXLinespacing_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXLinespacing_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYLinespacing_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYLinespacing_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXLinespacing == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXLinespacing = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXLinespacing_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXLinespacing_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_linespacing(value)

      # redraw?
      if(redraw):
        plotobject.myRefresh()
      
  def setTickLabelBold(self, value='normal', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXWeight == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXWeight = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if((self.ticksYWeight == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYWeight = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif((axis == 'y2') or (not self.ticksY2Show)):
            if(self.ticksY2Weight == value):
              redraw = False
            self.ticksY2Weight = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(axis == 'x'):
            if((self.ticksXWeight_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXWeight_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYWeight_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYWeight_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXWeight == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXWeight = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXWeight_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXWeight_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_fontweight(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()
      
  def setTickLabelItalic(self, value='normal', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXStyle == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXStyle = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if((self.ticksYStyle == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYStyle = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if((self.ticksY2Style == value) or (not self.ticksY2Show)):
              redraw = False
            self.ticksY2Style = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(axis == 'x'):
            if((self.ticksXStyle_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXStyle_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYStyle_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYStyle_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXStyle == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXStyle = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXStyle_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXStyle_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_fontstyle(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickLabelAngle(self, value=12.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXAngle == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXAngle = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject, axisobject2, axisname = self.ax, self.ax_div, 'ax'
            if((self.ticksYAngle == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYAngle = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if((self.ticksY2Angle == value) or (not self.ticksY2Show)):
              redraw = False
            self.ticksY2Angle = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(axis == 'x'):
            if((self.ticksXAngle_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXAngle_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYAngle_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYAngle_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXAngle == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXAngle = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXAngle_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXAngle_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_rotation(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickLabelAlignment(self, value=None, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXAlignment == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXAlignment = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if((self.ticksYAlignment == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYAlignment = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if((self.ticksY2Alignment == value) or (not self.ticksY2Show)):
              redraw = False
            self.ticksY2Alignment = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(axis == 'x'):
            if((self.ticksXAlignment_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXAlignment_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYAlignment_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYAlignment_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXAlignment == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXAlignment = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXAlignment_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXAlignment_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_horizontalalignment(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickLabelAlignmentVertical(self, value=None, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXAlignmentVertical == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXAlignmentVertical = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject, axisobject2, axisname = self.ax, self.ax_div, 'ax'
            if((self.ticksYAlignmentVertical == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYAlignmentVertical = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if((self.ticksY2AlignmentVertical == value) or (not self.ticksY2Show)):
              redraw = False
            self.ticksY2AlignmentVertical = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(axis == 'x'):
            if((self.ticksXAlignmentVertical_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXAlignmentVertical_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYAlignmentVertical_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYAlignmentVertical_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        # fix matplotlib glitch/feature?
        if(value == 'center'):
          if(len(tickLabels)):
            try:
              tickLabels[0].set_verticalalignment('center_baseline')
              value = 'center_baseline'
            except:
              pass
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXAlignmentVertical == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXAlignmentVertical = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXAlignmentVertical_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXAlignmentVertical_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_verticalalignment(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickLabelSize(self, value=12.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXSize == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXSize = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject, axisobject2, axisname = self.ax, self.ax_div, 'ax'
            if((self.ticksYSize == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYSize = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if((self.ticksY2Size == value) or (not self.ticksY2Show)):
              redraw = False
            self.ticksY2Size = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          # sets tick label size
          if(axis == 'x'):
            if((self.ticksXSize_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXSize_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            if((self.ticksYSize_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYSize_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXSize == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXSize = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXSize_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXSize_resid = value

        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_fontsize(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def toggleTicksLabel(self, value=True, axis='x', redraw=True, target='plot'):
    # toggles visibility of tick labels
    # Matplotlib 3.1.2 only returns handles to visible labels which is counter-intuitive and overall bad
    # rather go via set_tick_params
    if((target in ['plot', 'resid']) and (axis in ['x', 'x2', 'y', 'y2'])):
      if(target == 'plot'):
        plotobject, axisobject, axisobject2 = self.dataplotwidget, self.ax2, self.ax2_div 
        if(axis == 'y'):
          axisobject, axisobject2 = self.ax, self.ax_div
      else:
        plotobject, axisobject, axisobject2 = self.residplotwidget, self.ax_resid, self.ax_resid_div
        
      if(axis == 'x'):
        if(target == 'plot'):
          if(self.ticksXShow == value):
            redraw = False
          self.ticksXShow = value
        else:
          if(self.ticksXShow_resid == value):
            redraw = False
          self.ticksXShow_resid = value
        offsetLabel = axisobject.xaxis.get_offset_text()
      elif(axis == 'x2'):
        if(target == 'plot'):
          if(self.ticksXShow_div == value):
            redraw = False
          self.ticksXShow_div = value
        else:
          if(self.ticksXShow_resid_div == value):
            redraw = False
          self.ticksXShow_resid_div = value
        offsetLabel = axisobject2.xaxis.get_offset_text()
      elif(axis == 'y'):
        if(target == 'plot'):
          if(self.ticksYShow == value):
            redraw = False
          self.ticksYShow = value
        else:
          if(self.ticksResidYShow == value):
            redraw = False
          self.ticksResidYShow = value
        offsetLabel = axisobject.yaxis.get_offset_text()
      elif(axis == 'y2'):
        if(self.ticksY2Show == value):
          redraw = False
        self.ticksY2Show = value
        offsetLabel = axisobject.yaxis.get_offset_text()
      else:
        redraw = False
        offsetLabel = None

      # set visibility of tick labels
      if(axis in ['x', 'x2']):
        if(axis == 'x'):
          useAxisobject = axisobject
        else:
          useAxisobject = axisobject2

        if(value):
          useAxisobject.xaxis.set_tick_params(which='both', labelbottom=self.ticksLabelShow['bottom'], labeltop=self.ticksLabelShow['top'])
          if((axis == 'x') and (hasattr(self.parent, 'graphicsarea'))):
            for useAxis in ('bottom', 'top'):
              self.parent.graphicsarea.configTickMarkLabelShow[useAxis].setChecked(self.ticksLabelShow[useAxis])
        else:
          useAxisobject.xaxis.set_tick_params(which='both', labelbottom=False, labeltop=False)
          if((axis == 'x') and (hasattr(self.parent, 'graphicsarea'))):
            for useAxis in ('bottom', 'top'):
              self.parent.graphicsarea.configTickMarkLabelShow[useAxis].setChecked(False)
      elif(axis == 'y'):
        if(value):
          # let updateInnerSituation() handle this
          if(self.splitShow):
            self.updateInnerSituation()
          else:
            axisobject.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left'], labelright=self.ticksLabelShow['right'])            
            axisobject2.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left'], labelright=self.ticksLabelShow['right'])            
          if(hasattr(self.parent, 'graphicsarea')):
            for useAxis in ('left', 'right'):
              self.parent.graphicsarea.configTickMarkLabelShow[useAxis].setChecked(self.ticksLabelShow[useAxis])
        else:
          axisobject.yaxis.set_tick_params(which='both', labelleft=False, labelright=False)
          axisobject2.yaxis.set_tick_params(which='both', labelleft=False, labelright=False)
          if(hasattr(self.parent, 'graphicsarea')):
            for useAxis in ('left', 'right'):
              self.parent.graphicsarea.configTickMarkLabelShow[useAxis].setChecked(False)
      elif(axis == 'y2'):
        if(value):
          # let updateInnerSituation() handle this
          if(self.splitShow):
            self.updateInnerSituation()
          else:
            axisobject.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left2'], labelright=self.ticksLabelShow['right2'])            
            axisobject2.yaxis.set_tick_params(which='both', labelleft=self.ticksLabelShow['left2'], labelright=self.ticksLabelShow['right2'])            
          if(hasattr(self.parent, 'graphicsarea2')):
            for useAxis in ('left2', 'right2'):
              self.parent.graphicsarea2.configTickMarkLabelShow[useAxis].setChecked(self.ticksLabelShow[useAxis])
        else:
          axisobject.yaxis.set_tick_params(which='both', labelleft=False, labelright=False)
          axisobject2.yaxis.set_tick_params(which='both', labelleft=False, labelright=False)
          if(hasattr(self.parent, 'graphicsarea2')):
            for useAxis in ('left2', 'right2'):
              self.parent.graphicsarea2.configTickMarkLabelShow[useAxis].setChecked(False)
        
      # need to update tick formatting if labels are displayed again
      if(value):
        self.setTickOne4All(axis=axis, redraw=False, target=target)
        
      # offset label
      if(offsetLabel != None):
        offsetLabel.set_visible(value)
      
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def toggleAxisLabel(self, value=True, axis='x', redraw=True, target='plot'):
    # toggles visibility of axis label
    if((target in ['plot', 'resid']) and (axis in ['x', 'y', 'y2'])):
      if(target == 'plot'):
        plotobject, axisobject, axisobject2 = self.dataplotwidget, self.ax2, self.ax2_div
        if(axis == 'y'):
          axisobject = self.ax
      else:
        plotobject, axisobject = self.residplotwidget, self.ax_resid
        
      if(axis == 'x'):
        if(target == 'plot'):
          if(self.labelXShow == value):
            redraw = False
          self.labelXShow = value
        else:
          if(self.labelXShow_resid == value):
            redraw = False
          self.labelXShow_resid = value
        axisobject.xaxis.label.set_visible(value) 
      elif(axis == 'y'):
        if(target == 'plot'):
          if(self.labelYShow == value):
            redraw = False
          self.labelYShow = value
        else:
          if(self.labelYShow_resid == value):
            redraw = False
          self.labelYShow_resid = value
        axisobject.yaxis.label.set_visible(value) 
      elif(axis == 'y2'):
        if(self.labelY2Show == value):
          redraw = False
        self.labelY2Show = value
        if(self.splitShow):
          axisobject2.yaxis.label.set_visible(value) 
          # this is needed when activating split y axis
          if(not self.labelY2Show):
            axisobject.yaxis.label.set_visible(value) 
        else:
          axisobject.yaxis.label.set_visible(value) 
      else:
        redraw = False
      
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelLinespacing(self, value=12.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXLinespacing == value) or (not self.labelXShow)):
            redraw = False
          self.labelXLinespacing = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXLinespacing_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXLinespacing_resid = value

        handleAxis = axisobject.xaxis.label
        handleAxis.set_linespacing(value)
        handleAxis = axisobject2.xaxis.label
        handleAxis.set_linespacing(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYLinespacing == value) or (not self.labelYShow)):
            redraw = False
          self.labelYLinespacing = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYLinespacing_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYLinespacing_resid = value

        handleAxis = axisobject.yaxis.label
        handleAxis.set_linespacing(value)
        handleAxis = axisobject2.yaxis.label
        handleAxis.set_linespacing(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Linespacing == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Linespacing = value
        handleAxis = axisobject.yaxis.label
        handleAxis.set_linespacing(value)
        handleAxis = axisobject2.yaxis.label
        handleAxis.set_linespacing(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXLinespacing == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXLinespacing = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXLinespacing_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXLinespacing_resid = value

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelSize(self, value=12.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXSize == value) or (not self.labelXShow)):
            redraw = False
          self.labelXSize = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXSize_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXSize_resid = value

        handleAxis = axisobject.xaxis.label
        handleAxis.set_size(value)
        handleAxis = axisobject2.xaxis.label
        handleAxis.set_size(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYSize == value) or (not self.labelYShow)):
            redraw = False
          self.labelYSize = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYSize_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYSize_resid = value

        handleAxis = axisobject.yaxis.label
        handleAxis.set_size(value)
        handleAxis = axisobject2.yaxis.label
        handleAxis.set_size(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Size == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Size = value
        handleAxis = axisobject.yaxis.label
        handleAxis.set_size(value)
        handleAxis = axisobject2.yaxis.label
        handleAxis.set_size(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXSize == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXSize = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXSize_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXSize_resid = value

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelPad(self, value=12.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXPad == value) or (not self.labelXShow)):
            redraw = False
          self.labelXPad = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid, 'ax_resid'
          if((self.labelXPad_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXPad_resid = value

        axisobject.xaxis.labelpad = value
        axisobject2.xaxis.labelpad = value
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYPad == value) or (not self.labelYShow)):
            redraw = False
          self.labelYPad = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYPad_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYPad_resid = value

        axisobject.yaxis.labelpad = value
        axisobject2.yaxis.labelpad = value
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Pad == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Pad = value
        axisobject.yaxis.labelpad = value
        axisobject2.yaxis.labelpad = value
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXPad == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXPad = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXPad_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXPad_resid = value

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelPos(self, value=0.5, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXPos == value) or (not self.labelXShow)):
            redraw = False
          self.labelXPos = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXPos_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXPos_resid = value

        axisobject.xaxis.label.set_x(value)
        axisobject2.xaxis.label.set_x(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYPos == value) or (not self.labelYShow)):
            redraw = False
          self.labelYPos = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYPos_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYPos_resid = value

        axisobject.yaxis.label.set_y(value)
        axisobject2.yaxis.label.set_y(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Pos == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Pos = value
        axisobject.yaxis.label.set_y(value)
        axisobject2.yaxis.label.set_y(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXPos == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXPos = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXPos_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXPos_resid = value

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelAngle(self, value=0.0, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXAngle == value) or (not self.labelXShow)):
            redraw = False
          self.labelXAngle = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXAngle_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXAngle_resid = value

        axisobject.xaxis.label.set_rotation(value)
        axisobject2.xaxis.label.set_rotation(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYAngle == value) or (not self.labelYShow)):
            redraw = False
          self.labelYAngle = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYAngle_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYAngle_resid = value

        axisobject.yaxis.label.set_rotation(value)
        axisobject2.yaxis.label.set_rotation(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Angle == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Angle = value
        axisobject.yaxis.label.set_rotation(value)
        axisobject2.yaxis.label.set_rotation(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXAngle == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXAngle = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXAngle_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXAngle_resid = value

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelBold(self, value='normal', axis='x', redraw=True, target='plot'):
    # formats axis label bold
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXWeight == value) or (not self.labelXShow)):
            redraw = False
          self.labelXWeight = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXWeight_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXWeight_resid = value

        axisobject.xaxis.label.set_fontweight(value)
        axisobject2.xaxis.label.set_fontweight(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYWeight == value) or (not self.labelYShow)):
            redraw = False
          self.labelYWeight = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYWeight_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYWeight_resid = value

        axisobject.yaxis.label.set_fontweight(value)
        axisobject2.yaxis.label.set_fontweight(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Weight == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Weight = value
        axisobject.yaxis.label.set_fontweight(value)
        axisobject2.yaxis.label.set_fontweight(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXWeight == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXWeight = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXWeight_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXWeight_resid = value
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelItalic(self, value='normal', axis='x', redraw=True, target='plot'):
    # formats axis label italic
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXStyle == value) or (not self.labelXShow)):
            redraw = False
          self.labelXStyle = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXStyle_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXStyle_resid = value

        axisobject.xaxis.label.set_fontstyle(value)
        axisobject2.xaxis.label.set_fontstyle(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYStyle == value) or (not self.labelYShow)):
            redraw = False
          self.labelYStyle = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYStyle_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYStyle_resid = value

        axisobject.yaxis.label.set_fontstyle(value)
        axisobject2.yaxis.label.set_fontstyle(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Style == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Style = value
        axisobject.yaxis.label.set_fontstyle(value)
        axisobject2.yaxis.label.set_fontstyle(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXStyle == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXStyle = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXStyle_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXStyle_resid = value
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisLabelVariant(self, value='normal', axis='x', redraw=True, target='plot'):
    # formats axis label small-caps
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if((self.labelXVariant == value) or (not self.labelXShow)):
            redraw = False
          self.labelXVariant = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelXVariant_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXVariant_resid = value

        axisobject.xaxis.label.set_variant(value)
        axisobject2.xaxis.label.set_variant(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax, self.ax_div, 'ax'
          if((self.labelYVariant == value) or (not self.labelYShow)):
            redraw = False
          self.labelYVariant = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if((self.labelYVariant_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYVariant_resid = value

        axisobject.yaxis.label.set_variant(value)
        axisobject2.yaxis.label.set_variant(value)
      elif(axis == 'y2'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
        if((self.labelY2Variant == value) or (not self.labelY2Show)):
          redraw = False
        self.labelY2Variant = value
        axisobject.yaxis.label.set_variant(value)
        axisobject2.yaxis.label.set_variant(value)
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXVariant == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXVariant = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXVariant_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXVariant_resid = value
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setAxisArrow(self, state=True, axis='x', item='all', redraw=True, target='plot'):
    # toggles drawing of arrow
    if(item in ['all', 'edge', 'fill']):
      if(((target == 'plot') and (axis in ['x', 'y', 'y2'])) or ((target == 'resid') and (axis in ['x', 'y']))):
        if(target == 'plot'):
          plotobject, arrowhandle, arrowhandle2  = self.dataplotwidget, self.handleArrow, self.handleArrow2
        else:
          plotobject, arrowhandle, arrowhandle2 = self.residplotwidget, self.handleArrowResid, self.handleArrowResid2
        
        if(item == 'all'):
          self.arrowVisible[axis] = state
          if(state):
            self.drawAxisArrow(axis=axis, redraw=redraw, target=target)
          elif((arrowhandle[axis] != None) or (arrowhandle2[axis] != None)):
            if(arrowhandle[axis] != None):
              arrowhandle[axis].remove()
              arrowhandle[axis] = None
            if(arrowhandle2[axis] != None):
              arrowhandle2[axis].remove()
              arrowhandle2[axis] = None
            if(redraw):
              plotobject.myRefresh()
        else:
          if(item == 'edge'):
            if(target == 'plot'):
              if(self.arrowEdgeShow[axis] == state):
                redraw = False
              self.arrowEdgeShow[axis] = state
            else:
              if(self.arrowEdgeShow_resid[axis] == state):
                redraw = False
              self.arrowEdgeShow_resid[axis] = state
          else:
            if(target == 'plot'):
              if(self.arrowFillShow[axis] == state):
                redraw = False
              self.arrowFillShow[axis] = state
            else:
              if(self.arrowFillShow_resid[axis] == state):
                redraw = False
              self.arrowFillShow_resid[axis] = state
  
          if(redraw and self.arrowVisible[axis]):
            self.drawAxisArrow(axis=axis, redraw=redraw, target=target)

  def setAxisArrowColor(self, value=[0.0, 0.0, 0.0, 1.0], axis='x', item='fill', redraw=True):
    # changes color axis arrow
    if((axis in ['x', 'y', 'y2']) and (item in ['line', 'fill'])):
      if(item == 'line'):
        checkItem = self.arrowEdgeShow[axis]
        if(self.arrowColor[axis] == value):
          redraw = False
        self.arrowColor[axis] = value
      else:
        checkItem = self.arrowFillShow[axis]
        if(self.arrowFill[axis] == value):
          redraw = False
        self.arrowFill[axis] = value
      
      if(redraw and self.arrowVisible[axis] and checkItem):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowLineWidth(self, value=0.1, axis='x', redraw=True):
    # changes edge width of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowEdge[axis] == value):
        redraw = False
      self.arrowEdge[axis] = value

      if(redraw and self.arrowVisible[axis] and self.arrowEdgeShow[axis]):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowHeadWidth(self, value=0.1, axis='x', redraw=True):
    # changes width of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowHeadWidth[axis] == value):
        redraw = False
      self.arrowHeadWidth[axis] = value

      if((redraw) and (self.arrowVisible[axis])):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowHeadLength(self, value=0.1, axis='x', redraw=True):
    # changes width of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowHeadLength[axis] == value):
        redraw = False
      self.arrowHeadLength[axis] = value

      if((redraw) and (self.arrowVisible[axis])):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowOverhang(self, value=0.1, axis='x', redraw=True):
    # changes overhang of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowOverhang[axis] == value):
        redraw = False
      self.arrowOverhang[axis] = value

      if((redraw) and (self.arrowVisible[axis])):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowOffset(self, value=0, axis='x', redraw=True):
    # changes offset of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowOffset[axis] == value):
        redraw = False
      self.arrowOffset[axis] = value

      if((redraw) and (self.arrowVisible[axis])):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowLocation(self, value='both', axis='x', redraw=True):
    # changes location of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowLocation[axis] == value):
        redraw = False
      self.arrowLocation[axis] = value

      if((redraw) and (self.arrowVisible[axis])):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def setAxisArrowZ(self, value='axis', axis='x', redraw=True):
    # changes location of axis arrow
    if(axis in ['x', 'y', 'y2']):
      if(self.arrowZ[axis] == value):
        redraw = False
      self.arrowZ[axis] = value

      if((redraw) and (self.arrowVisible[axis])):
        self.drawAxisArrow(axis=axis, redraw=redraw, target='plot')
        if(axis != 'y2'):
          self.drawAxisArrow(axis=axis, redraw=redraw, target='resid')

  def drawSplitYAxisDivider(self, redraw=True):
    # draws divider marks on split y axis
    plotobject, axisobject, axisobject2, handleDividerY = self.dataplotwidget, self.ax, self.ax_div, self.handleDividerY
    drawobject, drawobject2 = self.ax, self.ax2

    # delete old lines
    if(handleDividerY != None):
      for entry in handleDividerY:
        entry.remove()
    handleDividerY = None

    # draw new lines
    if(self.splitY and self.splitYDivider):
      # determine draw coordinates
      xcoord, ycoord, handleDividerY, axisList = [], [], [], []
      if(self.splitYDividerLocation == 'both'):
        useItems = ['left', 'right']
      else:
        useItems = [self.splitYDividerLocation]
      # account for shifted axis
      for item in useItems:
        # have to go via axisobject as drawobject throws an error
        useAxis, useAxisobject = drawobject, axisobject
        if(self.splitShow and (item == 'right')):
          useAxisobject, useAxis = axisobject2, self.ax_div
        if(item in useAxisobject.spines):
          try:
            axisPosition, axisPositionValue = useAxisobject.spines[item].get_position()
            if(axisPosition == 'data'):
              drawX = axisPositionValue
            elif(axisPosition == 'axes'):
              axis_to_data = useAxis.transAxes + useAxis.transData.inverted()
              transformedPoint = axis_to_data.transform((axisPositionValue, 0))
              drawX = transformedPoint[0]
            # passed => store coords for drawing
            xcoord.append(drawX)
            ycoord.append(self.minY)
            axisList.append(useAxis)
          except:
            pass
            
      # now copy coordinates to axisobject2
      if(self.splitYPad):
        for number, x in enumerate([i for i in xcoord]):
          xcoord.append(x)
          ycoord.append(self.maxY2)
          if(axisList[number] == drawobject):
            axisList.append(drawobject2)
          else:
            axisList.append(self.ax2_div)

      for x, y, useAxis in zip(xcoord, ycoord, axisList):
        # assign use-x values
        if(useAxis in [drawobject, drawobject2]):
          useMinX, useMaxX, useModeX = self.minX, self.maxX, self.modeX
        else:
          useMinX, useMaxX, useModeX = self.minX_div, self.maxX_div, self.modeX_div
          
        if(useAxis in [drawobject, self.ax_div]):
          useMinY, useMaxY, useModeY = self.minY, self.maxY, self.modeY
        else:
          useMinY, useMaxY, useModeY = self.minY2, self.maxY2, self.modeY2
          
        # draw some lines
        if(useModeY == 'linear'):
          dimY = (useMaxY - useMinY)
        else:
          dimY = np.log(np.abs(useMaxY / useMinY))
        if(useModeX == 'linear'):
          dimX = (useMaxX - useMinX)
        else:
          dimX = np.log(np.abs(useMaxX / useMinX))
        plotX, plotY = self.matplot.get_size_inches()
        # also take into account plot padding
        plotX *= (self.padSize['right'] - self.padSize['left'])
        plotY *= (self.padSize['top'] - self.padSize['bottom'])
        deltaX = (1 + self.splitFraction) * self.splitYDividerLength * np.cos(self.splitYDividerAngle * np.pi / 180.0) / plotX * np.max((plotX, plotY))
        if(not useAxis in [drawobject, drawobject2]):
          deltaX /= self.splitFraction
        if(not self.splitShow):
          deltaX /= 2
        deltaY = (1 + self.splitYFraction) * self.splitYDividerLength * np.sin(self.splitYDividerAngle * np.pi / 180.0) / plotY * np.max((plotX, plotY))
        if(not useAxis in [drawobject, self.ax_div]):
          deltaY /= self.splitYFraction

        if(useModeX == 'linear'):
          cx1, cx2 = x - 0.5 * deltaX * dimX, x + 0.5 * deltaX * dimX
        else:
          cx1, cx2 = x / np.exp(0.5 * deltaX * dimX), x * np.exp(0.5 * deltaX * dimX)
        if(useModeY == 'linear'):
          cy1, cy2 = y - 0.5 * deltaY * dimY, y + 0.5 * deltaY * dimY
        else:
          cy1, cy2 = y / np.exp(0.5 * deltaY * dimY), y * np.exp(0.5 * deltaY * dimY)

        # transform to axiscoordinates
        drawLine = matplotlib.lines.Line2D([cx1, cx2], [cy1, cy2])
        handleDividerY.append(useAxis.add_line(drawLine))

      # apply styles
      for entry in handleDividerY:
        entry.set_color(self.splitYDividerColor)
        entry.set_solid_capstyle(self.splitYDividerDashStyle)
        entry.set_linewidth(self.splitYDividerWidth)
        entry.set_clip_on(False)
        entry.set_zorder(999)
        
    # save handles
    self.handleDividerY = handleDividerY

    if(redraw):
      plotobject.myRefresh()

  def drawSplitAxisDivider(self, redraw=True, target='plot'):
    # draws divider marks on split axis
    if(target in ['plot', 'resid']):
      # assign handles
      if(target == 'plot'):
        plotobject, axisobject, axisobject2, handleDivider = self.dataplotwidget, self.ax2, self.ax2_div, self.handleDivider
        useMinY, useMaxY, useModeY = self.minY, self.maxY, self.modeY
        drawobject, drawobject2 = self.ax, self.ax_div
      else:
        plotobject, axisobject, axisobject2, handleDivider = self.residplotwidget, self.ax_resid, self.ax_resid_div, self.handleDividerResid
        plotobject, axisobject, axisobject2, handleDivider = self.residplotwidget, self.ax_resid, self.ax_resid_div, self.handleDividerResid
        useMinY, useMaxY, useModeY = self.minResidY, self.maxResidY, 'linear'
        drawobject, drawobject2 = axisobject, axisobject2

      # delete old lines
      if(handleDivider != None):
        for entry in handleDivider:
          entry.remove()
      handleDivider = None

      # draw new lines
      if(self.splitShow and self.splitDivider):
        # determine draw coordinates
        xcoord, ycoord, handleDivider, axisList = [], [], [], []
        if(self.splitDividerLocation == 'both'):
          useItems = ['bottom', 'top']
        else:
          useItems = [self.splitDividerLocation]
        # account for shifted axis
        for item in useItems:
          # have to go via axisobject as drawobject throws an error
          useAxis, useAxisobject = drawobject, axisobject
          if((target == 'plot') and self.splitY and (item == 'bottom')):
            useAxisobject, useAxis = axisobject2, self.ax2
          if(item in useAxisobject.spines):
            try:
              axisPosition, axisPositionValue = useAxisobject.spines[item].get_position()
              if(axisPosition == 'data'):
                drawY = axisPositionValue
              elif(axisPosition == 'axes'):
                axis_to_data = useAxis.transAxes + useAxis.transData.inverted()
                transformedPoint = axis_to_data.transform((0, axisPositionValue))
                drawY = transformedPoint[1]
              # passed => store coords for drawing
              xcoord.append(self.maxX)
              ycoord.append(drawY)
              axisList.append(useAxis)
            except:
              pass
            
        # now copy coordinates to axisobject2
        if(self.splitPad):
          for number, y in enumerate([i for i in ycoord]):
            xcoord.append(self.minX_div)
            ycoord.append(y)
            if(axisList[number] == drawobject):
              axisList.append(drawobject2)
            else:
              axisList.append(self.ax2_div)
          
        for x, y, useAxis in zip(xcoord, ycoord, axisList):
          # assign use-x values
          if(target == 'plot'):
            if(useAxis in [drawobject, self.ax_div]):
              useMinY, useMaxY, useModeY = self.minY, self.maxY, self.modeY
            else:
              useMinY, useMaxY, useModeY = self.minY2, self.maxY2, self.modeY2

          if(useAxis in [drawobject, self.ax2]):
            useMinX, useMaxX, useModeX = self.minX, self.maxX, self.modeX
          else:
            useMinX, useMaxX, useModeX = self.minX_div, self.maxX_div, self.modeX_div
            
          # draw some lines
          if(useModeX == 'linear'):
            dimX = (useMaxX - useMinX)
          else:
            dimX = np.log(np.abs(useMaxX / useMinX))
          if(useModeY == 'linear'):
            dimY = (useMaxY - useMinY)
          else:
            dimY = np.log(np.abs(useMaxY / useMinY))
          plotX, plotY = self.matplot.get_size_inches()
          # also take into account plot padding
          plotX *= (self.padSize['right'] - self.padSize['left'])
          plotY *= (self.padSize['top'] - self.padSize['bottom'])
          deltaX = (1 + self.splitFraction) * self.splitDividerLength * np.cos(self.splitDividerAngle * np.pi / 180.0) / plotX * np.max((plotX, plotY))
          if(not useAxis in [drawobject2, self.ax2_div]):
            deltaX /= self.splitFraction
          deltaY = (1 + self.splitYFraction) * self.splitDividerLength * np.sin(self.splitDividerAngle * np.pi / 180.0) / plotY * np.max((plotX, plotY))
          if(target == 'plot'):
            if(self.splitY):
              if(not useAxis in [drawobject, self.ax_div]):
                deltaY /= self.splitYFraction
            else:
              deltaY /= 2.
          
          if(target == 'resid'):
            # account for size ratio plot/resid
            relativeSizes = self.masterwidget.sizes()
            sizeRatio = 1. * relativeSizes[0] / relativeSizes[1]
            deltaY *= sizeRatio / 2.
  
          if(useModeX == 'linear'):
            cx1, cx2 = x - 0.5 * deltaX * dimX, x + 0.5 * deltaX * dimX
          else:
            cx1, cx2 = x / np.exp(0.5 * deltaX * dimX), x * np.exp(0.5 * deltaX * dimX)
          if(useModeY == 'linear'):
            cy1, cy2 = y - 0.5 * deltaY * dimY, y + 0.5 * deltaY * dimY
          else:
            cy1, cy2 = y / np.exp(0.5 * deltaY * dimY), y * np.exp(0.5 * deltaY * dimY)

          # transform to axiscoordinates
          drawLine = matplotlib.lines.Line2D([cx1, cx2], [cy1, cy2])
          handleDivider.append(useAxis.add_line(drawLine))

        # apply styles
        for entry in handleDivider:
          entry.set_color(self.splitDividerColor)
          entry.set_solid_capstyle(self.splitDividerDashStyle)
          entry.set_linewidth(self.splitDividerWidth)
          entry.set_clip_on(False)
          entry.set_zorder(999)
          
      # save handles
      if(target == 'plot'):
        self.handleDivider = handleDivider
      else:
        self.handleDividerResid = handleDivider

      if(redraw):
        plotobject.myRefresh()

  def transformSlavedX(self, operation='+', number='1.0', redraw=True):
    # adds a transformation step for slaved x axis
    if(operation in ['+', '-', '/', '*', '**', 'e', 'ln']):
      self.slavedXTransform.append([operation, number])
    else:
      redraw = False
      
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()
        
    return self.slavedXTransform

  def resetSlavedX(self, redraw=True):
    # resets transformation for slaved x axis
    if(self.slavedXTransform == []):
      redraw = False
    self.slavedXTransform = []
        
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

  def toggleSlavedX(self, state=True, redraw=True):
    # toggles slaved x axis display
    if(state == self.slavedX):
      redraw = False
    self.slavedX = state
    
    if(redraw):
      # hide regular ticks on top axis
      if(state):
        self.setTickMarkVisibility(value=False, axis='top', redraw=False, target='plot')
        self.setTickMarkVisibility(value=False, axis='top', redraw=False, target='resid')
        if(hasattr(self.parent, 'graphicsarea')):
          self.parent.graphicsarea.configTickMarkLabel['top'].setChecked(False)
        
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

  def toggleSlavedXLabel(self, state=True, redraw=True):
    # toggles whether to display label for slaved x axis
    if(state == self.slavedXLabelShow):
      redraw = False
    self.slavedXLabelShow = state

    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()
          
  def changeSlavedXLabel(self, value='transformed x', redraw=True):
    # alters label for slaved x axis
    if(value == self.slavedXLabel):
      redraw = False
    self.slavedXLabel = value

    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

  def transform2ndAxisTicks(self, values, reverse=False):
    # transforms tick values back and forth for slaved x axis
    if(len(values)):
      values = np.array(values)
      
      if(reverse):
        for transform in self.slavedXTransform[::-1]:
          if(transform[0] == '+'):
            values -= transform[1]
          elif(transform[0] == '-'):
            values += transform[1]
          elif(transform[0] == '*'):
            values /= transform[1]
          elif(transform[0] == '/'):
            values *= transform[1]
          elif(transform[0] == '**'):
            values = values ** (1 / transform[1])
          elif(transform[0] == 'e'):
            values = np.log(values)
          elif(transform[0] == 'ln'):
            values = np.exp(values)
      else:
        for transform in self.slavedXTransform:
          if(transform[0] == '+'):
            values += transform[1]
          elif(transform[0] == '-'):
            values -= transform[1]
          elif(transform[0] == '*'):
            values *= transform[1]
          elif(transform[0] == '/'):
            values /= transform[1]
          elif(transform[0] == '**'):
            values = values ** transform[1]
          elif(transform[0] == 'e'):
            values = np.exp(values)
          elif(transform[0] == 'ln'):
            values = np.log(values)
          
    return values

  def draw2ndXAxisTicks(self, axis='x', target='plot'):
    # helper function to draw custom labels for 2nd x axis
    # subroutine for calculating position of minor ticks
    def calcMinorTicks(majorlocs, ndivs, vmin, vmax):
      'Return the locations of the ticks for specified parameters'
  
      if ndivs is None:
        ndivs = 9
  
      # view interval
      if vmin > vmax:
        vmin, vmax = vmax, vmin
  
      # calculate minor ticks
      locs = np.array([])
      for index in range(len(majorlocs) - 1):
        # can treat linear and log axes the same
        nulocs = np.linspace(majorlocs[index], majorlocs[index + 1], ndivs + 1)
        locs = np.hstack((locs, nulocs[1: -1]))
      
      # run through conditions
      cond1 = locs >= vmin
      cond2 = locs <= vmax
      locs = locs.compress(cond1 & cond2)
  
      return np.array(locs)

    # main function code
    if((target in ['plot', 'resid']) and (axis in ['x', 'x2'])):
      if(axis == 'x'):
        suffix = ''
        minX, maxX = self.minX, self.maxX
        if(target == 'plot'):
          axisobject = self.ax2
        else:
          axisobject = self.ax_resid
      else:
        suffix = '_div'
        minX, maxX = self.minX_div, self.maxX_div
        if(target == 'plot'):
          axisobject = self.ax2_div
        else:
          axisobject = self.ax_resid_div
          
      if(target == 'plot'):
        plotobject = self.matplot
      else:
        plotobject = self.residplot
        
      # remove old objects
      if(target == 'plot'):
        for killItem in self.__dict__['secondXLines' + suffix] + self.__dict__['secondXLabels' + suffix]:
          killItem.remove()
        self.__dict__['secondXLines' + suffix], self.__dict__['secondXLabels' + suffix]  = [], []
        if((self.handleSlavedXLabel != None) and (axis == 'x')):
          self.handleSlavedXLabel.remove()
          self.handleSlavedXLabel = None
      else:
        for killItem in self.__dict__['secondXLinesResid' + suffix] + self.__dict__['secondXLabelsResid' + suffix]:
          killItem.remove()
        self.__dict__['secondXLinesResid' + suffix], self.__dict__['secondXLabelsResid' + suffix]  = [], []
        if((self.handleSlavedXLabelResid != None) and (axis == 'x')):
          self.handleSlavedXLabelResid.remove()
          self.handleSlavedXLabelResid = None
          
      # draw new slaved ticks?
      if(self.slavedX and ((axis == 'x') or self.splitShow)):
        # by default use self.maxY2
        if(target == 'plot'):
          minY, maxY = self.minY2, self.maxY2
        else:
          minY, maxY = self.minResidY, self.maxResidY
        
        # check whether axis is shifted from default position
        try:
          axisPosition, axisPositionValue = axisobject.spines['top'].get_position()
          if(axisPosition == 'data'):
            maxY = axisPositionValue
          elif(axisPosition == 'axes'):
            axis_to_data = axisobject.transAxes + axisobject.transData.inverted()
            transformedPoint = axis_to_data.transform((0, axisPositionValue))
            maxY = transformedPoint[1]
        except:
          pass
  
        # transform x values according to formula
        try:
          [tmin, tmax] = self.transform2ndAxisTicks([minX, maxX], reverse=False)
          if(self.slavedTicksXAuto):
            autoticks = axisobject.xaxis.get_major_locator()
            transticks = autoticks.tick_values(tmin, tmax)
            # update tick entry field if auto ticks is enabled
            tickstr = self.parent.graphicsarea.magicTickstring(transticks)
            self.parent.graphicsarea.slavedTickXEntry.setText(tickstr)
          else:
            transticks = self.slavedTicksX
          ###print(autoticks, transticks)
          tickpositions = self.transform2ndAxisTicks(transticks, reverse=True)
          
          # deal with minor ticks
          if(self.slavedTicksXMinor):
            ndivs = self.slavedTicksXMinor
            transticks2 = calcMinorTicks(majorlocs=transticks, ndivs=ndivs, vmin=tmin, vmax=tmax)
            tickpositions2 = self.transform2ndAxisTicks(transticks2, reverse=True)
          else:
            transticks2, tickpositions2 = [], []
        except:
          transticks, tickpositions = [], []
          transticks2, tickpositions2 = [], []
          
        # need to calculate positions of axis ticks
        # the length is specified in units of 1 / 72 in
        currWidth, currHeight = plotobject.get_size_inches()
        currHeight *= self.padSize['top'] - self.padSize['bottom']
        useLength = self.ticksLength['slaved'] / currHeight / 72. * abs(maxY - minY)
        # need to correct useLength for split (if visible)
        # maybe need additional correction for visible plot area (i.e. take into account padding around and between plots)
        if(self.splitY and (target == 'plot')):
          useLength *= (1 + self.splitYFraction) / self.splitYFraction
        
        # consider direction of ticks
        if(self.ticksDirection['slaved'] == 'in'):
          pos1, pos2 = maxY, maxY - useLength
          mpos1, mpos2 = maxY, maxY - useLength * self.slavedTicksXMinorRelativeLength
        elif(self.ticksDirection['slaved'] == 'out'):
          pos1, pos2 = maxY, maxY + useLength
          mpos1, mpos2 = maxY, maxY + useLength * self.slavedTicksXMinorRelativeLength
        else:
          pos1, pos2 = maxY - .5 * useLength, maxY + .5 * useLength
          mpos1, mpos2 = maxY - .5 * useLength * self.slavedTicksXMinorRelativeLength, maxY + .5 * useLength * self.slavedTicksXMinorRelativeLength
        
        # now draw these on the current top axis
        if(self.slavedXFormatter != None):
          labelFormatter = self.slavedXFormatter
        else:
          labelFormatter = axisobject.xaxis.get_major_formatter()
        for tick, tickpos in zip(transticks, tickpositions):
          if((tickpos >= min(minX, maxX)) and (tickpos <= max(minX, maxX))):
            handle, = axisobject.plot([tickpos, tickpos], [pos1, pos2], 'r-')
            handle.set_clip_on(False)
            if(target == 'plot'):
              self.__dict__['secondXLines' + suffix].append(handle)
              # implement check for div axis
              # for some reason formatter returns empty string upon initialization
              # in this case, go via main axis
              labelText = labelFormatter.__call__(tick)
              if(not len(labelText)):
                labelText = self.ax2.xaxis.get_major_formatter().__call__(tick)
              self.__dict__['secondXLabels' + suffix].append(axisobject.text(tickpos, max(pos1, pos2), labelText))
            else:
              self.__dict__['secondXLinesResid' + suffix].append(handle)
              labelText = labelFormatter.__call__(tick)
              if(not len(labelText)):
                labelText = self.ax_resid.xaxis.get_major_formatter().__call__(tick)
              self.__dict__['secondXLabelsResid' + suffix].append(axisobject.text(tickpos, max(pos1, pos2), labelText))
        # and the minor ticklis as well
        for tickpos in tickpositions2:
          if((tickpos >= min(minX, maxX)) and (tickpos <= max(minX, maxX))):
            handle, = axisobject.plot([tickpos, tickpos], [mpos1, mpos2], 'r-')
            handle.set_clip_on(False)
            if(target == 'plot'):
              self.__dict__['secondXLines' + suffix].append(handle)
            else:
              self.__dict__['secondXLinesResid' + suffix].append(handle)
        # and finally the axis label
        if(target == 'plot'):
          useLabel = 'handleSlavedXLabel'
        else:
          useLabel = 'handleSlavedXLabelResid'
        if(self.slavedXLabelShow and (axis == 'x')):
          if(self.modeX == 'linear'):
            midX = (self.minX + self.maxX) / 2
          else:
            midX = np.exp((np.log(self.minX) + np.log(self.maxX)) / 2.)
          # prepare axis label string
          text = self.slavedXLabel.replace('\n', '\\n')
          text = text.replace('\t', '\\t')
          text = '\n'.join([i for i in text.split('\\n') if (len(i.strip()))])
          text = '\t'.join([i for i in text.split('\\t') if (len(i.strip()))])
          self.__dict__[useLabel] = axisobject.text(midX, mpos2, text)

        # format labels
        if(target == 'plot'):
          useLabels = 'secondXLabels'
        else:
          useLabels = 'secondXLabelsResid'
        for label in self.__dict__[useLabels + suffix]:
          label.set_visible(self.ticksLabelShow['slaved'])
          label.set_color(self.slavedTicksXColor)
          label.set_fontsize(self.slavedTicksXSize)
          label.set_fontweight(self.slavedTicksXWeight)
          label.set_fontstyle(self.slavedTicksXStyle)
          label.set_rotation(self.slavedTicksXAngle)
          label.set_horizontalalignment(self.slavedTicksXAlignment)
          label.set_verticalalignment(self.slavedTicksXAlignmentVertical)

        # phew, font setting can be dangerous as we can deviated from font of main ticks
        # have to check for problematic fonts
        # there is still a residual danger here that we get a mortal crash ... :(
        for label in self.__dict__[useLabels + suffix]:
          label.set_fontname(self.tickFont['xs'])
          _, _, descent = label._get_layout(self.matplot.canvas.renderer)
          if(not (descent > 0)):
            self.parent.statusbar.showMessage('Experiencing problems setting font ' + self.tickFont['xs'] + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
            self.tickFont['xs'] = SAFE_FONT
            label.set_fontname(self.tickFont['xs'])

        # deal with transformation on tick labels
        pad, pad2 = self.slavedTicksXPad, self.slavedTicksXPad2
        offset = matplotlib.transforms.ScaledTranslation(pad2 / 72., pad / 72., plotobject.dpi_scale_trans)
        if(target == 'plot'):
          useLabels = 'secondXLabels'
        else:
          useLabels = 'secondXLabelsResid'
        for label in self.__dict__[useLabels + suffix]:
          # strip transform down to inner CompositeGenericTransform
          transformA, transformB = label.get_transform(), matplotlib.transforms.IdentityTransform()
          while(isinstance(transformA, matplotlib.transforms.CompositeGenericTransform)):
            transformA, transformB = transformA._a, transformA._b
          # compile new transform
          transAll = transformA + transformB + offset
          label.set_transform(transAll)
          
        # format lines
        if(target == 'plot'):
          useLines = 'secondXLines'
        else:
          useLines = 'secondXLinesResid'
        for line in self.__dict__[useLines + suffix]:
          line.set_visible(self.ticksVisible['slaved'])
          line.set_linewidth(self.ticksWidth['slaved'])
          line.set_color(self.ticksColor['slaved'])

        # format axis label
        if(axis == 'x'):
          if(target == 'plot'):
            useLabel = self.__dict__['handleSlavedXLabel']
          else:
            useLabel = self.__dict__['handleSlavedXLabelResid']
          if(useLabel != None):
            useLabel.set_color(self.slavedLabelXColor)
            useLabel.set_size(self.slavedLabelXSize)
            useLabel.set_fontweight(self.slavedLabelXWeight)
            useLabel.set_fontstyle(self.slavedLabelXStyle)
            ###useLabel.setAxisLabelVariant(value = self.labelXVariant, axis = 'x', redraw = False, target=target)
            useLabel.set_horizontalalignment(self.slavedLabelXAlignment)
            useLabel.set_verticalalignment(self.slavedLabelXAlignmentVertical)
            useLabel.set_linespacing(self.slavedLabelXLinespacing)
            useLabel.set_rotation(self.slavedLabelXAngle)

            # phew, font setting can be dangerous as we can deviate from font of main axis label
            # have to check for problematic fonts
            # there is still a residual danger here that we get a mortal crash ... :(
            useLabel.set_fontname(self.axisFont['xs'])
            _, _, descent = useLabel._get_layout(self.matplot.canvas.renderer)
            if(not (descent > 0)):
              self.parent.statusbar.showMessage('Experiencing problems setting font ' + self.axisFont['xs'] + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
              self.axisFont['xs'] = SAFE_FONT
              useLabel.set_fontname(self.axisFont['xs'])

            # need to convert xpos of label to coordinates on slaved x axis -- brrrr
            if(self.modeX == 'linear'):
              usePos = self.minX + self.slavedLabelXPos * (self.maxX - self.minX)
            else:
              usePos = self.minX * np.exp(self.slavedLabelXPos * (np.log(self.maxX) - np.log(self.minX)))
            useLabel.set_x(usePos)
  
            # deal with transformation on axis labels
            # account for size and shift of axis ticks
            if((target == 'plot') and (len(self.__dict__['secondXLabels' + suffix]))):
              size = self.__dict__['secondXLabels' + suffix][0].get_size()
              shiftos = self.slavedTicksXPad + size
            elif((target == 'resid') and (len(self.__dict__['secondXLabelsResid' + suffix]))):
              size = self.__dict__['secondXLabelsResid' + suffix][0].get_size()
              shiftos = self.slavedTicksXPad + size
            else:
              shiftos = self.slavedTicksXPad
              
            pad, pad2 = self.slavedLabelXPad + shiftos, 0
            offset = matplotlib.transforms.ScaledTranslation(pad2 / 72., pad / 72., plotobject.dpi_scale_trans)
            # strip transform down to inner CompositeGenericTransform
            transformA, transformB = useLabel.get_transform(), matplotlib.transforms.IdentityTransform()
            while(isinstance(transformA, matplotlib.transforms.CompositeGenericTransform)):
              transformA, transformB = transformA._a, transformA._b
            # compile new transform
            transAll = transformA + transformB + offset
            useLabel.set_transform(transAll)
          
  def drawAxisArrow(self, axis='x', redraw=True, target='plot'):
    # draws arrowhead along axis
    drawOnDiv = self.splitShow and (axis == 'x')
    if((target in ['plot', 'resid']) and (axis in ['x', 'y', 'y2'])):
      # assign handles
      if(target == 'plot'):
        plotobject, axisobject, axisobject2, arrowhandle, arrowhandle2 = self.dataplotwidget, self.ax, self.ax_div, self.handleArrow, self.handleArrow2
        if(drawOnDiv):
          axisobject = self.ax_div
        if(axis == 'y2'):
          axisobject, axisobject2 = self.ax2, self.ax2_div
      else:
        plotobject, axisobject, axisobject2, arrowhandle, arrowhandle2 = self.residplotwidget, self.ax_resid, self.ax_resid_div, self.handleArrowResid, self.handleArrowResid2
        if(drawOnDiv):
          axisobject = self.ax_resid_div

      # assign values
      if(self.arrowEdgeShow[axis]):
        drawCol = self.arrowColor[axis]
      else:
        drawCol = 'None'
      if(self.arrowFillShow[axis]):
        drawFill = self.arrowFill[axis]
      else:
        drawFill = 'None'
      drawOverhang = self.arrowOverhang[axis]
      drawOffset = self.arrowOffset[axis]
      effectiveWidth = self.exportWidth * (self.padSize['right'] - self.padSize['left'])
      effectiveHeight = self.exportHeight * (self.padSize['top'] - self.padSize['bottom'])
      if(axis == 'x'):
        drawHeadWidth = self.arrowHeadWidth[axis] / effectiveHeight
        drawHeadLength = self.arrowHeadLength[axis] / effectiveWidth
      else:
        drawHeadWidth = self.arrowHeadWidth[axis] / effectiveWidth
        drawHeadLength = self.arrowHeadLength[axis] / effectiveHeight
      if(self.arrowZ[axis] == 'axis'):
        drawZ = axisobject.spines['bottom'].get_zorder() + 0.1
      else:
        # set to front
        drawZ = 500
      drawLw = self.arrowEdge[axis]
      
      # which axis to operate on?
      if(axis == 'x'):
        # calculate drawWidth
        if(drawOnDiv):
          useModeX, useMinX, useMaxX = self.modeX_div, self.minX_div, self.maxX_div
        else:
          useModeX, useMinX, useMaxX = self.modeX, self.minX, self.maxX
          
        if(useModeX == 'linear'):
          drawWidth = np.abs(useMaxX - useMinX)
          logx = False
        else:
          drawWidth = np.abs(np.log(useMaxX) - np.log(useMinX))
          logx = True

        if(self.splitShow):
          drawHeadLength *= drawWidth * (1 + 1 / self.splitFraction) * self.splitFraction
        else:
          drawHeadLength *= drawWidth
  
        # calculate drawHeight
        if(target == 'plot'):
          if(self.modeY == 'linear'):
            drawHeight = np.abs(self.maxY - self.minY)
            logy = False
          else:
            drawHeight = np.abs(np.log(self.maxY) - np.log(self.minY))
            logy = True
          if(self.modeY2 == 'linear'):
            drawHeight_2 = np.abs(self.maxY2 - self.minY2)
            logy_2 = False
          else:
            drawHeight_2 = np.abs(np.log(self.maxY2) - np.log(self.minY2))
            logy_2 = True

          if(self.splitY):
            drawHeadWidth_2 = drawHeadWidth * drawHeight_2 * (1 + self.splitYFraction) / self.splitYFraction
            drawHeadWidth *= drawHeight * (1 + self.splitYFraction)
          else:
            drawHeadWidth_2 = drawHeadWidth * drawHeight_2
            drawHeadWidth *= drawHeight
        else:
          drawHeight = np.abs(self.maxResidY - self.minResidY)
          # resid plot is always linear in y; account for difference in window size
          relativeSizes = self.masterwidget.sizes()
          sizeRatio = 1. * relativeSizes[0] / relativeSizes[1]
          drawHeadWidth *= drawHeight * sizeRatio
          logy = False

        # assign line width and xy coordinates
        ###drawLw = self.axisWidth['bottom']
        drawCs = self.axisDashStyle['bottom']
        
        if(useModeX == 'linear'):
          drawX = np.max((useMinX, useMaxX))
        else:
          drawX = np.max((np.log(useMinX), np.log(useMaxX)))

        # check for axis boundaries
        if('bottom' in self.ax2.spines):
          bounds = self.ax2.spines['bottom'].get_bounds()
        elif('top' in self.ax2.spines):
          bounds = self.ax2.spines['top'].get_bounds()
        else:
          bounds = None
        
        if((bounds != None) and len(bounds)):
          boundedMax = np.max(bounds)
          if((boundedMax < drawX) and (boundedMax >= np.min((useMinX, useMaxX)))):
            if(useModeX == 'linear'):
              drawX = boundedMax
            else:
              drawX = np.log(boundedMax)

        # apply offset
        drawX -= drawOffset * drawHeadLength
        
        # determine drawY and drawY2
        if(target == 'plot'):
          if(self.modeY == 'linear'):
            drawY, drawY2 = np.sort((self.minY, self.maxY))
          else:
            drawY, drawY2 = np.sort((np.log(self.minY), np.log(self.maxY)))
          if(self.modeY2 == 'linear'):
            drawY_2, drawY2_2 = np.sort((self.minY2, self.maxY2))
          else:
            drawY_2, drawY2_2 = np.sort((np.log(self.minY2), np.log(self.maxY2)))
        else:
          drawY, drawY2 = np.sort((self.minResidY, self.maxResidY))
        
        # account for shifted axis
        if(target == 'plot'):
          for item in ['bottom', 'top']:
            if(item in self.ax2.spines):
              if(self.splitY and (item == 'bottom')):
                useAxisObject, useAxisObject2, useModeY, useMinY, useMaxY = self.ax, self.ax2, self.modeY2, self.minY2, self.maxY2
              else:
                useAxisObject, useAxisObject2, useModeY, useMinY, useMaxY = self.ax2, self.ax, self.modeY, self.minY, self.maxY
              try:
                axisPosition, axisPositionValue = useAxisObject.spines[item].get_position()
                if(axisPosition == 'data'):
                  itemY = axisPositionValue
                  if(self.modeY == 'log'):
                    # have to test for zero/negative entry
                    if(itemY > 0):
                      itemY = np.log(itemY)
                    else:
                      if(item == 'bottom'):
                        itemY = np.log(np.min((useMinY, useMaxY)))
                      else:
                        itemY = np.log(np.min((useMinY, useMaxY)))
                elif(axisPosition in ['axes', 'outward']):
                  axis_to_data = useAxisObject2.transAxes + useAxisObject2.transData.inverted()
                  transformedPoint = axis_to_data.transform((0, axisPositionValue))
                  itemY = transformedPoint[1]
                  if(useModeY == 'log'):
                    itemY = np.log(itemY)
                if(axisPosition in ['data', 'axes', 'outward']):
                  if(item == 'bottom'):
                    if(self.splitY):
                      drawY_2 = itemY
                    else:
                      drawY = itemY
                  else:
                    drawY2 = itemY
              except:
                pass
        else:
          for item in ['bottom', 'top']:
            if(item in axisobject.spines):
              try:
                axisPosition, axisPositionValue = axisobject.spines[item].get_position()
                if(axisPosition == 'data'):
                  if(item == 'bottom'):
                    drawY = axisPositionValue
                  else:
                    drawY2 = axisPositionValue
                elif(axisPosition == 'axes'):
                  # in resid 
                  axis_to_data = axisobject.transAxes + axisobject.transData.inverted()
                  transformedPoint = axis_to_data.transform((0, axisPositionValue))
                  if(item == 'bottom'):
                    drawY = transformedPoint[1]
                  else:
                    drawY2 = transformedPoint[1]
              except:
                pass
      else:
        # this is the y arrows
        # calculate drawWidth
        if(self.modeX == 'linear'):
          drawHeight = np.abs(self.maxX - self.minX)
          logx = False
        else:
          drawHeight = np.abs(np.log(self.maxX) - np.log(self.minX))
          logx = True
        if(self.modeX_div == 'linear'):
          drawHeight_div = np.abs(self.maxX_div - self.minX_div)
          logx_div = False
        else:
          drawHeight_div = np.abs(np.log(self.maxX_div) - np.log(self.minX_div))
          logx_div = True

        if(self.splitShow):
          drawHeadWidth_div = drawHeadWidth * drawHeight_div * (1 + self.splitFraction)# / self.splitFraction
          drawHeadWidth *= drawHeight * (1 + self.splitFraction) / self.splitFraction
        else:
          drawHeadWidth_div = drawHeadWidth * drawHeight_div
          drawHeadWidth *= drawHeight
  
        # calculate drawHeight
        if(target == 'plot'):
          if(axis == 'y'):
            if(self.modeY == 'linear'):
              drawWidth = np.abs(self.maxY - self.minY)
              logy = False
            else:
              drawWidth = np.abs(np.log(self.maxY) - np.log(self.minY))
              logy = True
          else:
            if(self.modeY2 == 'linear'):
              drawWidth = np.abs(self.maxY2 - self.minY2)
              logy = False
            else:
              drawWidth = np.abs(np.log(self.maxY2) - np.log(self.minY2))
              logy = True

          if(self.splitY):
            drawHeadLength *= drawWidth * (1 + self.splitYFraction)
          else:
            drawHeadLength *= drawWidth
        else:
          drawWidth = np.abs(self.maxResidY - self.minResidY)
          # resid plot is always linear in y; account for difference in window size
          relativeSizes = self.masterwidget.sizes()
          sizeRatio = 1. * relativeSizes[0] / relativeSizes[1]
          drawHeadLength *= drawWidth * sizeRatio
          logy = False

        # assign line width and xy coordinates
        ###drawLw = self.axisWidth['left']
        drawCs = self.axisDashStyle['left']

        if(target == 'resid'):
          useMinY, drawY = np.sort((self.minResidY, self.maxResidY))
        elif(axis == 'y'):
          if (self.modeY == 'linear'):
            useMinY, drawY = np.sort((self.minY, self.maxY))
          else:
            useMinY, drawY = np.sort((np.log(self.minY), np.log(self.maxY)))
        else:
          if (self.modeY2 == 'linear'):
            useMinY, drawY = np.sort((self.minY2, self.maxY2))
          else:
            useMinY, drawY = np.sort((np.log(self.minY2), np.log(self.maxY2)))

        # check for axis boundaries
        bounds = None
        if('left' in axisobject.spines):
          bounds = axisobject.spines['left'].get_bounds()
        if((bounds == None) or (axis == 'y2')):
          # make arrow on second axes preferentially use the right axis
          if('right' in axisobject.spines):
            bounds = axisobject.spines['right'].get_bounds()
        
        if((bounds != None) and len(bounds)):
          boundedMax = np.max(bounds)
          if((boundedMax < drawY) and (boundedMax >= useMinY)):
            if((target == 'resid') or (self.modeY == 'linear')):
              drawY = boundedMax
            else:
              drawY = np.log(boundedMax)

        # apply offset
        drawY -= drawOffset * drawHeadLength
        
        # determine drawX
        if(self.modeX == 'linear'):
          drawX, drawX2 = np.sort((self.minX, self.maxX))
        else:
          drawX, drawX2 = np.sort((np.log(self.minX), np.log(self.maxX)))
        if(self.modeX_div == 'linear'):
          drawX_div, drawX2_div = np.sort((self.minX_div, self.maxX_div))
        else:
          drawX_div, drawX2_div = np.sort((np.log(self.minX_div), np.log(self.maxX_div)))

        # account for shifted axis
        for item in ['left', 'right']:
          if(item in axisobject.spines):
            if(self.splitShow and (item == 'right')):
              useAxisObject, useModeX, useMinX, useMaxX = axisobject2, self.modeX_div, self.minX_div, self.maxX_div
            else:
              useAxisObject, useModeX, useMinX, useMaxX = axisobject, self.modeX, self.minX, self.maxX
            try:
              axisPosition, axisPositionValue = useAxisObject.spines[item].get_position()
              if(axisPosition == 'data'):
                itemX = axisPositionValue
                if(useModeX == 'log'):
                  # have to test for zero/negative entry
                  if(itemX > 0):
                    itemX = np.log(itemX)
                  else:
                    if(item == 'left'):
                      itemX = np.log(np.min((useMinX, useMaxX)))
                    else:
                      itemX = np.log(np.max((useMinX, useMaxX)))
              elif(axisPosition == 'axes'):
                axis_to_data = useAxisObject.transAxes + useAxisObject.transData.inverted()
                transformedPoint = axis_to_data.transform((axisPositionValue, self.minY))
                itemX = transformedPoint[0]
                if(useModeX == 'log'):
                  itemX = np.log(itemX)
                  
              if(item == 'left'):
                drawX = itemX
              else:
                if(self.splitShow):
                  drawX2_div = itemX
                else:
                  drawX2 = itemX
            except:
              pass

      # remove previous arrows
      if(arrowhandle[axis] != None):
        arrowhandle[axis].remove()
        arrowhandle[axis] = None
      if(arrowhandle2[axis] != None):
        arrowhandle2[axis].remove()
        arrowhandle2[axis] = None

      # draw customized arrow
      if((axis == 'x') and (self.arrowLocation['x'] in ['both', 'bottom'])):
        if(self.splitY and (target == 'plot')):
          if(drawOnDiv):
            thisAxisObject = self.ax2_div
          else:
            thisAxisObject = self.ax2
          arrowhandle[axis] = self.drawMyArrow(axisobject=thisAxisObject, x=drawX, y=drawY_2, axis=axis,\
            head_width=drawHeadWidth_2, head_length=drawHeadLength, overhang=drawOverhang, linewidth=drawLw,\
            linecolor=drawCol, fillcolor=drawFill, capstyle=drawCs, zorder=drawZ, logx=logx, logy=logy_2)
        else:
          arrowhandle[axis] = self.drawMyArrow(axisobject=axisobject, x=drawX, y=drawY, axis=axis,\
            head_width=drawHeadWidth, head_length=drawHeadLength, overhang=drawOverhang, linewidth=drawLw,\
            linecolor=drawCol, fillcolor=drawFill, capstyle=drawCs, zorder=drawZ, logx=logx, logy=logy)
        arrowhandle[axis].set_clip_on(False)
      if((axis == 'x') and (self.arrowLocation['x'] in ['both', 'top'])):
        arrowhandle2[axis] = self.drawMyArrow(axisobject=axisobject, x=drawX, y=drawY2, axis=axis,\
          head_width=drawHeadWidth, head_length=drawHeadLength, overhang=drawOverhang, linewidth=drawLw,\
          linecolor=drawCol, fillcolor=drawFill, capstyle=drawCs, zorder=drawZ, logx=logx, logy=logy)
        arrowhandle2[axis].set_clip_on(False)

      if(axis in ['y', 'y2']):
        if(self.arrowLocation[axis] in ['both', 'left']):
          arrowhandle[axis] = self.drawMyArrow(axisobject=axisobject, x=drawX, y=drawY, axis=axis,\
            head_width=drawHeadWidth, head_length=drawHeadLength, overhang=drawOverhang, linewidth=drawLw,\
            linecolor=drawCol, fillcolor=drawFill, capstyle=drawCs, zorder=drawZ, logx=logx, logy=logy)
          arrowhandle[axis].set_clip_on(False)
        if(self.arrowLocation[axis] in ['both', 'right']):
          if(self.splitShow):
            arrowhandle2[axis] = self.drawMyArrow(axisobject=axisobject2, x=drawX2_div, y=drawY, axis=axis,\
              head_width=drawHeadWidth_div, head_length=drawHeadLength, overhang=drawOverhang, linewidth=drawLw,\
              linecolor=drawCol, fillcolor=drawFill, capstyle=drawCs, zorder=drawZ, logx=logx_div, logy=logy)
          else:
            arrowhandle2[axis] = self.drawMyArrow(axisobject=axisobject, x=drawX2, y=drawY, axis=axis,\
              head_width=drawHeadWidth, head_length=drawHeadLength, overhang=drawOverhang, linewidth=drawLw,\
              linecolor=drawCol, fillcolor=drawFill, capstyle=drawCs, zorder=drawZ, logx=logx, logy=logy)
          arrowhandle2[axis].set_clip_on(False)

      if(redraw):
        plotobject.myRefresh()

  def drawMyArrow(self, axisobject, x, y, axis, head_width, head_length, overhang, linewidth, linecolor, fillcolor, capstyle, zorder, logx=False, logy=False):
    # draw customized arrow
    # calculate coordinates
    coords = np.array(4 * [[x, y]])
    if(axis=='x'):
      coords[0, 0] += head_length * overhang
      coords[2, 0] += head_length
      coords[1, 1] += head_width / 2.0
      coords[3, 1] -= head_width / 2.0
    else:
      coords[1, 0] += head_width / 2.0
      coords[3, 0] -= head_width / 2.0
      coords[0, 1] += head_length * overhang
      coords[2, 1] += head_length

    # transform to log scale?
    if(logx):
      coords[:, 0] = np.exp(coords[:,0])
    if(logy):
      coords[:, 1] = np.exp(coords[:,1])

    if(axisobject is self.ax):
      axisname = 'ax'
    else:
      axisname = 'ax_resid'
      
    if(overhang < 1):
      polyPatch = matplotlib.patches.Polygon(coords, closed=True, facecolor=fillcolor, fill=True,\
        edgecolor=linecolor, linestyle='solid', linewidth=linewidth, zorder=zorder, capstyle=capstyle)
      retv = axisobject.add_patch(polyPatch)
    else:
      polyPatch = matplotlib.patches.Polygon(coords[1:,:], closed=False, fill=False,\
        edgecolor=linecolor, linestyle='solid', linewidth=linewidth, zorder=zorder, capstyle=capstyle)
      retv = axisobject.add_patch(polyPatch)

    return retv

  def setCanvasGradientStyle(self, value=0.0, redraw=True, target='plot'):
    # sets angle of canvas gradient
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        if(self.canvasGradientStyle == value):
          redraw = False
        else:
          self.canvasGradientStyle = value
      else:
        if(self.canvasGradientStyle_resid == value):
          redraw = False
        else:
          self.canvasGradientStyle_resid = value
          
      # issue redraw if needed
      if(redraw and self.canvasGradientVisible):
        self.setCanvasGradient(state=True, redraw=redraw, target=target)

  def setCanvasGradientColor(self, value=0.0, redraw=True, target='plot', color=0):
    # sets angle of canvas gradient
    if((target in ['plot', 'resid']) and (color in [0, 1])):
      if(target == 'plot'):
        if(color):
          if(self.canvasGradientColor2 == value):
            redraw = False
          else:
            self.canvasGradientColor2 = value
        else:
          if(self.canvasGradientColor1 == value):
            redraw = False
          else:
            self.canvasGradientColor1 = value
      else:
        if(color):
          if(self.canvasGradientColor2_resid == value):
            redraw = False
          else:
            self.canvasGradientColor2_resid = value
        else:
          if(self.canvasGradientColor1_resid == value):
            redraw = False
          else:
            self.canvasGradientColor1_resid = value
          
      # issue redraw if needed
      if(redraw and self.canvasGradientVisible):
        self.setCanvasGradient(state=True, redraw=redraw, target=target)

  def setCanvasGradientCenter(self, value=0.0, redraw=True, target='plot', axis='x'):
    # sets angle of canvas gradient
    if((target in ['plot', 'resid']) and (axis in ['x', 'y'])):
      if(axis == 'x'):
        index = 0
      else:
        index = 1
      if(target == 'plot'):
        if(self.canvasGradientCenter[index] == value):
          redraw = False
        else:
          self.canvasGradientCenter[index] = value
      else:
        if(self.canvasGradientCenter_resid[index] == value):
          redraw = False
        else:
          self.canvasGradientCenter_resid[index] = value
          
      # issue redraw if needed
      if(redraw and self.canvasGradientVisible and (self.canvasGradientStyle == 'radial')):
        self.setCanvasGradient(state=True, redraw=redraw, target=target)

  def setCanvasGradientWidth(self, value=0.0, redraw=True, target='plot'):
    # sets angle of canvas gradient
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        if(self.canvasGradientWidth == value):
          redraw = False
        else:
          self.canvasGradientWidth = value
      else:
        if(self.canvasGradientWidth_resid == value):
          redraw = False
        else:
          self.canvasGradientWidth_resid = value
          
      # issue redraw if needed
      if(redraw and self.canvasGradientVisible and (self.canvasGradientStyle == 'radial')):
        self.setCanvasGradient(state=True, redraw=redraw, target=target)

  def setCanvasGradientAngle(self, value=0.0, redraw=True, target='plot'):
    # sets angle of canvas gradient
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        if(self.canvasGradientAngle == value):
          redraw = False
        else:
          self.canvasGradientAngle = value
      else:
        if(self.canvasGradientAngle_resid == value):
          redraw = False
        else:
          self.canvasGradientAngle_resid = value
          
      # issue redraw if needed
      if(redraw and self.canvasGradientVisible and (self.canvasGradientStyle == 'linear')):
        self.setCanvasGradient(state=True, redraw=redraw, target=target)
  
  def setCanvasGradient(self, state=True, redraw=True, target='plot', steps=50):
    # draws a color gradient on axis background
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, planeobject, handleContour = self.dataplotwidget, self.colorPlane, self.colorBackground
        axisobject = self.ax2; axisobject2 = self.ax2_div
      else:
        plotobject, planeobject, handleContour = self.residplotwidget, self.colorPlane_resid, self.colorBackground_resid
        axisobject, axisobject2 = self.ax_resid, self.ax_resid_div
      
      # remove previous gradient (if present)
      if(handleContour != None):
        # unexpectedly collections became deprecated, so have to escape this
        try:
          for entry in handleContour.collections:
            entry.remove()
        except:
          # this should take care of that
          handleContour.remove()
        handleContour = None
  
      self.canvasGradientVisible = state
      if(state):
        # define new gradient
        gradientDict = {}
        for index, component in enumerate(['red', 'green', 'blue', 'alpha']):
          gradientDictTemp = {component: ((0.0, self.canvasGradientColor1[index], self.canvasGradientColor1[index]),
                                          (1.0, self.canvasGradientColor2[index], self.canvasGradientColor2[index]))}
          gradientDict.update(gradientDictTemp)
        gradientMap = matplotlib.colors.LinearSegmentedColormap('MyMap', gradientDict)
        x, y = np.linspace(0, 1, steps), np.linspace(0, 1, steps)
        X, Y = np.meshgrid(x, y)
        if(self.canvasGradientStyle == 'linear'):
          angle = self.canvasGradientAngle / 360.0 * 2 * np.pi
          Z = X * np.cos(angle) + Y * np.sin(angle)
        else:
          width = self.canvasGradientWidth
          Z = np.exp(-(((X - self.canvasGradientCenter[0]) / width) ** 2) - (((Y - self.canvasGradientCenter[1]) / width) ** 2))
        handleContour = planeobject.contourf(X, Y, Z, 128, cmap=gradientMap, vmax=1.0, vmin=0.0)
        # hide canvas background of top plots
        axisobject.patch.set_facecolor([1.0] * 3 + [0.0])
        axisobject2.patch.set_facecolor([1.0] * 3 + [0.0])
        if(self.splitY and (target == 'plot')):
          self.ax.patch.set_facecolor([1.0] * 3 + [0.0])
          self.ax_div.patch.set_facecolor([1.0] * 3 + [0.0])
      else:
        # restore canvas background of top plots
        if(self.canvasFill):
          axisobject.patch.set_facecolor(self.canvasColor)
          axisobject2.patch.set_facecolor(self.canvasColor)
          if(self.splitY and (target == 'plot')):
            self.ax.patch.set_facecolor(self.canvasColor)
            self.ax_div.patch.set_facecolor(self.canvasColor)
        else:
          axisobject.patch.set_facecolor('none')
          axisobject2.patch.set_facecolor('none')
          if(self.splitY and (target == 'plot')):
            self.ax.patch.set_facecolor('none')
            self.ax_div.patch.set_facecolor('none')
  
      if(redraw):
        plotobject.myRefresh()
  
      # assign handles
      if(target == 'plot'):
        self.colorBackground = handleContour
      else:
        self.colorBackground_resid = handleContour

  def setFigureColor(self, value=[0, 0, 0, 1], redraw=True, target='plot', silent=True):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, matobject, objectname = self.dataplotwidget, self.matplot, 'matplot'
      else:
        plotobject, matobject, objectname = self.residplotwidget, self.residplot, 'residplot'
      # sets figure background color
      if((self.figureColor == value) and (target == 'plot')):
        redraw = False
        
      self.figureColor = value
      # always change figure color on screen as we cannot assume which label and axis color etc. people are using
      if((not self.figureFill) and (not silent)):
        self.parent.statusbar.showMessage('Note that figure background of exported graphics is currently set to transparent.', self.parent.STATUS_TIME, color='blue')
      matobject.set_facecolor(self.figureColor)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def toggleFigureFill(self, value=True, redraw=True, target='plot', silent=True):
    # toggles whether figure background is filled or not
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject = self.dataplotwidget
      else:
        plotobject = self.residplotwidget

      # disable redraw and never change figure color on screen as we cannot assume which label and axis color etc. people are using
      redraw = False
      if((not value) and (not silent)):
        self.parent.statusbar.showMessage('Figure background of exported graphics set to transparent.', self.parent.STATUS_TIME, color='blue')
  
      self.figureFill = value
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def toggleFrameDraw(self, value=True, redraw=True, target='plot'):
    # toggles whether figure frame is drawn or not
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, figobject = self.dataplotwidget, self.matplot
        if(self.frameDraw == value):
          redraw = False
        self.frameDraw = value
      else:
        plotobject, figobject = self.residplotwidget, self.residplot
        if(self.frameDraw_resid == value):
          redraw = False
        self.frameDraw_resid = value

      # adjust value
      if(value):
        if(target == 'plot'):
          value = self.frameColor
        else:
          value = self.frameColor_resid
      else:
        value = 'none'
      # sets frame color
      figobject.patch.set_edgecolor(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def toggleCanvasFill(self, value=True, redraw=True, target='plot'):
    # toggles whether canvas is filled or not
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject, axisobject2 = self.dataplotwidget, self.ax2, self.ax2_div
      else:
        plotobject, axisobject, axisobject2 = self.residplotwidget, self.ax_resid, self.ax_resid_div
      # sets canvas color
      if((self.canvasFill == value) and (target == 'plot')):
        redraw = False

      self.canvasFill = value
      # adjust value
      if(value):
        value = self.canvasColor
      else:
        value = 'none'
      if(not self.canvasGradientVisible):
        axisobject.patch.set_facecolor(value)
        axisobject2.patch.set_facecolor(value)
        if(self.splitY and (target == 'plot')):
          self.ax.patch.set_facecolor(value)
          self.ax_div.patch.set_facecolor(value)

      # redraw?
      if(redraw):
        plotobject.myRefresh()
        
  def setCanvasColor(self, value=[0, 0, 0, 1], redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
      else:
        plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
      # sets canvas color
      if((self.canvasColor == value) and (target == 'plot')):
        redraw = False

      self.canvasColor = value
      if(self.canvasFill):
        if(not self.canvasGradientVisible):
          axisobject.patch.set_facecolor(self.canvasColor)
          axisobject2.patch.set_facecolor(self.canvasColor)
          if(self.splitY and (target == 'plot')):
            self.ax.patch.set_facecolor(self.canvasColor)
            self.ax_div.patch.set_facecolor(self.canvasColor)
      else:
        redraw = False
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setFrameWidth(self, value=0.5, redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, figobject, axisname, plotname = self.dataplotwidget, self.matplot, 'ax', 'matplot'
        if(self.frameWidth == value):
          redraw = False
        self.frameWidth = value
      else:
        plotobject, figobject, axisname, plotname = self.residplotwidget, self.residplot, 'ax_resid', 'residplot'
        if(self.frameWidth_resid == value):
          redraw = False
        self.frameWidth_resid = value

      # check frame style to avoid crash at zero width
      if(value < .01):
        if(target == 'plot'):
          if(self.frameStyle != 'solid'):
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            self.frameStyle = 'solid'
            figobject.patch.set_linestyle('solid')
        else:
          if(self.frameStyle_resid != 'solid'):
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            self.frameStyle_resid = 'solid'
            figobject.patch.set_linestyle('solid')

      # sets frame width
      figobject.patch.set_linewidth(value)
      
      # check whether frame is visible at all
      if(not self.frameDraw):
        redraw = False
      
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setFrameStyle(self, value='solid', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, figobject, axisname, plotname = self.dataplotwidget, self.matplot, 'ax', 'matplot'
        if(self.frameStyle == value):
          redraw = False
        self.frameStyle = value
      else:
        plotobject, figobject, axisname, plotname = self.residplotwidget, self.residplot, 'ax_resid', 'residplot'
        if(self.frameStyle_resid == value):
          redraw = False
        self.frameStyle_resid = value

      # check frame style to avoid crash at zero width
      if(value != 'solid'):
        if(target == 'plot'):
          if(self.frameWidth < .01):
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            value = 'solid'
            self.frameStyle = 'solid'
        else:
          if(self.frameWidth_resid < .01):
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            value = 'solid'
            self.frameStyle_resid = 'solid'

      # sets frame line style
      figobject.patch.set_linestyle(value)

      # check whether frame is visible at all
      if(not self.frameDraw):
        redraw = False
      
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setFrameDashStyle(self, value='solid', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, figobject, axisname, plotname = self.dataplotwidget, self.matplot, 'ax', 'matplot'
        if(self.frameDashStyle == value):
          redraw = False
        self.frameDashStyle = value
      else:
        plotobject, figobject, axisname, plotname = self.residplotwidget, self.residplot, 'ax_resid', 'residplot'
        if(self.frameDashStyle_resid == value):
          redraw = False
        self.frameDashStyle_resid = value

      # sets grid line style
      figobject.patch.set_capstyle(value)

      # check whether frame is visible at all
      if(not self.frameDraw):
        redraw = False
      
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setFrameColor(self, value=[0, 0, 0, 1], redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, figobject, axisname, plotname = self.dataplotwidget, self.matplot, 'ax', 'matplot'
        if(self.frameColor == value):
          redraw = False
        self.frameColor = value
      else:
        plotobject, figobject, axisname, plotname = self.residplotwidget, self.residplot, 'ax_resid', 'residplot'
        if(self.frameColor_resid == value):
          redraw = False
        self.frameColor_resid = value

      # sets grid color
      if(self.frameDraw):
        figobject.patch.set_edgecolor(value)
      else:
        redraw = False

      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setGridOrder(self, value='back', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridOrder[axis] == value):
            redraw = False
          self.gridOrder[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridOrder_resid[axis] == value):
            redraw = False
          self.gridOrder_resid[axis] = value
          
        if(value == 'back'):
          useZ = 1
        else:
          useZ = 500
        # so here is the deal: set_axisbelow() toggles axis zorder between 0.5 and 2.5
        # however, due to z offset, all our objects are at higher z values than 2.5
        # hence draw custom lines
        if(target == 'plot'):
          for line in self.gridLinesStore[axis]:
            line.set_zorder(useZ)
        else:
          for line in self.gridLinesStore_resid[axis]:
            line.set_zorder(useZ)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridStyle(self, value='solid', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridStyle[axis] == value):
            redraw = False
          self.gridStyle[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridStyle_resid[axis] == value):
            redraw = False
          self.gridStyle_resid[axis] = value

        # check grid style to avoid crash at zero width
        if(value != 'solid'):
          if(self.gridWidth[axis] < .01):
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            value = 'solid'
            if(target == 'plot'):
              self.gridStyle[axis] = 'solid'
            else:
              self.gridStyle_resid[axis] = 'solid'

        # sets grid line style
        if(target == 'plot'):
          for line in self.gridLinesStore[axis]:
            line.set_linestyle(value)
        else:
          for line in self.gridLinesStore_resid[axis]:
            line.set_linestyle(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridDashStyle(self, value='butt', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridDashStyle[axis] == value):
            redraw = False
          self.gridDashStyle[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridDashStyle_resid[axis] == value):
            redraw = False
          self.gridDashStyle_resid[axis] = value

        # sets grid line dash style
        if(target == 'plot'):
          for line in self.gridLinesStore[axis]:
            line.set_dash_capstyle(value)
            line.set_solid_capstyle(value)
        else:
          for line in self.gridLinesStore_resid[axis]:
            line.set_dash_capstyle(value)
            line.set_solid_capstyle(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridWidth(self, value=0.5, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridWidth[axis] == value):
            redraw = False
          self.gridWidth[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridWidth_resid[axis] == value):
            redraw = False
          self.gridWidth_resid[axis] = value

        # check grid style to avoid crash at zero width
        if(value < .01):
          if(target == 'plot'):
            if(self.gridStyle[axis] != 'solid'):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              self.gridStyle[axis] = 'solid'
              for line in self.gridLinesStore[axis]:
                line.set_linestyle('solid')
          else:
            if(self.gridStyle_resid[axis] != 'solid'):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              self.gridStyle_resid[axis] = 'solid'
              for line in self.gridLinesStore_resid[axis]:
                line.set_linestyle('solid')

        # sets grid line width
        if(target == 'plot'):
          for line in self.gridLinesStore[axis]:
            line.set_linewidth(value)
        else:
          for line in self.gridLinesStore_resid[axis]:
            line.set_linewidth(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridColor(self, value=[0, 0, 0, 1], axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridColor[axis] == value):
            redraw = False
          self.gridColor[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridColor_resid[axis] == value):
            redraw = False
          self.gridColor_resid[axis] = value

        # sets grid line color
        if(target == 'plot'):
          for line in self.gridLinesStore[axis]:
            line.set_color(value)
        else:
          for line in self.gridLinesStore_resid[axis]:
            line.set_color(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridRectColor(self, value=[0, 0, 0, 1], axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridRectColor[axis] == value):
            redraw = False
          self.gridRectColor[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridRectColor_resid[axis] == value):
            redraw = False
          self.gridRectColor_resid[axis] = value

        # sets grid line color
        if(target == 'plot'):
          for rect in self.gridRectStore[axis]:
            rect.set_facecolor(value)
        else:
          for rect in self.gridRectStore_resid[axis]:
            rect.set_facecolor(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridRectVisibility(self, value=True, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridRectVisible[axis] == value):
            redraw = False
          self.gridRectVisible[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridRectVisible_resid[axis] == value):
            redraw = False
          self.gridRectVisible_resid[axis] = value

        # sets visibility of grid lines
        self.drawAxisGrid(axis=axis, redraw=False, target=target)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridVisibility(self, value=True, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridVisible[axis] == value):
            redraw = False
          self.gridVisible[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridVisible_resid[axis] == value):
            redraw = False
          self.gridVisible_resid[axis] = value

        # sets visibility of grid lines
        self.drawAxisGrid(axis=axis, redraw=False, target=target)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridMinorOrder(self, value='back', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridMinorOrder[axis] == value):
            redraw = False
          self.gridMinorOrder[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridMinorOrder_resid[axis] == value):
            redraw = False
          self.gridMinorOrder_resid[axis] = value
          
        if(value == 'back'):
          useZ = 1
        else:
          useZ = 500
        # so here is the deal: set_axisbelow() toggles axis zorder between 0.5 and 2.5
        # however, due to z offset, all our objects are at higher z values than 2.5
        # hence draw custom lines
        if(target == 'plot'):
          for line in self.gridMinorLinesStore[axis]:
            line.set_zorder(useZ)
        else:
          for line in self.gridMinorLinesStore_resid[axis]:
            line.set_zorder(useZ)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridMinorStyle(self, value='solid', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridMinorStyle[axis] == value):
            redraw = False
          self.gridMinorStyle[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridMinorStyle_resid[axis] == value):
            redraw = False
          self.gridMinorStyle_resid[axis] = value

        # check grid style to avoid crash at zero width
        if(value != 'solid'):
          if(self.gridMinorWidth[axis] < .01):
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            value = 'solid'
            if(target == 'plot'):
              self.gridMinorStyle[axis] = 'solid'
            else:
              self.gridMinorStyle_resid[axis] = 'solid'

        # sets grid line style
        if(target == 'plot'):
          for line in self.gridMinorLinesStore[axis]:
            line.set_linestyle(value)
        else:
          for line in self.gridMinorLinesStore_resid[axis]:
            line.set_linestyle(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridMinorDashStyle(self, value='butt', axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridMinorDashStyle[axis] == value):
            redraw = False
          self.gridMinorDashStyle[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridMinorDashStyle_resid[axis] == value):
            redraw = False
          self.gridMinorDashStyle_resid[axis] = value

        # sets grid line dash style
        if(target == 'plot'):
          for line in self.gridMinorLinesStore[axis]:
            line.set_dash_capstyle(value)
            line.set_solid_capstyle(value)
        else:
          for line in self.gridMinorLinesStore_resid[axis]:
            line.set_dash_capstyle(value)
            line.set_solid_capstyle(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridMinorWidth(self, value=0.5, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridMinorWidth[axis] == value):
            redraw = False
          self.gridMinorWidth[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridMinorWidth_resid[axis] == value):
            redraw = False
          self.gridMinorWidth_resid[axis] = value

        # check grid style to avoid crash at zero width
        if(value < .01):
          if(target == 'plot'):
            if(self.gridMinorStyle[axis] != 'solid'):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              self.gridMinorStyle[axis] = 'solid'
              for line in self.gridMinorLinesStore[axis]:
                line.set_linestyle('solid')
          else:
            if(self.gridMinorStyle_resid[axis] != 'solid'):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              self.gridMinorStyle_resid[axis] = 'solid'
              for line in self.gridMinorLinesStore_resid[axis]:
                line.set_linestyle('solid')

        # sets grid line width
        if(target == 'plot'):
          for line in self.gridMinorLinesStore[axis]:
            line.set_linewidth(value)
        else:
          for line in self.gridMinorLinesStore_resid[axis]:
            line.set_linewidth(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridMinorColor(self, value=[0, 0, 0, 1], axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridMinorColor[axis] == value):
            redraw = False
          self.gridMinorColor[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridMinorColor_resid[axis] == value):
            redraw = False
          self.gridMinorColor_resid[axis] = value

        # sets grid line color
        if(target == 'plot'):
          for line in self.gridMinorLinesStore[axis]:
            line.set_color(value)
        else:
          for line in self.gridMinorLinesStore_resid[axis]:
            line.set_color(value)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setGridMinorVisibility(self, value=True, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'x2', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.gridMinorVisible[axis] == value):
            redraw = False
          self.gridMinorVisible[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.gridMinorVisible_resid[axis] == value):
            redraw = False
          self.gridMinorVisible_resid[axis] = value

        # sets visibility of grid lines
        self.drawMinorAxisGrid(axis=axis, redraw=False, target=target)

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setPadding(self, value=0.5, axis='bottom', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['bottom', 'top', 'left', 'right']):
        if(target == 'plot'):
          plotobject, matobject, objectname = self.dataplotwidget, self.matplot, 'matplot'
          if(self.padSize[axis] == value):
            redraw = False

          # implement check for too large padding in case of split plots
          if((axis in ['left', 'right']) and self.splitShow):
            flag = False
            if(axis == 'left'):
              maxPad = .45 - self.splitPad / 2.
              if(value > maxPad):
                flag = True
            else:
              maxPad = .55 + self.splitPad / 2.
              if(value < maxPad):
                flag = True
            if(flag):
              if(hasattr(self.parent, 'statusbar')):
                self.parent.statusbar.showMessage('Cannot set ' + axis + ' padding to ' + self.parent.formatNumber(value) + ', using ' + self.parent.formatNumber(maxPad) + ' instead!', self.parent.STATUS_TIME)
              value, redraw = maxPad, True
          elif((axis in ['bottom', 'top']) and self.splitY):
            flag = False
            if(axis == 'bottom'):
              maxPad = .45 - self.splitYPad / 2.
              if(value > maxPad):
                flag = True
            else:
              maxPad = .55 + self.splitYPad / 2.
              if(value < maxPad):
                flag = True
            if(flag):
              if(hasattr(self.parent, 'statusbar')):
                self.parent.statusbar.showMessage('Cannot set ' + axis + ' padding to ' + self.parent.formatNumber(value) + ', using ' + self.parent.formatNumber(maxPad) + ' instead!', self.parent.STATUS_TIME)
              value, redraw = maxPad, True

          self.padSize[axis] = value
          
          # set padding
          if(self.splitY):
            ybreak = (self.padSize['top'] - self.padSize['bottom'] - self.splitYPad) * self.splitYFraction / (1.0 + self.splitYFraction)
            if(self.splitShow):
              xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
              self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['left'] + xbreak,  self.padSize['top']]]))
              self.ax_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['right'],  self.padSize['top']]]))
              self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['bottom'] + ybreak]]))
              self.ax2_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['bottom'] + ybreak]]))
            else:
              self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom'] + ybreak + self.splitYPad], [self.padSize['right'], self.padSize['top']]]))
              self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['bottom'] + ybreak]]))
          else:
            if(self.splitShow):
              xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
              self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak,  self.padSize['top']]]))
              self.ax_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'],  self.padSize['top']]]))
              self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['top']]]))
              self.ax2_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
            else:
              self.ax.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
              self.ax2.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
              
          # also need to adjust color plane
          self.colorPlane.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
        else:
          plotobject, matobject, objectname = self.residplotwidget, self.residplot, 'residplot'
          if(matobject.subplotpars.__dict__[axis] == value):
            redraw = False

          # implement check for too large padding in case of split plots
          if((axis in ['left', 'right']) and self.splitShow):
            flag = False
            if(axis == 'left'):
              maxPad = .45 - self.splitPad / 2.
              if(value > maxPad):
                flag = True
            else:
              maxPad = .55 + self.splitPad / 2.
              if(value < maxPad):
                flag = True
            if(flag):
              if(hasattr(self.parent, 'statusbar')):
                self.parent.statusbar.showMessage('Cannot set ' + axis + ' padding to ' + self.parent.formatNumber(value) + ', using ' + self.parent.formatNumber(maxPad) + ' instead!', self.parent.STATUS_TIME)
              value, redraw = maxPad, True

          self.padSize_resid[axis] = value

          # set padding
          if(self.splitShow):
            xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
            self.ax_resid.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['left'] + xbreak, self.padSize['top']]]))
            self.ax_resid_div.set_position(matplotlib.transforms.Bbox([[self.padSize['left'] + xbreak + self.splitPad, self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
          else:
            self.ax_resid.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))

          # also need to adjust color plane
          self.colorPlane_resid.set_position(matplotlib.transforms.Bbox([[self.padSize['left'], self.padSize['bottom']], [self.padSize['right'], self.padSize['top']]]))
        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setTickOne4All(self, axis='x', redraw=True, target='plot'):
    # calls all tick label formatters successively to fix glitch when no. of tick labels change upon rescaling
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject,  axisobject, axisobject2 = self.dataplotwidget, self.ax2, self.ax2_div
        else:
          plotobject,  axisobject, axisobject2 = self.residplotwidget, self.ax_resid, self.ax_resid_div
        # which axis?  
        if(axis == 'x'):
          tickLabelColor, tickLabelSize, tickLabelWeight, tickLabelStyle, tickLabelFont = self.ticksXColor, self.ticksXSize, self.ticksXWeight, self.ticksXStyle, self.tickFont[axis]
          tickLabelAngle, tickLabelAlignment, tickLabelAlignmentVertical, tickLabelPad, tickLabelPad2 = self.ticksXAngle, self.ticksXAlignment, self.ticksXAlignmentVertical, self.ticksXPad, self.ticksXPad2
        elif(axis == 'y'):
          if(target == 'plot'):
            axisobject, axisobject2 = self.ax, self.ax_div
          tickLabelColor, tickLabelSize, tickLabelWeight, tickLabelStyle, tickLabelFont = self.ticksYColor, self.ticksYSize, self.ticksYWeight, self.ticksYStyle, self.tickFont[axis]
          tickLabelAngle, tickLabelAlignment, tickLabelAlignmentVertical, tickLabelPad, tickLabelPad2 = self.ticksYAngle, self.ticksYAlignment, self.ticksYAlignmentVertical, self.ticksYPad, self.ticksYPad2
        else:
          tickLabelColor, tickLabelSize, tickLabelWeight, tickLabelStyle, tickLabelFont = self.ticksY2Color, self.ticksY2Size, self.ticksY2Weight, self.ticksY2Style, self.tickFont[axis]
          tickLabelAngle, tickLabelAlignment, tickLabelAlignmentVertical, tickLabelPad, tickLabelPad2 = self.ticksY2Angle, self.ticksY2Alignment, self.ticksY2AlignmentVertical, self.ticksY2Pad, self.ticksY2Pad2
        
        if(axis == 'x'):
          tickLabels = axisobject.get_xticklabels(which='both')
          tickLabels.append(axisobject.xaxis.get_offset_text())
          tickLabels.extend(axisobject2.get_xticklabels(which='both'))
          tickLabels.append(axisobject2.xaxis.get_offset_text())
          ticks = axisobject.xaxis.get_major_ticks()
          ticks.extend(axisobject.xaxis.get_minor_ticks())
          ticks.extend(axisobject2.xaxis.get_major_ticks())
          ticks.extend(axisobject2.xaxis.get_minor_ticks())
          if(self.splitY and (target == 'plot')):
            tickLabels.extend(self.ax.get_xticklabels(which='both'))
            tickLabels.append(self.ax.xaxis.get_offset_text())
            ticks.extend(self.ax.xaxis.get_major_ticks())
            ticks.extend(self.ax.xaxis.get_minor_ticks())
            tickLabels.extend(self.ax_div.get_xticklabels(which='both'))
            tickLabels.append(self.ax_div.xaxis.get_offset_text())
            ticks.extend(self.ax_div.xaxis.get_major_ticks())
            ticks.extend(self.ax_div.xaxis.get_minor_ticks())
        else:
          tickLabels = axisobject.get_yticklabels(which='both')
          tickLabels.append(axisobject.yaxis.get_offset_text())
          tickLabels.extend(axisobject2.get_yticklabels(which='both'))
          tickLabels.append(axisobject2.yaxis.get_offset_text())
          ticks = axisobject.yaxis.get_major_ticks()
          ticks.extend(axisobject.yaxis.get_minor_ticks())
          ticks.extend(axisobject2.yaxis.get_major_ticks())
          ticks.extend(axisobject2.yaxis.get_minor_ticks())

        if((target == 'plot') or (axis != 'y2')):
          # don't call formatter functions as this is too slow
          ###safeFont = 'DejaVu Sans'
          useFont = tickLabelFont
          for entry in tickLabels:
            entry.set_color(tickLabelColor)
            entry.set_fontsize(tickLabelSize)
            entry.set_fontweight(tickLabelWeight)
            entry.set_fontstyle(tickLabelStyle)
            try:
              # we should be in the clear but there is a certain danger here
              entry.set_fontname(useFont)
              _, _, descent = entry._get_layout(self.matplot.canvas.renderer)
              if(not (descent > 0)):
                useFont = SAFE_FONT
                entry.set_fontname(useFont)
            except:
              useFont = SAFE_FONT
              entry.set_fontname(useFont)
            entry.set_rotation(tickLabelAngle)
            entry.set_horizontalalignment(tickLabelAlignment)
            if(tickLabelAlignmentVertical != 'center'):
              entry.set_verticalalignment(tickLabelAlignmentVertical)
            else:
              try:
                entry.set_verticalalignment('center_baseline')
              except:
                entry.set_verticalalignment('center')

          for entry in ticks:
            entry.set_pad(tickLabelPad)
         
          # call formatter for pad2 as this is more complicated
          self.setTickLabelPad2(value=tickLabelPad2, axis=axis, redraw=False, target=target)
          
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setTickLabelColor(self, value=[0, 0, 0, 1], axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          # sets axis label color
          if(axis == 'x'):
            if((self.ticksXColor == value) or (not self.ticksXShow)):
              redraw = False
            self.ticksXColor = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
            if(self.splitY):
              tickLabels.extend(self.ax.get_xticklabels(which='both'))
              tickLabels.append(self.ax.xaxis.get_offset_text())
              tickLabels.extend(self.ax_div.get_xticklabels(which='both'))
              tickLabels.append(self.ax_div.xaxis.get_offset_text())
          elif(axis == 'y'):
            axisobject, axisobject2, axisname = self.ax, self.ax_div, 'ax'
            if((self.ticksYColor == value) or (not self.ticksYShow)):
              redraw = False
            self.ticksYColor = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if((self.ticksY2Color == value) or (not self.ticksY2Show)):
              redraw = False
            self.ticksY2Color = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          # sets axis label color
          if(axis == 'x'):
            if((self.ticksXColor_resid == value) or (not self.ticksXShow_resid)):
              redraw = False
            self.ticksXColor_resid = value
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          else:
            if((self.ticksYColor_resid == value) or (not self.ticksResidYShow)):
              redraw = False
            self.ticksYColor_resid = value
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedTicksXColor == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXColor = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedTicksXColor_resid == value) or (not self.ticksXShow)):
            redraw = False
          self.slavedTicksXColor_resid = value
          
        # set tickLabels to empty list as draw2ndAxisTicks() will take care of the rest
        tickLabels = []
        
      for entry in tickLabels:
        entry.set_color(value)
      # redraw?
      if(redraw):
        plotobject.myRefresh()

  def setTickMarkLabelShow(self, value=True, axis='left', redraw=True, target='plot'):
    # sets where to display the axis labels
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2 = self.dataplotwidget, self.ax2, self.ax2_div
          if (axis in ['left', 'right']):
            axisobject, axisobject2, axisname = self.ax, self.ax_div, 'ax'
        else:
          plotobject, axisobject, axisobject2 = self.residplotwidget, self.ax_resid, self.ax_resid_div

        self.ticksLabelShow[axis] = value
        if(axis == 'bottom'):
          axisobject.xaxis.set_tick_params(which='both', labelbottom=value)
          axisobject2.xaxis.set_tick_params(which='both', labelbottom=value)
        elif(axis == 'top'):
          axisobject.xaxis.set_tick_params(which='both', labeltop=value)
          axisobject2.xaxis.set_tick_params(which='both', labeltop=value)
        elif(axis in ['left', 'left2']):
          axisobject.yaxis.set_tick_params(which='both', labelleft=value)
          axisobject2.yaxis.set_tick_params(which='both', labelleft=value)
        else:
          axisobject.yaxis.set_tick_params(which='both', labelright=value)
          axisobject2.yaxis.set_tick_params(which='both', labelright=value)

        # check general tick show dialog
        if(value):
          if(axis in ['bottom', 'top']):
            self.ticksXShow, self.ticksXShow_resid = True, True
            if(hasattr(self.parent, 'graphicsarea')):
              self.parent.graphicsarea.configTickXLabel.setChecked(True)
            # and activate TickOne4All()
            self.setTickOne4All(axis='x', redraw=False, target=target)
          elif(axis in ['left', 'right']):
            self.ticksYShow, self.ticksResidYShow  = True, True
            if(hasattr(self.parent, 'graphicsarea')):
              self.parent.graphicsarea.configTickYLabel.setChecked(True)
              self.parent.graphicsarea.configTickResidYLabel.setChecked(True)
              self.parent.graphicsarea.configInnerLabel.setChecked(True)
            self.setTickOne4All(axis='y', redraw=False, target=target)
          else:
            self.ticksY2Show  = True
            if(hasattr(self.parent, 'graphicsarea2')):
              self.parent.graphicsarea2.configTickYLabel.setChecked(True)
            self.setTickOne4All(axis='y2', redraw=False, target=target)
        
        # set visibility of inner items correctly
        self.updateInnerSituation()

        # redraw?
        if(redraw):
          plotobject.myRefresh()
      elif(axis == 'slaved'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.ticksLabelShow[axis] == value):
            redraw = False
          self.ticksLabelShow[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.ticksLabelShow_resid[axis] == value):
            redraw = False
          self.ticksLabelShow_resid[axis] = value

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setTickMarkDirection(self, value='in', axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if(self.ticksDirection[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis, axisobject, axisobject2, axisname = 'y', self.ax, self.ax_div, 'ax'
            self.ticksDirection['left'] = value
            self.ticksDirection['right'] = value
          elif (axis in ['left2', 'right2']):
            axis = 'y'
            self.ticksDirection['left2'] = value
            self.ticksDirection['right2'] = value
          else:
            axis = 'x'
            self.ticksDirection['top'] = value
            self.ticksDirection['bottom'] = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(self.ticksDirection_resid[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis = 'y'
            self.ticksDirection_resid['left'] = value
            self.ticksDirection_resid['right'] = value
          elif (axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            axis = 'x'
            self.ticksDirection_resid['top'] = value
            self.ticksDirection_resid['bottom'] = value

        # sets tick mark position
        if(axis != 'abort'):
          axisobject.tick_params(axis=axis, direction=value, which='both')
          axisobject2.tick_params(axis=axis, direction=value, which='both')
          if(self.splitY and (axis == 'x') and (target == 'plot')):
            self.ax.tick_params(axis=axis, direction=value, which='both')
            self.ax_div.tick_params(axis=axis, direction=value, which='both')
          
          # redraw?
          if(redraw):
            plotobject.myRefresh()
      elif(axis == 'slaved'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.ticksDirection[axis] == value):
            redraw = False
          self.ticksDirection[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.ticksDirection_resid[axis] == value):
            redraw = False
          self.ticksDirection_resid[axis] = value

        # redraw?
        if(redraw):
          plotobject.myRefresh()
          
  def setTickMarkVisibility(self, value=True, axis='left', redraw=True, target='plot', initMode=False):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if(axis in ['left', 'right']):
            axisobject, axisobject2, axisname = self.ax, self.ax_div, 'ax'
          if(self.ticksVisible[axis] == value):
            redraw = False
          self.ticksVisible[axis] = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(self.ticksVisible_resid[axis] == value):
            redraw = False
          self.ticksVisible_resid[axis] = value

        if(axis in ['left', 'right']):
          # modify ticks along y axes
          # adjust axis labels
          if(not initMode):
            if(axis == 'left'):
              if(value or self.ticksLabelShow['right']):
                self.ticksLabelShow['left'] = value
                # ensure that graphicsarea has already been created (required during program start)
                if(hasattr(self.parent, 'graphicsarea')):
                  self.parent.graphicsarea.configTickMarkLabelShow['left'].setChecked(value)
            else:
              if(value and (not self.ticksVisible['left'])):
                self.ticksLabelShow['left'] = False
                if(hasattr(self.parent, 'graphicsarea')):
                  self.parent.graphicsarea.configTickMarkLabelShow['left'].setChecked(False)
              if(value == (not self.ticksLabelShow['left'])):
                self.ticksLabelShow['right'] = value
                if(hasattr(self.parent, 'graphicsarea')):
                  self.parent.graphicsarea.configTickMarkLabelShow['right'].setChecked(value)
          # then set ticks and tick labels
          if(target == 'plot'):
            useShow = self.ticksYShow
          else:
            useShow = self.ticksResidYShow
          axisobject.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'], labelleft=self.ticksLabelShow['left'] and useShow, right=self.ticksVisible['right'], labelright=self.ticksLabelShow['right'] and useShow)
          axisobject2.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'], labelleft=self.ticksLabelShow['left'] and useShow, right=self.ticksVisible['right'], labelright=self.ticksLabelShow['right'] and useShow)
          if(self.splitY and (target == 'plot')):
            self.ax.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'], labelleft=self.ticksLabelShow['left'] and useShow, right=self.ticksVisible['right'], labelright=self.ticksLabelShow['right'] and useShow)
            self.ax_div.yaxis.set_tick_params(which='both', left=self.ticksVisible['left'], labelleft=self.ticksLabelShow['left'] and useShow, right=self.ticksVisible['right'], labelright=self.ticksLabelShow['right'] and useShow)
        elif (axis in ['left2', 'right2']):
          # adjust axis labels
          if(not initMode):
            if(axis == 'right2'):
              if(value or self.ticksLabelShow['left2']):
                self.ticksLabelShow['right2'] = value
                # ensure that graphicsarea has already been created (required during program start)
                if(hasattr(self.parent, 'graphicsarea2')):
                  self.parent.graphicsarea2.configTickMarkLabelShow['right2'].setChecked(value)
            else:
              if(value and (not self.ticksVisible['right2'])):
                self.ticksLabelShow['right2'] = False
                if(hasattr(self.parent, 'graphicsarea2')):
                  self.parent.graphicsarea2.configTickMarkLabelShow['right2'].setChecked(False)
              if(value == (not self.ticksLabelShow['right2'])):
                self.ticksLabelShow['left2'] = value
                if(hasattr(self.parent, 'graphicsarea2')):
                  self.parent.graphicsarea2.configTickMarkLabelShow['left2'].setChecked(value)
          # then set ticks and tick labels
          useShow = self.ticksY2Show
          axisobject.yaxis.set_tick_params(which='both', left=self.ticksVisible['left2'], labelleft=self.ticksLabelShow['left2'] and useShow, right=self.ticksVisible['right2'], labelright=self.ticksLabelShow['right2'] and useShow)
          axisobject2.yaxis.set_tick_params(which='both', left=self.ticksVisible['left2'], labelleft=self.ticksLabelShow['left2'] and useShow, right=self.ticksVisible['right2'], labelright=self.ticksLabelShow['right2'] and useShow)
        else:
          # modify ticks along x axes
          # adjust axis labels
          if(not initMode):
            if(axis == 'bottom'):
              if(value or self.ticksLabelShow['top']):
                self.ticksLabelShow['bottom'] = value
                # ensure that graphicsarea has already been created (required during program start)
                if(hasattr(self.parent, 'graphicsarea')):
                  self.parent.graphicsarea.configTickMarkLabelShow['bottom'].setChecked(value)
            else:
              if(value and (not self.ticksVisible['bottom'])):
                self.ticksLabelShow['bottom'] = False
                if(hasattr(self.parent, 'graphicsarea')):
                  self.parent.graphicsarea.configTickMarkLabelShow['bottom'].setChecked(False)
              if(value == (not self.ticksLabelShow['bottom'])):
                self.ticksLabelShow['top'] = value
                if(hasattr(self.parent, 'graphicsarea')):
                  self.parent.graphicsarea.configTickMarkLabelShow['top'].setChecked(value)
          # then set ticks and tick labels
          useShow, useShow2 = self.ticksXShow, self.ticksXShow_div
          axisobject.xaxis.set_tick_params(which='both', bottom=self.ticksVisible['bottom'], labelbottom=self.ticksLabelShow['bottom'] and useShow, top=self.ticksVisible['top'], labeltop=self.ticksLabelShow['top'] and useShow)
          axisobject2.xaxis.set_tick_params(which='both', bottom=self.ticksVisible['bottom'], labelbottom=self.ticksLabelShow['bottom'] and useShow2, top=self.ticksVisible['top'], labeltop=self.ticksLabelShow['top'] and useShow2)
              
        # remember settings
        if(axis in ['left', 'right']):
          axis = 'y'
        elif(axis in ['left2', 'right2']):
          if(target == 'plot'):
            axis = 'y'
          else:
            axis = 'abort'
        else:
          axis = 'x'
        
        if(axis != 'abort'):
          # need to solve inner situation
          self.updateInnerSituation()
          # redraw?
          if(redraw):
            plotobject.myRefresh()
      elif(axis == 'slaved'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.ticksVisible[axis] == value):
            redraw = False
          self.ticksVisible[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.ticksVisible_resid[axis] == value):
            redraw = False
          self.ticksVisible_resid[axis] = value

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setTickMarkColor(self, value=[0, 0, 0, 1], axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject, axisobject, axisobject2, axisname = self.dataplotwidget, self.ax2, self.ax2_div, 'ax2'
          if(self.ticksColor[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis, axisobject, axisobject2, axisname = 'y', self.ax, self.ax_div, 'ax'
            self.ticksColor['left'] = value
            self.ticksColor['right'] = value
          elif (axis in ['left2', 'right2']):
            axis = 'y'
            self.ticksColor['left2'] = value
            self.ticksColor['right2'] = value
          else:
            axis = 'x'
            self.ticksColor['top'] = value
            self.ticksColor['bottom'] = value
        else:
          plotobject, axisobject, axisobject2, axisname = self.residplotwidget, self.ax_resid, self.ax_resid_div, 'ax_resid'
          if(self.ticksColor_resid[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis = 'y'
            self.ticksColor_resid['left'] = value
            self.ticksColor_resid['right'] = value
          elif (axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            axis = 'x'
            self.ticksColor_resid['top'] = value
            self.ticksColor_resid['bottom'] = value

        # sets axis label color
        if(axis != 'abort'):
          axisobject.tick_params(axis=axis, color=value, which='both')
          axisobject2.tick_params(axis=axis, color=value, which='both')
          if(self.splitY and (axis == 'x') and (target == 'plot')):
            self.ax.tick_params(axis=axis, color=value, which='both')
            self.ax_div.tick_params(axis=axis, color=value, which='both')
    
          # redraw?
          if(redraw):
            plotobject.myRefresh()
      elif(axis == 'slaved'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.ticksColor[axis] == value):
            redraw = False
          self.ticksColor[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.ticksColor_resid[axis] == value):
            redraw = False
          self.ticksColor_resid[axis] = value

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setTickMarkWidth(self, value=1.0, axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(self.ticksWidth[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis = 'y'; axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            self.ticksWidth['left'] = value
            self.ticksWidth['right'] = value
          elif (axis in ['left2', 'right2']):
            axis = 'y'
            self.ticksWidth['left2'] = value
            self.ticksWidth['right2'] = value
          else:
            axis = 'x'
            self.ticksWidth['top'] = value
            self.ticksWidth['bottom'] = value
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
          if(self.ticksWidth_resid[axis] == value):
            redraw = False
          self.ticksWidth_resid[axis] = value
          if (axis in ['left', 'right']):
            axis = 'y'
            self.ticksWidth_resid['left'] = value
            self.ticksWidth_resid['right'] = value
          elif (axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            axis = 'x'
            self.ticksWidth_resid['top'] = value
            self.ticksWidth_resid['bottom'] = value

        if(axis != 'abort'):
          axisobject.tick_params(axis=axis, width=value, which='both')
          axisobject2.tick_params(axis=axis, width=value, which='both')
          if(self.splitY and (axis == 'x') and (target == 'plot')):
            self.ax.tick_params(axis=axis, width=value, which='both')
            self.ax_div.tick_params(axis=axis, width=value, which='both')
  
          # redraw?
          if(redraw):
            plotobject.myRefresh()
      elif(axis == 'slaved'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(self.ticksWidth[axis] == value):
            redraw = False
          self.ticksWidth[axis] = value
        else:
          plotobject = self.residplotwidget
          if(self.ticksWidth_resid[axis] == value):
            redraw = False
          self.ticksWidth_resid[axis] = value

        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setMinorTickRelativeLength(self, value, axis, redraw=True, target='plot'):
    # changes relative length of ticks
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2', 'xs']):
        if(target == 'plot'):
          if(axis == 'y'):
            if(self.ticksYMinorRelativeLength == value):
              redraw = False
            self.ticksYMinorRelativeLength = value
            item = 'left'
          elif(axis == 'y2'):
            if(self.ticksY2MinorRelativeLength == value):
              redraw = False
            self.ticksY2MinorRelativeLength = value
            item = 'left2'
          elif(axis == 'x'):
            if(self.ticksXMinorRelativeLength == value):
              redraw = False
            self.ticksXMinorRelativeLength = value
            item = 'top'
          elif(axis == 'xs'):
            if(self.slavedTicksXMinorRelativeLength == value):
              redraw = False
            self.slavedTicksXMinorRelativeLength = value
            item = 'xs'
        else: 
          if(axis == 'y'):
            if(self.ticksResidYMinorRelativeLength == value):
              redraw = False
            self.ticksResidYMinorRelativeLength = value
            item = 'left'
          elif(axis == 'y'):
            axis = 'abort'
          elif(axis == 'x'):
            if(self.ticksXMinorRelativeLength_resid == value):
              redraw = False
            self.ticksXMinorRelativeLength_resid = value
            item = 'top'
          elif(axis == 'xs'):
            if(self.slavedTicksXMinorRelativeLength_resid == value):
              redraw = False
            self.slavedTicksXMinorRelativeLength_resid = value
            item = 'xs'
            
        # update tick marks
        if(axis != 'xs'):
          self.setTickMarkLength(value=self.ticksLength[item], axis=item, redraw=redraw, target=target, forceRedraw=redraw)
        else:
          self.setTickMarkLength(value=value, axis=item, redraw=redraw, target=target, forceRedraw=redraw)
      
  def setTickMarkLength(self, value=1.0, axis='left', redraw=True, target='plot', forceRedraw=False):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(self.ticksLength[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis = 'y'; axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            self.ticksLength['left'] = value
            self.ticksLength['right'] = value
            ticksMinorRelativeLength = self.ticksYMinorRelativeLength
          elif (axis in ['left2', 'right2']):
            axis = 'y'
            self.ticksLength['left2'] = value
            self.ticksLength['right2'] = value
            ticksMinorRelativeLength = self.ticksY2MinorRelativeLength
          else:
            axis = 'x'
            self.ticksLength['top'] = value
            self.ticksLength['bottom'] = value
            ticksMinorRelativeLength = self.ticksXMinorRelativeLength
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
          if(self.ticksLength_resid[axis] == value):
            redraw = False
          # for time being, left/right and top/bottom are linked
          if (axis in ['left', 'right']):
            axis = 'y'
            self.ticksLength_resid['left'] = value
            self.ticksLength_resid['right'] = value
            ticksMinorRelativeLength = self.ticksResidYMinorRelativeLength
          elif (axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            axis = 'x'
            self.ticksLength_resid['top'] = value
            self.ticksLength_resid['bottom'] = value
            ticksMinorRelativeLength = self.ticksXMinorRelativeLength

        if(axis != 'abort'):
          axisobject.tick_params(axis=axis, length=value, which='major')
          axisobject.tick_params(axis=axis, length=value * ticksMinorRelativeLength, which='minor')
          axisobject2.tick_params(axis=axis, length=value, which='major')
          axisobject2.tick_params(axis=axis, length=value * ticksMinorRelativeLength, which='minor')
          if(self.splitY and (axis == 'x') and (target == 'plot')):
            self.ax.tick_params(axis=axis, length=value, which='major')
            self.ax.tick_params(axis=axis, length=value * ticksMinorRelativeLength, which='minor')
            self.ax_div.tick_params(axis=axis, length=value, which='major')
            self.ax_div.tick_params(axis=axis, length=value * ticksMinorRelativeLength, which='minor')
          # redraw?
          if(redraw or forceRedraw):
            plotobject.myRefresh()
      elif(axis in ['xs', 'slaved']):
        # note that 'xs' redirects from minor tick realtive length
        # by contrast, 'slaved' results from the UI call back
        # draw2ndAxisTicks() takes care of the nitty-gritty stuff
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if(axis == 'slaved'):
            if(self.ticksLength[axis] == value):
              redraw = False
            self.ticksLength[axis] = value
        else:
          plotobject = self.residplotwidget
          if(axis == 'slaved'):
            if(self.ticksLength_resid[axis] == value):
              redraw = False
            self.ticksLength_resid[axis] = value
        # redraw?
        if(redraw or forceRedraw):
          plotobject.myRefresh()

  def setAxisLabelColor(self, value=[0, 0, 0, 1], axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis == 'x'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if((self.labelXColor == value) or (not self.labelXShow)):
            redraw = False
          self.labelXColor = value
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
          if((self.labelXColor_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.labelXColor_resid = value

        handleAxis = axisobject.xaxis.label
        handleAxis.set_color(value)
        handleAxis = axisobject2.xaxis.label
        handleAxis.set_color(value)
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
          if((self.labelYColor == value) or (not self.labelYShow)):
            redraw = False
          self.labelYColor = value
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
          if((self.labelYColor_resid == value) or (not self.labelYShow_resid)):
            redraw = False
          self.labelYColor_resid = value

        handleAxis = axisobject.yaxis.label
        handleAxis.set_color(value)
        handleAxis = axisobject2.yaxis.label
        handleAxis.set_color(value)
      elif(axis == 'y2'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if((self.labelY2Color == value) or (not self.labelY2Show)):
            redraw = False
          self.labelY2Color = value
          handleAxis = axisobject.yaxis.label
          handleAxis.set_color(value)
          handleAxis = axisobject2.yaxis.label
          handleAxis.set_color(value)
        else:
          axis = 'abort'
      elif(axis == 'xs'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXColor == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXColor = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXColor_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXColor_resid = value

      # remember settings
      if(axis != 'abort'):
        # redraw?
        if(redraw):
          plotobject.myRefresh()

  def setAxisLabel(self, labeltext=None, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      # updates axis label
      if(labeltext == None):
        labeltext = axis

      if(axis == 'x'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; figobject = self.matplot; axisname = 'ax2'
          if((self.labelX == labeltext) or (not self.labelXShow)):
            redraw = False
          self.labelX = labeltext
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; figobject = self.residplot; axisname = 'ax_resid'
          if((self.labelX_resid == labeltext) or (not self.labelXShow_resid)):
            redraw = False
          self.labelX_resid = labeltext
        # manually process escape characters
        labeltext = labeltext.replace('\n', '\\n')
        labeltext = labeltext.replace('\t', '\\t')
        labeltext = '\n'.join([i for i in labeltext.split('\\n') if (len(i.strip()))])
        labeltext = '\t'.join([i for i in labeltext.split('\\t') if (len(i.strip()))])
        # check for potential Mathtext errors
        prevLabel = axisobject.xaxis.get_label_text()
        axisobject.xaxis.set_label_text(labeltext)
        try:
          axisobject.xaxis.label._get_layout(figobject.canvas.renderer)
        except:
          # revert to previous label
          self.parent.statusbar.showMessage('Cannot set axis label to ' + labeltext, self.parent.STATUS_TIME)
          
          axisobject.xaxis.set_label_text(prevLabel)
          labeltext = prevLabel
          redraw = False
      elif(axis == 'y'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax; figobject = self.matplot; axisname = 'ax'
          if((self.labelY == labeltext) or (not self.labelYShow)):
            redraw = False
          self.labelY = labeltext
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; figobject = self.residplot; axisname = 'ax_resid'
          if((self.labelY_resid == labeltext) or (not self.labelYShow_resid)):
            redraw = False
          self.labelY_resid = labeltext
        # manually process escape characters
        labeltext = labeltext.replace('\n', '\\n')
        labeltext = labeltext.replace('\t', '\\t')
        labeltext = '\n'.join([i for i in labeltext.split('\\n') if (len(i.strip()))])
        labeltext = '\t'.join([i for i in labeltext.split('\\t') if (len(i.strip()))])
        if((target == 'resid') and (labeltext != '')):
          labeltext = u'\N{GREEK CAPITAL LETTER DELTA}' + labeltext
        # check for potential Mathtext errors
        prevLabel = axisobject.yaxis.get_label_text()
        axisobject.yaxis.set_label_text(labeltext)
        try:
          axisobject.yaxis.label._get_layout(figobject.canvas.renderer)
        except:
          # revert to previous label
          self.parent.statusbar.showMessage('Cannot set axis label to ' + labeltext, self.parent.STATUS_TIME)
          axisobject.yaxis.set_label_text(prevLabel)
          labeltext = prevLabel
          redraw = False
      elif(axis == 'y2'):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; figobject = self.matplot; axisname = 'ax2'
          if((self.labelY2 == labeltext) or (not self.labelY2Show)):
            redraw = False
          self.labelY2 = labeltext
          # manually process escape characters
          labeltext = labeltext.replace('\n', '\\n')
          labeltext = labeltext.replace('\t', '\\t')
          labeltext = '\n'.join([i for i in labeltext.split('\\n') if (len(i.strip()))])
          labeltext = '\t'.join([i for i in labeltext.split('\\t') if (len(i.strip()))])
          # check for potential Mathtext errors
          prevLabel = axisobject.yaxis.get_label_text()
          axisobject.yaxis.set_label_text(labeltext)
          try:
            axisobject.yaxis.label._get_layout(figobject.canvas.renderer)
            axisobject2.yaxis.set_label_text(labeltext)
          except:
            # revert to previous label
            self.parent.statusbar.showMessage('Cannot set axis label to' + labeltext, self.parent.STATUS_TIME)
            axisobject.yaxis.set_label_text(prevLabel)
            axisobject2.yaxis.set_label_text(prevLabel)
            labeltext = prevLabel
            redraw = False
        else:
          axis = 'abort'
                        
      # remember settings
      if(axis != 'abort'):
        # capture errors with bad fonts (can occur when switching back from all-Mathtext label)
        try:
          if(redraw):
            plotobject.myRefresh()
        except:
          ###safeFont = 'DejaVu Sans'
          self.parent.statusbar.showMessage('Experiencing problems with font ' + self.axisFont[axis] + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
          self.axisFont[axis] = SAFE_FONT
          if(axis == 'x'):
            axisobject.xaxis.label.set_fontname(SAFE_FONT)
          else:
            axisobject.yaxis.label.set_fontname(SAFE_FONT)
            if(axis == 'y2'):
              axisobject2.yaxis.label.set_fontname(SAFE_FONT)
  
          if(redraw):
            plotobject.myRefresh()

  def setAxisVisibility(self, value=True, axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div
          if(self.axisVisible[axis] == value):
            redraw = False
          self.axisVisible[axis] = value
          if(axis in ['left', 'right']):
            axisname = 'ax'
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisVisible_resid[axis] == value):
              redraw = False
            self.axisVisible_resid[axis] = value

        # sets axis visibility
        if(axis != 'abort'):
          if(axis in axisobject.spines):
            axisobject.spines[axis].set_visible(value)
          if(axis in axisobject2.spines):
            axisobject2.spines[axis].set_visible(value)
  
          # need to solve inner situation
          if(axis in ['bottom', 'top']):
            self.updateInnerYSituation()
          else:
            self.updateInnerSituation()

          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisColor(self, value=True, axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div
          if(self.axisColor[axis] == value):
            redraw = False
          self.axisColor[axis] = value
          if(axis in ['left', 'right']):
            axisname = 'ax'
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisColor_resid[axis] == value):
              redraw = False
            self.axisColor_resid[axis] = value

        # set axis color
        if(axis != 'abort'):
          if(axis in axisobject.spines):
            axisobject.spines[axis].set_color(value)
          if(axis in axisobject2.spines):
            axisobject2.spines[axis].set_color(value)
          
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisWidth(self, value=1.0, axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div
          if(self.axisWidth[axis] == value):
            redraw = False
          self.axisWidth[axis] = value
          if(axis in ['left', 'right']):
            axisname = 'ax'
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisWidth_resid[axis] == value):
              redraw = False
            self.axisWidth_resid[axis] = value

        # check grid style to avoid crash at zero width
        if(value < .01):
          if(target == 'plot'):
            if(self.axisStyle[axis] != 'solid'):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              self.axisStyle[axis] = 'solid'
          else:
            if(self.axisStyle_resid[axis] != 'solid'):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              self.axisStyle_resid[axis] = 'solid'

          if(axis in axisobject.spines):
            axisobject.spines[axis].set_linestyle('solid')
          if(axis in axisobject2.spines):
            axisobject2.spines[axis].set_linestyle('solid')

        # updates axis width
        if(axis != 'abort'):
          if(axis in axisobject.spines):
            axisobject.spines[axis].set_linewidth(value)
          if(axis in axisobject2.spines):
            axisobject2.spines[axis].set_linewidth(value)
  
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisFont(self, value=None, axis='x', redraw=True, target='plot'):
    # check whether font exists
    ###safeFont = 'DejaVu Sans'
    if(not (value in self.parent.fontNames)):
      value = SAFE_FONT

    if(value in self.parent.fontNames):
      # check whether to operate on data or resid plot
      if(target in ['plot', 'resid']):
        if(axis in ['x', 'y', 'y2']):
          if(target == 'plot'):
            prevFont = self.axisFont[axis]
            plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
            if(axis == 'y'):
              axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if(self.axisFont[axis] == value):
              redraw = False
            self.axisFont[axis] = value
          else:
            if(axis == 'y2'):
              axis = 'abort'
            else:
              prevFont = self.axisFont_resid[axis]
              plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
              if(self.axisFont_resid[axis] == value):
                redraw = False
              self.axisFont_resid[axis] = value

          if(axis != 'abort'):
            useFont = value
            # try setting font value
            if(axis == 'x'):
              for useObject in [axisobject, axisobject2]:
                useObject.xaxis.label.set_fontname(useFont)
                _, _, descent = useObject.xaxis.label._get_layout(self.matplot.canvas.renderer)
                if(not (descent > 0)):
                  self.parent.statusbar.showMessage('Experiencing problems setting font ' + useFont + ' -- reverting to ' + prevFont, self.parent.STATUS_TIME)
                  useFont = prevFont
                  useObject.xaxis.label.set_fontname(useFont)
                  _, _, descent =useObject.xaxis.label._get_layout(self.matplot.canvas.renderer)
                  if(not (descent > 0)):
                    self.parent.statusbar.showMessage('Also experiencing problems setting font ' + useFont + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                    useFont = SAFE_FONT
                    useObject.xaxis.label.set_fontname(useFont)
            else:
              for useObject in [axisobject, axisobject2]:
                useObject.yaxis.label.set_fontname(useFont)
                _, _, descent = useObject.yaxis.label._get_layout(self.matplot.canvas.renderer)
                if(not (descent > 0)):
                  self.parent.statusbar.showMessage('Experiencing problems setting font ' + useFont + ' -- reverting to ' + prevFont, self.parent.STATUS_TIME)
                  useFont = prevFont
                  useObject.yaxis.label.set_fontname(useFont)
                  _, _, descent =useObject.yaxis.label._get_layout(self.matplot.canvas.renderer)
                  if(not (descent > 0)):
                    self.parent.statusbar.showMessage('Also experiencing problems setting font ' + useFont + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                    useFont = SAFE_FONT
                    useObject.yaxis.label.set_fontname(useFont)
  
            # save actual font value
            if(target == 'plot'):
              self.axisFont[axis] = useFont
            else:
              self.axisFont_resid[axis] = useFont
            
            # have to capture errors in case a strange font is set
            # retain this as 2nd-layer contro
            try:
              if(redraw):
                plotobject.myRefresh()
            except:
              self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + prevFont, self.parent.STATUS_TIME)
              
              # revert to previous font
              value = prevFont
              if(target == 'plot'):
                self.axisFont[axis] = prevFont
              else:
                self.axisFont_resid[axis] = prevFont
  
              if(axis == 'x'):
                axisobject.xaxis.label.set_fontname(prevFont)
                axisobject2.xaxis.label.set_fontname(prevFont)
              else:
                axisobject.yaxis.label.set_fontname(prevFont)
                axisobject2.yaxis.label.set_fontname(prevFont)
  
              # also capture errors with previous font (can happen if selecting two bad fonts in a row)
              try:
                if(redraw):
                  plotobject.myRefresh()
              except:
                self.parent.statusbar.showMessage('Also experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
  
                # revert to previous font
                value = SAFE_FONT
                if(target == 'plot'):
                  self.axisFont[axis] = SAFE_FONT
                else:
                  self.axisFont_resid[axis] = SAFE_FONT
    
                if(axis == 'x'):
                  axisobject.xaxis.label.set_fontname(SAFE_FONT)
                  axisobject2.xaxis.label.set_fontname(SAFE_FONT)
                else:
                  axisobject.yaxis.label.set_fontname(SAFE_FONT)
                  axisobject2.yaxis.label.set_fontname(SAFE_FONT)
        elif(axis == 'xs'):
          # ideally, we just rely on draw2ndXAxisTicks() to do the right thing here
          # but we should issue a redraw as we skip the above
          if(target == 'plot'):
            plotobject = self.dataplotwidget
            if(self.axisFont[axis] == value):
              redraw = False
            self.axisFont[axis] = value
          else:
            if(self.axisFont_resid[axis] == value):
              redraw = False
            self.axisFont_resid[axis] = value
            plotobject = self.residplotwidget

          # have to capture errors in case a strange font is set
          # retain this as 2nd-layer control
          try:
            if(redraw):
              plotobject.myRefresh()
          except:
            self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
            self.axisFont['xs'] = SAFE_FONT
            self.axisFont_resid['xs'] = SAFE_FONT
            plotobject.myRefresh()
  
  def setAxisLabelAlignment(self, value=None, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'

        if(axis == 'x'):
          axisobject.xaxis.label.set_horizontalalignment(value)
          axisobject2.xaxis.label.set_horizontalalignment(value)
          direction = 'horizontal'
          if(target == 'plot'):
            if((self.labelXAlignment == value) or (not self.labelXShow)):
              redraw = False
            self.labelXAlignment = value
          else:
            if((self.labelXAlignment_resid == value) or (not self.labelXShow_resid)):
              redraw = False
            self.labelXAlignment_resid = value
        elif(axis == 'y'):
          if(target == 'plot'):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if((self.labelYAlignment == value) or (not self.labelYShow)):
              redraw = False
            self.labelYAlignment = value
          else:
            if((self.labelYAlignment_resid == value) or (not self.labelYShow_resid)):
              redraw = False
            self.labelYAlignment_resid = value
          axisobject.yaxis.label.set_horizontalalignment(value)
          axisobject2.yaxis.label.set_horizontalalignment(value)
          direction = 'horizontal'
        elif(axis == 'y2'):
          if(target == 'plot'):
            axisobject.yaxis.label.set_horizontalalignment(value)
            axisobject2.yaxis.label.set_horizontalalignment(value)
            direction = 'horizontal'
            if((self.labelY2Alignment == value) or (not self.labelYShow_resid)):
              redraw = False
            self.labelY2Alignment = value
          else:
            axis = 'abort'
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXAlignment == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXAlignment = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXAlignment_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXAlignment_resid = value

        # remember settings
        if(axis != 'abort'):
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisLabelAlignmentVertical(self, value=None, axis='x', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
        else:
          plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'

        if(axis == 'x'):
          if(value != 'center'):
            axisobject.xaxis.label.set_verticalalignment(value)
            axisobject2.xaxis.label.set_verticalalignment(value)
          else:
            try:
              axisobject.xaxis.label.set_verticalalignment('center_baseline')
              axisobject2.xaxis.label.set_verticalalignment('center_baseline')
            except:
              axisobject.xaxis.label.set_verticalalignment('center')
              axisobject2.xaxis.label.set_verticalalignment('center')
          direction = 'vertical'
          if(target == 'plot'):
            if((self.labelXAlignmentVertical == value) or (not self.labelXShow)):
              redraw = False
            self.labelXAlignmentVertical = value
          else:
            if((self.labelXAlignmentVertical_resid == value) or (not self.labelXShow_resid)):
              redraw = False
            self.labelXAlignmentVertical_resid = value
        elif(axis == 'y'):
          if(target == 'plot'):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
            if((self.labelYAlignmentVertical == value) or (not self.labelYShow)):
              redraw = False
            self.labelYAlignmentVertical = value
          else:
            if((self.labelYAlignmentVertical_resid == value) or (not self.labelYShow_resid)):
              redraw = False
            self.labelYAlignmentVertical_resid = value
          if(value != 'center'):
            axisobject.yaxis.label.set_verticalalignment(value)
            axisobject2.yaxis.label.set_verticalalignment(value)
          else:
            try:
              axisobject.yaxis.label.set_verticalalignment('center_baseline')
              axisobject2.yaxis.label.set_verticalalignment('center_baseline')
            except:
              axisobject.yaxis.label.set_verticalalignment('center')
              axisobject2.yaxis.label.set_verticalalignment('center')
          direction = 'vertical'
        elif(axis == 'y2'):
          if(target == 'plot'):
            if(value != 'center'):
              axisobject.yaxis.label.set_verticalalignment(value)
              axisobject2.yaxis.label.set_verticalalignment(value)
            else:
              try:
                axisobject.yaxis.label.set_verticalalignment('center_baseline')
                axisobject2.yaxis.label.set_verticalalignment('center_baseline')
              except:
                axisobject.yaxis.label.set_verticalalignment('center')
                axisobject2.yaxis.label.set_verticalalignment('center')
            direction = 'vertical'
            if((self.labelY2AlignmentVertical == value) or (not self.labelY2Show)):
              redraw = False
            self.labelY2AlignmentVertical = value
          else:
            axis = 'abort'
      elif(axis in ['xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget
          if((self.slavedLabelXAlignmentVertical == value) or (not self.labelXShow)):
            redraw = False
          self.slavedLabelXAlignmentVertical = value
        else:
          plotobject = self.residplotwidget
          if((self.slavedLabelXAlignmentVertical_resid == value) or (not self.labelXShow_resid)):
            redraw = False
          self.slavedLabelXAlignmentVertical_resid = value

        # remember settings
        if(axis != 'abort'):
          # redraw?
          if(redraw):
            plotobject.myRefresh()
  
  def setTickFont(self, value=None, axis='x', redraw=True, target='plot'):
    # check whether font exists
    ###safeFont = 'DejaVu Sans'
    if(not (value in self.parent.fontNames)):
      value = SAFE_FONT
    if(value in self.parent.fontNames):
      # check whether to operate on data or resid plot
      if(target in ['plot', 'resid']):
        if(axis in ['x', 'y', 'y2', 'xs']):
          if(target == 'plot'):
            prevFont = self.tickFont[axis]
            plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
            if(axis == 'y'):
              axisobject = self.ax; axisname = 'ax'
            if(self.tickFont[axis] == value):
              redraw = False
            self.tickFont[axis] = value
          else:
            prevFont = self.tickFont_resid[axis]
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.tickFont_resid[axis] == value):
              redraw = False
            self.tickFont_resid[axis] = value
        
          # updates tick font
          if(axis == 'x'):
            tickLabels = axisobject.get_xticklabels(which='both')
            tickLabels.append(axisobject.xaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_xticklabels(which='both'))
            tickLabels.append(axisobject2.xaxis.get_offset_text())
          elif(axis == 'y'):
            tickLabels = axisobject.get_yticklabels(which='both')
            tickLabels.append(axisobject.yaxis.get_offset_text())
            tickLabels.extend(axisobject2.get_yticklabels(which='both'))
            tickLabels.append(axisobject2.yaxis.get_offset_text())
          elif(axis == 'y2'):
            if(target == 'plot'):
              tickLabels = axisobject.get_yticklabels(which='both')
              tickLabels.append(axisobject.yaxis.get_offset_text())
              tickLabels.extend(axisobject2.get_yticklabels(which='both'))
              tickLabels.append(axisobject2.yaxis.get_offset_text())
            else:
              axis = 'abort'
          elif(axis == 'xs'):
            # ideally, we just rely on draw2ndXAxisTicks() to do the right thing here
            axis = 'abort'
            # but we should issue a redraw as we skip the below
            # have to capture errors in case a strange font is set
            # retain this as 2nd-layer control
            try:
              if(redraw):
                plotobject.myRefresh()
            except:
              self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
              self.tickFont['xs'] = SAFE_FONT
              self.tickFont_resid['xs'] = SAFE_FONT
              plotobject.myRefresh()
          
          if(axis != 'abort'):
            useFont = value
            # try setting font value
            for entry in tickLabels:
              entry.set_fontname(useFont)
              _, _, descent = entry._get_layout(self.matplot.canvas.renderer)
              if(not (descent > 0)):
                self.parent.statusbar.showMessage('Experiencing problems setting font ' + useFont + ' -- reverting to ' + prevFont, self.parent.STATUS_TIME)
                useFont = prevFont
                entry.set_fontname(useFont)
                _, _, descent = entry._get_layout(self.matplot.canvas.renderer)
                if(not (descent > 0)):
                  self.parent.statusbar.showMessage('Also experiencing problems setting font ' + useFont + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                  useFont = SAFE_FONT
                  entry.set_fontname(useFont)
  
            # save actual font value
            if(target == 'plot'):
              self.tickFont[axis] = useFont
            else:
              self.tickFont_resid[axis] = useFont
            
            # have to capture errors in case a strange font is set
            # retain this as 2nd-layer control
            try:
              if(redraw):
                plotobject.myRefresh()
            except:
              self.parent.statusbar.showMessage('Experiencing problems setting font ' + useFont + ' -- reverting to ' + prevFont, self.parent.STATUS_TIME)
              
              # revert to previous font
              useFont = prevFont
              if(target == 'plot'):
                self.tickFont[axis] = prevFont
              else:
                self.tickFont_resid[axis] = prevFont
                
              for entry in tickLabels:
                entry.set_fontname(prevFont)
  
              # also capture errors with previous font (can happen if selecting two bad fonts in a row)
              try:
                if(redraw):
                  plotobject.myRefresh()
              except:
                self.parent.statusbar.showMessage('Also experiencing problems setting font ' + useFont + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
  
                # revert to previous font
                value = SAFE_FONT
                if(target == 'plot'):
                  self.tickFont[axis] = SAFE_FONT
                else:
                  self.tickFont_resid[axis] = SAFE_FONT
  
                for entry in tickLabels:
                  entry.set_fontname(SAFE_FONT)
  
  def setMinorTick(self, value=2, axis='x', redraw=True, target='plot'):
    # changes number of minor tick marks (on linear axes)
    if(target in ['plot', 'resid']):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = [self.ax, self.ax2]; axisname = 'ax2'
          if(axis == 'x'):
            if(self.ticksXMinor == value):
              redraw = False
            self.ticksXMinor = value
          elif(axis == 'x2'):
            axisobject = [self.ax_div, self.ax2_div]
            if(self.ticksXMinor_div == value):
              redraw = False
            self.ticksXMinor_div = value
          elif(axis == 'xs'):
            if(self.slavedTicksXMinor == value):
              redraw = False
            self.slavedTicksXMinor = value
          elif(axis == 'y'):
            axisname = 'ax'
            axisobject = [self.ax, self.ax_div]
            if(self.ticksYMinor == value):
              redraw = False
            self.ticksYMinor = value
          else:
            axisobject = [self.ax2, self.ax2_div]
            if(self.ticksY2Minor == value):
              redraw = False
            self.ticksY2Minor = value
        else:
          plotobject = self.residplotwidget; axisobject = [self.ax_resid]; axisname = 'ax_resid'
          if(axis == 'y'):
            if(self.ticksResidYMinor == value):
              redraw = False
            self.ticksResidYMinor = value
          elif(axis == 'x2'):
            axisobject = [self.ax_resid_div]
            
        # remember settings
        if(axis != 'xs'):
          minorAutoticks = MyAutoMinorLocator(value)
          if(axis in ['x', 'x2']):
            for useAxis in axisobject:
              useAxis.xaxis.set_minor_locator(minorAutoticks)
              useAxis.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
          else:
            for useAxis in axisobject:
              useAxis.yaxis.set_minor_locator(minorAutoticks)
              useAxis.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
          # redraw minor grid lines
          self.drawMinorAxisGrid(axis=axis, target=target, redraw=False)

        # redraw?
        if(axis != 'abort') :
          if(redraw):
            plotobject.myRefresh()

  def setAxisStyle(self, value='solid', axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
          if(self.axisStyle[axis] == value):
            redraw = False
          self.axisStyle[axis] = value
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisStyle_resid[axis] == value):
              redraw = False
            self.axisStyle_resid[axis] = value

        if(axis != 'abort'):
          # check axis style to avoid crash at zero width
          if(value != 'solid'):
            if(self.axisWidth[axis] < .01):
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              value = 'solid'
              if(target == 'plot'):
                self.axisStyle[axis] = 'solid'
              else:
                self.axisStyle_resid[axis] = 'solid'
  
          if(axis in axisobject.spines):
            axisobject.spines[axis].set_linestyle(value)
          if(axis in axisobject2.spines):
            axisobject2.spines[axis].set_linestyle(value)
  
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisDashStyle(self, value='solid', axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
          if(self.axisDashStyle[axis] == value):
            redraw = False
          self.axisDashStyle[axis] = value
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisDashStyle_resid[axis] == value):
              redraw = False
            self.axisDashStyle_resid[axis] = value

        if(axis != 'abort'):
          if(axis in axisobject.spines):
            axisobject.spines[axis].set_capstyle(value)
          if(axis in axisobject2.spines):
            axisobject2.spines[axis].set_capstyle(value)
  
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def resetAxisPosition(self, axis='left', redraw=True):
    # resets axis position to original values
    originalPosition = {'left':('axes', 0), 'right':('axes', 1.0), 'bottom':('axes', 0), 'top':('axes', 1.0), 'left2':('axes', 0), 'right2':('axes', 1.0)}
    if(axis in originalPosition):
      # check whether to do anything
      if((originalPosition[axis][0] != self.axisPosition[axis]) or (originalPosition[axis][1] != self.axisPositionValue[axis])):
        for target in ['plot', 'resid']:
          self.setAxisPosition(value=originalPosition[axis][0], axis=axis, redraw=False, target=target)
          self.setAxisPositionValue(value=originalPosition[axis][1], axis=axis, redraw=False, target=target)
        # refresh plots
        if(redraw):
          self.dataplotwidget.myRefresh()
          self.residplotwidget.myRefresh()

  def setAxisBoundaryValue(self, value=0.0, lower=True, axis='left', redraw=True, target='plot'):
    # changes limits on the axes
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(lower):
          index = 0
        else:
          index = 1
        if(target == 'plot'):
          if(self.axisBoundary[axis][index] == value):
            redraw = False
          self.axisBoundary[axis][index] = value
        else:
          if(self.axisBoundary_resid[axis][index] == value):
            redraw = False
          self.axisBoundary_resid[axis][index] = value
        
        self.setAxisBoundaryHelper(axis=axis, redraw=redraw, target=target)

  def setAxisBoundary(self, value=False, axis='left', redraw=True, target='plot'):
    # sets limits on the axes
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        if(target == 'plot'):
          if(self.axisBoundaryCheck[axis] == value):
            redraw = False
          self.axisBoundaryCheck[axis] = value
        else:
          if(self.axisBoundaryCheck_resid[axis] == value):
            redraw = False
          self.axisBoundaryCheck_resid[axis] = value
        
        self.setAxisBoundaryHelper(axis=axis, redraw=redraw, target=target)

  def setAxisBoundaryHelper(self, axis='left', redraw=True, target='plot'):
    # helper function for setting axis boundaries
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        secondAxes = False
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
            secondAxes = True
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div
        
        if(axis != 'abort'):
          if(secondAxes):
            useAxis = axis + '2'
          else:
            useAxis = axis
          if(self.axisBoundaryCheck[useAxis]):
            lower, upper = self.axisBoundary[useAxis]
            for useobject in [axisobject, axisobject2]:
              if(axis in useobject.spines):
                useobject.spines[axis].set_bounds(lower, upper)
          else:
            for useobject in [axisobject, axisobject2]:
              if(axis in useobject.spines):
                useobject.spines[axis]._bounds = None

          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisPosition(self, value='axes', axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        secondAxes = False
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div; axisname = 'ax'
          if(self.axisPosition[axis] == value):
            redraw = False
          self.axisPosition[axis] = value
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
            secondAxes = True
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisPosition_resid[axis] == value):
              redraw = False
            self.axisPosition_resid[axis] = value

        if(axis != 'abort'):
          if(axis in axisobject.spines):
            value = self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=axisobject, target=target, secondAxes=secondAxes)
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=axisobject2, target=target, secondAxes=secondAxes, splitX=True)
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisPositionValue(self, value=1.0, axis='left', redraw=True, target='plot'):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      if(axis in ['left', 'right', 'bottom', 'top', 'left2', 'right2']):
        secondAxes = False
        if(target == 'plot'):
          plotobject = self.dataplotwidget; axisobject = self.ax2; axisobject2 = self.ax2_div; axisname = 'ax2'
          if(axis in ['left', 'right']):
            axisobject = self.ax; axisobject2 = self.ax_div
          if(self.axisPositionValue[axis] == value):
            redraw = False
          self.axisPositionValue[axis] = value
          if(axis in ['left', 'right']):
            axisname = 'ax'
          if(axis in ['left2', 'right2']):
            axis = axis[:-1]
            secondAxes = True
        else:
          if(axis in ['left2', 'right2']):
            axis = 'abort'
          else:
            plotobject = self.residplotwidget; axisobject = self.ax_resid; axisobject2 = self.ax_resid_div; axisname = 'ax_resid'
            if(self.axisPositionValue_resid[axis] == value):
              redraw = False
            self.axisPositionValue_resid[axis] = value

        # updates axis width
        if(axis != 'abort'):
          if(axis in axisobject.spines):
            value = self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=axisobject, target=target, secondAxes=secondAxes)
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=axisobject2, target=target, secondAxes=secondAxes, splitX=True)
          # redraw?
          if(redraw):
            plotobject.myRefresh()

  def setAxisPositionHelper(self, axis, plotobject, axisobject, target, secondAxes=False, splitX=False, splitY=False):
    # called by setAxisPosition
    # duh, this is ugly!
    # the following call breaks the connection b/w spine and axis and thus disables detrimental updates to ticks and labels
    saveAxis = axisobject.spines[axis].axis
    axisobject.spines[axis].axis = None
    # assign useModeX and transAxisobject1/2
    if(splitX):
      useModeX, useMinX, transAxisobject1, transAxisobject2  = self.modeX_div, self.minX_div, self.ax_div, self.ax2_div
      ##useModeX, useMinX, transAxisobject1, transAxisobject2  = self.modeX_div, self.minX_div, self.ax, self.ax2
      transAxisobjectResid = self.ax_resid_div
    else:
      useModeX, useMinX, transAxisobject1, transAxisobject2 = self.modeX, self.minX, self.ax, self.ax2
      transAxisobjectResid = self.ax_resid
    #
    if(secondAxes):
      if((self.axisPosition[axis + '2'] == 'data') and (useModeX == 'log')):
        # matplotlib somehow screws up for log data (a bug I discovered!) => map to plot axes
        try:
          # have to do this b/c matplotlib.scale on Linux is buggy
          axis_to_data = transAxisobject1.transAxes + transAxisobject1.transData.inverted()
          point_on_data = (self.axisPositionValue[axis + '2'], self.minY)
          point_on_axes = axis_to_data.inverted().transform(point_on_data)
          assembledValue = ('axes', point_on_axes[0])
        except:
          assembledValue = ('outward', 0.0)
      else:
        assembledValue = (self.axisPosition[axis + '2'], self.axisPositionValue[axis + '2'])
    elif(target == 'plot'):
      if(axis in ['top', 'bottom']):
        # argh -- top and bottom are on self.ax2, meaning they are linked to the second axes
        # => we hence need to convert data values from one axis to the other
        if(self.axisPosition[axis] == 'data'):
          if(self.splitY):
            assembledValue = ('data', self.axisPositionValue[axis])
          else:
            try:
              # have to do this b/c matplotlib.scale on Linux is buggy
              axis_to_data1 = transAxisobject1.transAxes + transAxisobject1.transData.inverted()
              axis_to_data2 = transAxisobject2.transAxes + transAxisobject2.transData.inverted()
              point_on_data1 = (useMinX, self.axisPositionValue[axis])
              # map to axes
              point_on_axes = axis_to_data1.inverted().transform(point_on_data1)
              assembledValue = ('axes', point_on_axes[1])
            except:
              assembledValue = ('outward', 0.0)
        else:
          assembledValue = (self.axisPosition[axis], self.axisPositionValue[axis])
      else:
        if((self.axisPosition[axis] == 'data') and (useModeX == 'log')):
          # matplotlib somehow screws up for log data (a bug I discovered!) => map to plot axes
          try:
            # have to do this b/c matplotlib.scale on Linux is buggy
            axis_to_data2 = transAxisobject2.transAxes + transAxisobject2.transData.inverted()
            point_on_data2 = (self.axisPositionValue[axis], self.minY2)
            point_on_axes = axis_to_data2.inverted().transform(point_on_data2)
            assembledValue = ('axes', point_on_axes[0])
          except:
            assembledValue = ('outward', 0.0)
        else:
          assembledValue = (self.axisPosition[axis], self.axisPositionValue[axis])
    else:
      # the resid plot
      if(axis in ['left', 'right']):
        if((self.axisPosition[axis] == 'data') and (useModeX == 'log')):
          # deal with matplotlib error => map to plot axes
          try:
            # have to do this b/c matplotlib.scale on Linux is buggy
            axis_to_data = transAxisobjectResid.transAxes + transAxisobjectResid.transData.inverted()
            point_on_data = (self.axisPositionValue[axis], self.minResidY)
            point_on_axes = axis_to_data.inverted().transform(point_on_data)
            assembledValue = ('axes', point_on_axes[0])
          except:
            assembledValue = ('outward', 0.0)
        else:
          assembledValue = (self.axisPosition[axis], self.axisPositionValue[axis])
      else:
        assembledValue = (self.axisPosition[axis], self.axisPositionValue[axis])
      
    # set the new axis mode and value
    axisobject.spines[axis].set_position(assembledValue)

    # for good measure restore axis setting
    axisobject.spines[axis].axis = saveAxis
    
    # now we have to manually update the position of the tick labels and ticks
    if(axis in ['left', 'right']):
      ticks = axisobject.yaxis.get_major_ticks()
      ticks.extend(axisobject.yaxis.get_minor_ticks())
      if(axis == 'left'):
        useTransform = axisobject.get_yaxis_transform(which='tick1')
        tick1 = True
      else:
        useTransform = axisobject.get_yaxis_transform(which='tick2')
        tick1 = False
    else:
      ticks = axisobject.xaxis.get_major_ticks()
      ticks.extend(axisobject.xaxis.get_minor_ticks())
      if(axis == 'bottom'):
        useTransform = axisobject.get_xaxis_transform(which='tick1')
        tick1 = True
      else:
        useTransform = axisobject.get_xaxis_transform(which='tick2')
        tick1 = False

    horizontal = True        
    if(target == 'plot'):
      plotobject = self.matplot
      if(axis in ['left', 'right']):
        usePad = self.ticksYPad2
        horizontal = False
        if(secondAxes):
          usePad = self.ticksY2Pad2
      elif(axis in ['bottom', 'top']):
        usePad = self.ticksXPad2
    else:
      plotobject = self.residplot
      if(axis in ['left', 'right']):
        usePad = self.ticksYPad2_resid
        horizontal = False
      else:
        usePad = self.ticksXPad2_resid

    for entry in ticks:
      if(tick1):
        entry.tick1line.set_transform(useTransform)
        trans, vert, horiz = entry._get_text1_transform()
        # also have to apply pad2 padding - phew!
        if(horizontal):
          offset = matplotlib.transforms.ScaledTranslation(usePad / 72.0, 0.0, plotobject.dpi_scale_trans)
        else:
          offset = matplotlib.transforms.ScaledTranslation(0.0, usePad / 72.0, plotobject.dpi_scale_trans)
        trans += offset
        entry.label1.set_transform(trans)
      else:
        entry.tick2line.set_transform(useTransform)
        trans, vert, horiz = entry._get_text2_transform()
        # also have to apply pad2 padding - phew!
        if(horizontal):
          offset = matplotlib.transforms.ScaledTranslation(usePad / 72.0, 0, plotobject.dpi_scale_trans)
        else:
          offset = matplotlib.transforms.ScaledTranslation(0, usePad / 72.0, plotobject.dpi_scale_trans)
        trans += offset
        entry.label2.set_transform(trans)

    return repr(assembledValue)

  def setAutoScale(self, axis='x'):
    # updates autoscale options for axes
    if(axis in ['x', 'y', 'y2', 'x2']):
      if(axis == 'x'):
        state = self.autoScaleCheckX.isChecked()
        self.autoScaleX = state
      elif(axis == 'x2'):
        state = self.xSplitAutoScaleCheckX.isChecked()
        self.autoScaleX_div = state
      elif(axis == 'y'):
        state = self.autoScaleCheckY.isChecked()
        self.autoScaleY = state
      else:
        state = self.secondAutoScaleCheckY.isChecked()
        self.autoScaleY2 = state
        
      # rescale contents on setting auto to True
      if(state):
        self.doAutoScale(axis=axis, redraw=True)
        
  def doAutoScale(self, axis='x', redraw=True):
    # performs autoscale on axis
    if(axis in ['x', 'y', 'y2', 'x2', 'resid']):
      currData, currRoles = self.parent.data[self.parent.activeData].getData_n_Fit()
      # we have some data that we could zoom to
      if(axis in ['x', 'x2']):
        if(axis == 'x'):
          useModeX, splitx = self.modeX, False
        else:
          useModeX, splitx = self.modeX_div, True
          
        # consider all data sets
        xval = np.array([])
        for dataset in self.parent.data:
          if(((dataset == self.parent.data[self.parent.activeData]) or (dataset.visibility)) and (dataset.onBothAxes or (dataset.onSecondAxes == splitx))):
            values, roles = dataset.getData_n_Fit()
            if('x' in roles):
              datax = list(values[:, roles.index('x')])
              if('xerr' in roles):
                dataxerr = list(values[:, roles.index('xerr')])
                xval = np.hstack((xval, [i - j for i, j in zip(datax, dataxerr)]))
                xval = np.hstack((xval, [i + j for i, j in zip(datax, dataxerr)]))
              else:
                xval = np.hstack((xval, datax))
        xval = np.array([i for i in xval if ((not np.isnan(i)) and (not np.isinf(i)))])

        if(len(xval)):
          temp_xmin = np.min(xval)
          temp_xmax = np.max(xval)

          # check for split x axis
          if(self.splitShow):
            if(axis == 'x'):
              temp_xmax = np.min((temp_xmax, self.maxX_div, self.minX_div))
            else:
              temp_xmin = np.max((temp_xmin, self.maxX, self.minX))
            
          # ensure minimum limit
          if(temp_xmax - temp_xmin < self.EPSILON):
            temp_xmax += self.EPSILON; temp_xmin -= self.EPSILON
          elif(self.data_spacer > 0):
            if(useModeX == 'linear'):
              difference = temp_xmax - temp_xmin
              if((not self.splitShow) or (axis == 'x2')):
                temp_xmax += difference * self.data_spacer
              if((not self.splitShow) or (axis == 'x')):
                temp_xmin -= difference * self.data_spacer
            else:
              # log scale -- isolate positive data
              pos_x = np.array(xval)
              pos_x = pos_x[pos_x > 0]
              pos_x = np.array([i for i in pos_x if ((not np.isnan(i)) and (not np.isinf(i)))])
              if(len(pos_x > 1)):
                # recalc. xmin to address error when restoring state
                temp_xmin, temp_xmax = np.min(pos_x), np.max(pos_x)
                if(self.splitShow and (axis == 'x2') and (np.max((self.minX, self.maxX)) > 0)):
                  posLimits = [i for i  in [self.minX, self.maxX] if (i > 0)]
                  temp_xmin = np.max((temp_xmin, np.max(posLimits)))
                if(self.splitShow and (axis == 'x') and (np.max((self.minX_div, self.maxX_div)) > 0)):
                  posLimits = [i for i  in [self.minX_div, self.maxX_div] if (i > 0)]
                  temp_xmax = np.min((temp_xmax, np.min(posLimits)))
                difference = np.log(pos_x[-1] / pos_x[0])
                if((not self.splitShow) or (axis == 'x')):
                  temp_xmin = np.exp(np.log(temp_xmin) - self.data_spacer * difference)
                if((not self.splitShow) or (axis == 'x2')):
                  temp_xmax = np.exp(np.log(temp_xmax) + self.data_spacer * difference)
          
          if(axis == 'x'):
            self.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x', updateLabel=True, target='plot', redraw=False, updateGrid=True)
            self.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x', updateLabel=True, target='resid', redraw=False, updateGrid=True)
            # plot current function over new x-range
            self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
              fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot,\
              handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False, doAutoZoom=False)
            # copy in case split axes are shown
            curve = self.parent.fit[self.parent.activeFit]
            if(self.parent.plotArea.splitY and curve.onBothAxes):
              curve.duplicateForSplit()
            if(redraw):
              self.parent.plotArea.dataplotwidget.myRefresh()
            # plot zeroResid line over new x-range
            self.parent.plotArea.handleResidZero = self.parent.plotArea.plotResidZero(\
              handleResidZero=self.parent.plotArea.handleResidZero, redraw=redraw)
          else:
            self.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x2', updateLabel=True, target='plot', redraw=False, updateGrid=True)
            self.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x2', updateLabel=True, target='resid', redraw=False, updateGrid=True)
            # plot current function over new x-range
            self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
              fitobject=self.parent.fit[self.parent.activeFit], x=[], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div,\
              handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True, doAutoZoom=False)
            # copy in case split axes are shown
            curve = self.parent.fit[self.parent.activeFit]
            if(self.parent.plotArea.splitY and curve.onBothAxes):
              curve.duplicateForSplit()
            if(redraw):
              self.parent.plotArea.dataplotwidget.myRefresh()
            # plot zeroResid line over new x-range
            self.parent.plotArea.handleResidZero_div = self.parent.plotArea.plotResidZero(\
              handleResidZero=self.parent.plotArea.handleResidZero_div, redraw=redraw, splitX=True)
          # update tick entry fields
          self.updateTickEntryField(axis=axis)
      elif(axis in ['y', 'y2']):
        # only consider active function and data if on current y axis
        if(axis == 'y'):
          if(self.splitShow):
            funcY = np.hstack((self.y, self.y_div))
          else:
            funcY = self.y
        else:
          if(self.splitShow):
            funcY = np.hstack((self.y2, self.y2_div))
          else:
            funcY = self.y2
        if(self.splitY and self.parent.fit[self.parent.activeFit].onBothAxes):
          if(self.splitShow):
            funcY = np.hstack((self.y, self.y_div, self.y2, self.y2_div))
          else:
            funcY = np.hstack((self.y, self.y2))
        # check whether curve is visible at all
        if(not self.parent.fit[self.parent.activeFit].visibility):
          funcY = []

        # consider all data sets
        temp_y = np.array([])
        for dataset in self.parent.data:
          if(dataset.visibility and (dataset.onBothAxes or (dataset.onSecondAxes == (axis == 'y2')))):
            values, roles = dataset.getData_n_Fit()
            if('y' in roles):
              datay = list(values[:, roles.index('y')])
              if('yerr' in roles):
                datayerr = list(values[:, roles.index('yerr')])
                temp_y = np.hstack((temp_y, [i - j for i, j in zip(datay, datayerr)]))
                temp_y = np.hstack((temp_y, [i + j for i, j in zip(datay, datayerr)]))
              else:
                temp_y = np.hstack((temp_y, datay))
        # check whether any data present/visible
        if(not len(temp_y)):
          temp_y = funcY
          
        temp_y = np.array([i for i in temp_y if ((not np.isnan(i)) and (not np.isinf(i)))])
        # ensure that temp_y is at least one element long
        if(temp_y.size):
          temp_ymin = np.min(temp_y)
          temp_ymax = np.max(temp_y)
          
          # check for split y axis
          if(self.splitY):
            if(axis == 'y'):
              temp_ymin = np.max((temp_ymin, self.maxY2, self.minY2))
            else:
              temp_ymax = np.min((temp_ymax, self.maxY, self.minY))
  
          # ensure minimum limit
          if (temp_ymax - temp_ymin < self.EPSILON):
            temp_ymax += self.EPSILON; temp_ymin -= self.EPSILON
          elif(self.data_spacer > 0):
            if(axis == 'y'):
              useMode = self.modeY
            else:
              useMode = self.modeY2
            if(useMode == 'linear'):
              difference = temp_ymax - temp_ymin
              if((not self.splitY) or (axis == 'y')):
                temp_ymax += difference * self.data_spacer
              if((not self.splitY) or (axis == 'y2')):
                temp_ymin -= difference * self.data_spacer
            else:
              # log scale -- isolate positive data
              pos_y = np.array(temp_y)
              pos_y = pos_y[pos_y > 0]
              if(len(pos_y > 1)):
                temp_ymin, temp_ymax = np.min(pos_y), np.max(pos_y)
                if(self.splitY and (axis == 'y') and (np.max((self.minY2, self.maxY2)) > 0)):
                  posLimits = [i for i  in [self.minY2, self.maxY2] if (i > 0)]
                  temp_ymin = np.max((temp_ymin, np.max(posLimits)))
                if(self.splitY and (axis == 'y2') and (np.max((self.minY, self.maxY)) > 0)):
                  posLimits = [i for i  in [self.minY, self.maxY] if (i > 0)]
                  temp_ymax = np.min((temp_ymax, np.min(posLimits)))
                difference = np.log(temp_ymax / temp_ymin)
                if((not self.splitY) or (axis == 'y2')):
                  temp_ymin = np.exp(np.log(temp_ymin) - self.data_spacer * difference)
                if((not self.splitY) or (axis == 'y')):
                  temp_ymax = np.exp(np.log(temp_ymax) + self.data_spacer * difference)
          
          self.setAxisLimits(lower = temp_ymin, upper = temp_ymax, axis = axis, updateLabel = True, target='plot', redraw=redraw, updateGrid=True)
          # update tick entry fields
          self.updateTickEntryField(axis=axis)
      else:
        # consider all data sets
        resid = np.array([])
        for dataset in self.parent.data:
          if(dataset.visibilityResid):
            values, roles = dataset.getData_n_Fit()
            if('resid' in roles):
              dataresid = list(values[:, roles.index('resid')])
              resid = np.hstack((resid, dataresid))

        # ensure that temp_y is at least one element long
        if(resid.size):
          temp_residmin = np.min(resid)
          temp_residmax = np.max(resid)
  
          # ensure minimum limit
          if (temp_residmax - temp_residmin < self.EPSILON):
            temp_residmax += self.EPSILON; temp_residmin -= self.EPSILON
          else:
            # this needs to correspond to the setting in doFit()
            usespacer = 1.2
            temp_residmax *= usespacer
            temp_residmin *= usespacer
          # ensure symmetric limits
          uselimit = np.max((np.abs(temp_residmax), np.abs(temp_residmin)))
          temp_residmin, temp_residmax = -1. * uselimit, 1. * uselimit
          
          self.setAxisLimits(lower=temp_residmin, upper=temp_residmax, axis='y', updateLabel=True, target='resid', redraw=redraw, updateGrid=True)
          # update tick entry fields
          self.updateTickEntryField(axis='resid')

  def flipAxis(self, axis='x', redraw=True):
    # swaps upper and lower limit of axis
    if(axis in ['x', 'x2', 'y', 'y2']):
      if(axis == 'x'):
        self.minX, self.maxX = self.maxX, self.minX
        # better to swap actual text and thus avoid reformatting of entered text (avoids rounding issues)
        lowerText, upperText = self.lowerLimitx.text(), self.upperLimitx.text()
        self.upperLimitx.setText(lowerText)
        self.lowerLimitx.setText(upperText)
        # update axis
        self.ax2.set_xlim([self.minX, self.maxX])
        self.ax.set_xlim([self.minX, self.maxX])
        self.ax_resid.set_xlim([self.minX, self.maxX])
        updateResid = True
      elif(axis == 'x2'):
        self.minX_div, self.maxX_div = self.maxX_div, self.minX_div
        # better to swap actual text and thus avoid reformatting of entered text (avoids rounding issues)
        lowerText, upperText = self.xSplitLowerLimitx.text(), self.xSplitUpperLimitx.text()
        self.xSplitUpperLimitx.setText(lowerText)
        self.xSplitLowerLimitx.setText(upperText)
        # update axis
        self.ax2_div.set_xlim([self.minX_div, self.maxX_div])
        self.ax_div.set_xlim([self.minX_div, self.maxX_div])
        self.ax_resid_div.set_xlim([self.minX_div, self.maxX_div])
        updateResid = True
      elif(axis == 'y'):
        self.minY, self.maxY = self.maxY, self.minY
        # better to swap actual text and thus avoid reformatting of entered text (avoids rounding issues)
        lowerText, upperText = self.lowerLimity.text(), self.upperLimity.text()
        self.upperLimity.setText(lowerText)
        self.lowerLimity.setText(upperText)
        # update axis
        self.ax.set_ylim([self.minY, self.maxY])
        self.ax_div.set_ylim([self.minY, self.maxY])
        updateResid = False
      else:
        self.minY2, self.maxY2 = self.maxY2, self.minY2
        # better to swap actual text and thus avoid reformatting of entered text (avoids rounding issues)
        lowerText, upperText = self.secondLowerLimit.text(), self.secondUpperLimit.text()
        self.secondUpperLimit.setText(lowerText)
        self.secondLowerLimit.setText(upperText)
        # update axis
        self.ax2.set_ylim([self.minY2, self.maxY2])
        self.ax2_div.set_ylim([self.minY2, self.maxY2])
        updateResid = False
        
      # refresh the plot(s)
      if(redraw):
        self.dataplotwidget.myRefresh()
        if(updateResid):
          self.residplotwidget.myRefresh()

  def lassoCallback(self, vertices):
    # call back for lasso selector function
    
    # as a precaution hide our lassoli
    self.lassoSelector.cleanUp()
        
    # then take care of the actual fallback
    if(self.pick2Freehand or (self.pickedMode == 3)):
      self.pick2Freehand = False
      # generate a new extras object
      self.parent.extras.append(ExtrasObject(self.parent))
      valueDict = {'labeltext': 'Freehand', 'extrasType': 'shape', 'shape__type': 'freehand', 'shape__vertices': vertices, 'shape__doFill': False}
      self.parent.extras[-1].setValues(valueDict, redraw=False)
      # deal with duplication and update of menus
      if(self.splitY):
        self.parent.extras[-1].onBothAxes = True
        self.parent.extras[-1].duplicateForSplit()
      self.parent.extras[-1].drawMe(redraw=True)
      # update extras table
      self.parent.objectsarea.refreshExtrasTable()
      self.parent.objectsarea.refreshCurvesTable()
      self.parent.objectsarea.refreshDataTable()
      self.parent.plotArea.updateToggloContainer()
    else:
      # need to check if shift key was pressed at time of initial click
      if(self.toggleShift):
        # check whether current object is on second axes
        if(self.parent.data[self.parent.activeData].onSecondAxes and (not self.splitY)):
          # do we need to check for regular vs. split x axes?
          # => tests show that not which comes as a surprise
          tempVertices = self.ax.transData.transform(vertices)
          transformedVertices = self.ax2.transData.inverted().transform(tempVertices)
          lassoPath = matplotlib.path.Path(transformedVertices)
        else:
          lassoPath = matplotlib.path.Path(vertices)
        values, roles = self.parent.data[self.parent.activeData].getData_n_Fit()
        if(('x' in roles) and ('y' in roles) and len(values[roles.index('x')])):
          xyData = [(x, y) for x, y in zip(values[:, roles.index('x')], values[:, roles.index('y')])]
          if(len(xyData)):
            selectData = lassoPath.contains_points(xyData)
            self.parent.selectedData.mouseSelectIndices(selectData)
    
    # destroy lasso
    del self.lassoSelector
    self.lassoSelector = None
    
  def rectangleCallback(self, vertices):
    # call back for rectangle selector function
    # determine which axis we are on
    inaxes = self.rectangleSelector.ax
    if(inaxes in [self.ax, self.ax2]):
      divAxis = False
    else:
      divAxis = True
      
    # get extents of rectangle selector
    start, end = self.rectangleSelector.getMyExtents()
    if(start != None):
      x1, y1 = start
      x2, y2 = end
      
      if((np.abs(x1 - x2) > 0) and (np.abs(y1 - y2) > 0)):
        xmin, xmax = np.min((x1, x2)), np.max((x1, x2))
        ymin, ymax = np.min((y1, y2)), np.max((y1, y2))
        
        # now assign these values correctly
        if(inaxes in [self.ax, self.ax2]):
          minX, maxX = xmin, xmax
          minX_div, maxX_div = self.minX_div, self.maxX_div
        else:
          minX, maxX = self.minX, self.maxX
          minX_div, maxX_div = xmin, xmax
          
        if(inaxes in [self.ax, self.ax_div]):
          minY, maxY = ymin, ymax
          # check for split y axes
          if(not self.splitY):
            # determine event x and y & transform to second axes
            event_x, event_y = self.ax.transData.transform((xmin, ymin))
            xmin2, ymin2 = self.ax2.transData.inverted().transform((event_x, event_y))
            # et encore une fois
            event_x, event_y = self.ax.transData.transform((xmax, ymax))
            xmax2, ymax2 = self.ax2.transData.inverted().transform((event_x, event_y))
            # and assign values
            minY2, maxY2 = ymin2, ymax2
          else:
            minY2, maxY2 = self.minY2, self.maxY2
        else:
          minY, maxY = self.minY, self.maxY
          minY2, maxY2 = ymin, ymax

        # store current axis limits
        self.storeCoord.extend((self.minX, self.maxX, self.minY, self.maxY, self.minX_div, self.maxX_div, self.minY2, self.maxY2))

        coords = [minX, maxX, minY, maxY, minX_div, maxX_div, minY2, maxY2]
        self.rectSelectorCallbackHelper(coords=coords, divAxis=divAxis, restore=False)
   
    # as a precaution hide our rectangli
    self.rectangleSelector.cleanUp()

    # destroy rectangle
    del self.rectangleSelector
    self.rectangleSelector = None

  def rectSelectorCallbackHelper(self, coords=[], divAxis=False, restore=False):
    # utility function to handle zoom and unzoom
    # this became necessary as later matplotlib versions trigger events differently
    # have to capture right click (for unzoom) already in toggleCrosshairEncore()
    if(len(coords) == 8):
      # only continue if axes limits can be assigned
      [xmin, xmax, ymin, ymax, xmin_div, xmax_div, secondYmin, secondYmax] = coords
        
      if(divAxis or restore):
        # only change the x axis when really required
        if((xmin_div != self.minX_div) or (xmax_div != self.maxX_div)):
          self.setAxisLimits(lower=xmin_div, upper=xmax_div, axis='x2', updateLabel=True, target='plot', redraw=False, updateGrid=True)
          self.setAxisLimits(lower=xmin_div, upper=xmax_div, axis='x2', updateLabel=False, target='resid', redraw=False, updateGrid=True)
          self.updateTickEntryField(axis='x2')
      if(not divAxis or restore):
        # only change the x axis when really required
        if((xmin != self.minX) or (xmax != self.maxX)):
          self.setAxisLimits(lower=xmin, upper=xmax, axis='x', updateLabel=True, target='plot', redraw=False, updateGrid=True)
          self.setAxisLimits(lower=xmin, upper=xmax, axis='x', updateLabel=False, target='resid', redraw=False, updateGrid=True)
          self.updateTickEntryField(axis='x')
      # trigger redrawing of fit function with new axis limits
      if(divAxis or restore):
        self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.plotFunction(fitobject=self.parent.fit[self.parent.activeFit], x=[],\
                       handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True, doAutoZoom=False)
        # copy in case split axes are shown
        curve = self.parent.fit[self.parent.activeFit]
        if(self.parent.plotArea.splitY and curve.onBothAxes):
          curve.duplicateForSplit()
        self.handleResidZero_div = self.plotResidZero(self.handleResidZero_div, redraw=not restore, splitX=True)
      if(not divAxis or restore):
        self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.plotFunction(fitobject=self.parent.fit[self.parent.activeFit], x=[],\
                       handlePlot=self.parent.fit[self.parent.activeFit].handlePlot, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False, doAutoZoom=False)
        # copy in case split axes are shown
        curve = self.parent.fit[self.parent.activeFit]
        if(self.parent.plotArea.splitY and curve.onBothAxes):
          curve.duplicateForSplit()
        self.handleResidZero = self.plotResidZero(self.handleResidZero, redraw=True)
  
      # reset y axis
      self.setAxisLimits(lower=secondYmin, upper=secondYmax, axis='y2', updateLabel=True, target='plot', redraw=False, updateGrid=True)
      self.setAxisLimits(lower=ymin, upper=ymax, axis='y', updateLabel=True, target='plot', redraw=True, updateGrid=True)
      self.updateTickEntryField(axis='y')
      self.updateTickEntryField(axis='y2')

  def setPathStroke(self, state=True, redraw=True):
    # applies path stroke
    self.applyPathStroke = state
    self.setPathEffects(redraw=redraw)

  def setPathStrokeColor(self, value=4*[0.0], redraw=True):
    # changes color of path stroke
    self.pathStrokeColor = value
    # do the path setting draw?
    if((redraw) and (self.applyPathStroke)):
      self.setPathEffects(redraw=redraw)

  def setPathStrokeWidth(self, value=1.0, redraw=True):
    # changes witdth of path stroke
    self.pathStrokeWidth = value
    # do the path setting draw?
    if((redraw) and (self.applyPathStroke)):
      self.setPathEffects(redraw=redraw)

  def setPathShadow(self, state=True, redraw=True):
    # applies path shadow
    self.applyPathShadow = state
    self.setPathEffects(redraw=redraw)

  def setPathShadowColor(self, value=4*[0.0], redraw=True):
    # changes color of path shadow
    self.pathShadowColor = value
    self.pathShadowAlpha = value[-1]
    # do the path setting draw?
    if((redraw) and (self.applyPathShadow)):
      self.setPathEffects(redraw=redraw)

  def setPathShadowOffset(self, value=1.0, direction='x', redraw=True):
    # changes offset of path shadow
    if(direction in ['x', 'y']):
      if(direction == 'x'):
        self.pathShadowX = value
      else:
        self.pathShadowY = value
      # do the path setting draw?
      if((redraw) and (self.applyPathShadow)):
        self.setPathEffects(redraw=redraw)

  def setPathEffects(self, redraw=True):
    # applies path effects
    if(self.applyPathStroke):
      baseEffect = []
    else:
      baseEffect = [PathEffects.Normal()]

    if(self.applyPathShadow):
      pathShadowX = self.pathShadowX
      pathShadowY = self.pathShadowY
      pathShadowColor = self.pathShadowColor
      pathShadowAlpha = self.pathShadowAlpha
      baseEffect = [PathEffects.SimpleLineShadow(offset=(pathShadowX, pathShadowY), shadow_color=pathShadowColor,\
        alpha=pathShadowAlpha)] + baseEffect
      baseEffectText = [PathEffects.SimplePatchShadow(offset=(pathShadowX, pathShadowY), shadow_rgbFace=pathShadowColor,\
        alpha=pathShadowAlpha)] + baseEffect
    else:
      baseEffectText = baseEffect
    
    # deal with existing objects
    if(self.applyPathStroke):
      pathStrokeWidth = self.pathStrokeWidth
      pathStrokeColor = self.pathStrokeColor

      # modify existing extras
      for entry in self.parent.extras:
        relevantHandles = [entry.handle, entry.handle_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handle', 'handle_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleExtra in relevantHandles:
          # exclude area objects from path effects => throws tuple/list error
          if((handleExtra != None) and (entry.extrasType != 'area')):
            if(hasattr(handleExtra, 'get_lw')):
              curr_linewidth = 2 * pathStrokeWidth + handleExtra.get_lw()
            else:
              curr_linewidth = 2 * pathStrokeWidth + 1
            # check whether we have a label here
            if(hasattr(handleExtra, 'get_text')):
              handleExtra.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            else:
              handleExtra.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            # set bbox if present
            if(hasattr(handleExtra, 'get_bbox_patch')):
              handle = handleExtra.get_bbox_patch()
              if(handle != None):
                curr_linewidth = 2 * pathStrokeWidth + handle.get_lw()
                handle.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            # set arrow patch if present
            if(hasattr(handleExtra, 'arrow_patch')):
              handle = handleExtra.arrow_patch
              if(handle != None):
                curr_linewidth = 2 * pathStrokeWidth + handle.get_lw()
                handle.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      # modify existing curves
      for entry in self.parent.fit:
        relevantHandles = [entry.handlePlot, entry.handlePlot_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handlePlot', 'handlePlot_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handlePlot in relevantHandles:
          if(handlePlot != None):
            curr_linewidth = 2 * pathStrokeWidth + handlePlot.get_lw()
            handlePlot.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
  
      # modify existing data sets
      for entry in self.parent.data:
        relevantHandles = [entry.handleData, entry.handleData_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleData', 'handleData_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleData in relevantHandles:
          if(handleData != None):
            curr_linewidth = 2 * pathStrokeWidth + handleData.get_lw()
            handleData.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            
        for handleResid in [entry.handleResid, entry.handleResid_div]:
          if(handleResid != None):
            curr_linewidth = 2 * pathStrokeWidth + handleResid.get_lw()
            handleResid.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      ### leave out scatter plot for now as patheffects invariably throw an error here
#      for entry in self.parent.data:
#        relevantHandles = [entry.handleScatter, entry.handleScatter_div]
#        if(self.splitY):
#          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleScatter', 'handleScatter_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
#        for handleScatter in relevantHandles:
#          if(handleScatter != None):
#            curr_linewidth = 2 * pathStrokeWidth + handleScatter.get_lw()[0]
#            handleScatter.set_path_effects([PathEffects.withStroke(linewidth=5., foreground=pathStrokeColor)])
                    
        relevantHandles = [entry.handleBar, entry.handleBar_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleBar', 'handleBar_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleBar in relevantHandles:
          if(handleBar != None):
            children = handleBar.get_children()
            for entry2 in children:
              curr_linewidth = 2 * pathStrokeWidth + entry2.get_lw()
              entry2.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
        # don't apply to stack style, throws strange error
        #if(entry.handleStack != None):
          #entry.handleStack.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

        for handleResidBar in [entry.handleResidBar, entry.handleResidBar_div]:
          if(handleResidBar != None):
            children = handleResidBar.get_children()
            for entry2 in children:
              curr_linewidth = 2 * pathStrokeWidth + entry2.get_lw()
              entry2.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
        # don't apply to stack style, throws strange error
        #if(entry.handleStack != None):
          #entry.handleStack.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

        relevantHandles = [entry.handleErr, entry.handleErr_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleErr', 'handleErr_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleErr in relevantHandles:
          if(handleErr != None):
            curr_linewidth = 2 * pathStrokeWidth + handleErr[0].get_lw()
            handleErr[0].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            for entry2 in handleErr[1]:
              # remember caps are drawn as markers not lines
              if(entry2.get_markeredgewidth() > 0):
                curr_linewidth = 2 * pathStrokeWidth + entry2.get_markeredgewidth()
                entry2.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
              else:
                # don't draw effects for zero width markers
                #entry2.set_path_effects(baseEffect)
                # in this case don't do anything since setting path effects would cause drawing of caps (w/ currently set path effects)
                pass
            for entry2 in handleErr[2]:
              # have to use get_linewidth() here as get_lw() not implemented?!
              curr_linewidth = 2 * pathStrokeWidth + entry2.get_linewidth()
              entry2.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

        # now handle violin and box plots
        relevantHandles = [entry.handleViolin, entry.handleViolin_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleViolin', 'handleViolin_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleViolin in relevantHandles:
          if(handleViolin != None):
            # bodies throws a crash when applying path effects
            validItems = ['cmins', 'cmaxes', 'cmeans', 'cmedians', 'cbars', 'boxes', 'medians', 'whiskers', 'caps', 'fliers']
            for subtarget in [i for i in handleViolin if i in validItems]:
              if(type(handleViolin[subtarget]) == type([])):
                itemList = handleViolin[subtarget]
              else:
                itemList = [handleViolin[subtarget]]
              # cycle through all items
              for item in itemList:
                if(hasattr(item, 'get_linewidth')):
                  curr_linewidth = 2 * pathStrokeWidth + item.get_linewidth()
                else:
                  curr_linewidth = 2 * pathStrokeWidth + 1.0
                if(hasattr(item, 'set_path_effects')):
                  item.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

        # now handle the text labels
        relevantHandles = [entry.handleText, entry.handleText_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleText', 'handleText_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleText in relevantHandles:
          if(handleText != None):
            for item in handleText:
              if(hasattr(item, 'get_linewidth')):
                curr_linewidth = 2 * pathStrokeWidth + item.get_linewidth()
              else:
                curr_linewidth = 2 * pathStrokeWidth + 1.0
              if(hasattr(item, 'set_path_effects')):
                item.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
       
      # modify axes
      for entry in self.ax.spines:
        if(entry in ['left', 'right']):
          curr_linewidth = 2 * pathStrokeWidth + self.ax.spines[entry].get_lw()
          self.ax.spines[entry].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in self.ax2.spines:
        curr_linewidth = 2 * pathStrokeWidth + self.ax2.spines[entry].get_lw()
        self.ax2.spines[entry].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in self.ax_resid.spines:
        curr_linewidth = 2 * pathStrokeWidth + self.ax_resid.spines[entry].get_lw()
        self.ax_resid.spines[entry].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in self.ax_div.spines:
        if(entry in ['left', 'right']):
          curr_linewidth = 2 * pathStrokeWidth + self.ax_div.spines[entry].get_lw()
          self.ax_div.spines[entry].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in self.ax2_div.spines:
        curr_linewidth = 2 * pathStrokeWidth + self.ax2_div.spines[entry].get_lw()
        self.ax2_div.spines[entry].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in self.ax_resid_div.spines:
        curr_linewidth = 2 * pathStrokeWidth + self.ax_resid_div.spines[entry].get_lw()
        self.ax_resid_div.spines[entry].set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      # modify grid lines
      for key in self.gridLinesStore:
        if(self.gridVisible[key]):
          for entry in self.gridLinesStore[key]:
            curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
            entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
        if(key in self.gridLinesStore_resid):
          for entry in self.gridLinesStore_resid[key]:
            curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
            entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            
      # modify tick lines
      #for entry in self.ax.xaxis.get_ticklines():
      #  curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
      #  entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in (self.ax.yaxis.get_ticklines() + self.ax.yaxis.get_ticklines(minor=True) + self.ax_div.yaxis.get_ticklines() + self.ax_div.yaxis.get_ticklines(minor=True)):
        curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
        entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in (self.ax_resid.xaxis.get_ticklines() + self.ax_resid.xaxis.get_ticklines(minor=True) + self.ax_resid_div.xaxis.get_ticklines() + self.ax_resid_div.xaxis.get_ticklines(minor=True)):
        curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
        entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in (self.ax_resid.yaxis.get_ticklines() + self.ax_resid.yaxis.get_ticklines(minor=True) + self.ax_resid_div.yaxis.get_ticklines() + self.ax_resid_div.yaxis.get_ticklines(minor=True)):
        curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
        entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in (self.ax2.xaxis.get_ticklines() + self.ax2.xaxis.get_ticklines(minor=True) + self.ax2_div.xaxis.get_ticklines() + self.ax2_div.xaxis.get_ticklines(minor=True)):
        curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
        entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      for entry in (self.ax2.yaxis.get_ticklines() + self.ax2.yaxis.get_ticklines(minor=True) + self.ax2_div.yaxis.get_ticklines() + self.ax2_div.yaxis.get_ticklines(minor=True)):
        curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
        entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
  
      # zero line
      for handleResidZero in [self.handleResidZero, self.handleResidZero_div]:
        if(handleResidZero != None):
          curr_linewidth = 2 * pathStrokeWidth + handleResidZero.get_lw()
          handleResidZero.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      # and the cursor
      for cursor in [self.cursor, self.cursor_div, self.cursor2, self.cursor2_div]:
        if(cursor != None):
          handles = cursor.getHandles()
          for entry in handles:
            if(hasattr(entry, 'get_lw')):
              curr_linewidth = 2 * pathStrokeWidth + entry.get_lw()
            else:
              curr_linewidth = 2 * pathStrokeWidth + 1
            # check for text
            if(hasattr(entry, 'get_text')):
              entry.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
            else:
              entry.set_path_effects(baseEffect + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      # and the axes labels
      curr_linewidth = 2 * pathStrokeWidth + 1
      for entry in [self.ax2, self.ax_resid, self.ax2_div, self.ax_resid_div]:
        entry.xaxis.label.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
        entry.yaxis.label.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      self.ax.yaxis.label.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])
      self.ax_div.yaxis.label.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      # and the tick labels
      curr_linewidth = 2 * pathStrokeWidth + 1
      tickLabels = []
      for entry in [self.ax, self.ax_resid, self.ax2, self.ax_div, self.ax_resid_div, self.ax2_div]:
        tickLabels.extend(entry.get_xticklabels(which='both'))
        tickLabels.extend(entry.get_yticklabels(which='both'))
        tickLabels.append(entry.xaxis.get_offset_text())
        tickLabels.append(entry.yaxis.get_offset_text())
      for entry in tickLabels:
        entry.set_path_effects(baseEffectText + [PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor)])

      # update baseEffect for new plot items
      curr_linewidth = 2 * pathStrokeWidth + 1.0
      baseEffect.append(PathEffects.withStroke(linewidth=curr_linewidth, foreground=pathStrokeColor))
    else:
      # check for complete turnoff of pathEffects for text labels
      if((not self.applyPathStroke) and (not self.applyPathShadow)):
        baseEffectText = []

      # modify existing extras
      for entry in self.parent.extras:
        relevantHandles = [entry.handle, entry.handle_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handle', 'handle_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleExtra in relevantHandles:
          # exclude area objects from path effects => throws tuple/list error
          if((handleExtra != None) and (entry.extrasType != 'area')):
            # check whether we have a label here
            if(hasattr(handleExtra, 'get_text')):
              handleExtra.set_path_effects(baseEffectText)
            else:
              handleExtra.set_path_effects(baseEffect)
            # set bbox if present
            if(hasattr(handleExtra, 'get_bbox_patch')):
              handle = handleExtra.get_bbox_patch()
              if(handle != None):
                handle.set_path_effects(baseEffect)
            # set arrow patch if present
            if(hasattr(handleExtra, 'arrow_patch')):
              handle = handleExtra.arrow_patch
              if(handle != None):
                handle.set_path_effects(baseEffect)

      # modify existing curves
      for entry in self.parent.fit:
        relevantHandles = [entry.handlePlot, entry.handlePlot_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handlePlot', 'handlePlot_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handlePlot in relevantHandles:
          if(handlePlot != None):
            handlePlot.set_path_effects(baseEffect)
  
      # modify existing data sets
      for entry in self.parent.data:
        relevantHandles = [entry.handleData, entry.handleData_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleData', 'handleData_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleData in relevantHandles:
          if(handleData != None):
            handleData.set_path_effects(baseEffect)
            
        for handleResid in [entry.handleResid, entry.handleResid_div]:
          if(handleResid != None):
            handleResid.set_path_effects(baseEffect)
            
        ### leave out scatter plot for now as patheffects invariably throw an error here
            
        relevantHandles = [entry.handleBar, entry.handleBar_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleBar', 'handleBar_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleBar in relevantHandles:
          if(handleBar != None):
            children = handleBar.get_children()
            for entry2 in children:
              entry2.set_path_effects(baseEffect)

        # don't apply to stack style, throws strange error
        for handleResidBar in [entry.handleResidBar, entry.handleResidBar_div]:
          if(handleResidBar != None):
            children = handleResidBar.get_children()
            for entry2 in children:
              entry2.set_path_effects(baseEffect)

        # don't apply to stack style, throws strange error
        relevantHandles = [entry.handleErr, entry.handleErr_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleErr', 'handleErr_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleErr in relevantHandles:
          if(handleErr != None):
            handleErr[0].set_path_effects(baseEffect)
            for entry2 in handleErr[1]:
              entry2.set_path_effects(baseEffect)
            for entry2 in handleErr[2]:
              entry2.set_path_effects(baseEffect)

        # now handle violin and box plots
        relevantHandles = [entry.handleViolin, entry.handleViolin_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleViolin', 'handleViolin_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleViolin in relevantHandles:
          if(handleViolin != None):
            # bodies throws a crash when applying path effects
            validItems = ['cmins', 'cmaxes', 'cmeans', 'cmedians', 'cbars', 'boxes', 'medians', 'whiskers', 'caps', 'fliers']
            for subtarget in [i for i in handleViolin if i in validItems]:
              if(type(handleViolin[subtarget]) == type([])):
                itemList = handleViolin[subtarget]
              else:
                itemList = [handleViolin[subtarget]]
              # cycle through all items
              for item in itemList:
                if(hasattr(item, 'set_path_effects')):
                  item.set_path_effects(baseEffect)

        # now handle the text labels
        relevantHandles = [entry.handleText, entry.handleText_div]
        if(self.splitY):
          relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleText', 'handleText_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
        for handleText in relevantHandles:
          if(handleText != None):
            for item in handleText:
              if(hasattr(item, 'set_path_effects')):
                item.set_path_effects(baseEffectText)
       
      # modify axes
      for entry in self.ax.spines:
        if(entry in ['left', 'right']):
          self.ax.spines[entry].set_path_effects(baseEffect)
      for entry in self.ax2.spines:
        self.ax2.spines[entry].set_path_effects(baseEffect)
      for entry in self.ax_resid.spines:
        self.ax_resid.spines[entry].set_path_effects(baseEffect)
      for entry in self.ax_div.spines:
        if(entry in ['left', 'right']):
          self.ax_div.spines[entry].set_path_effects(baseEffect)
      for entry in self.ax2_div.spines:
        self.ax2_div.spines[entry].set_path_effects(baseEffect)
      for entry in self.ax_resid_div.spines:
        self.ax_resid_div.spines[entry].set_path_effects(baseEffect)

      # modify grid lines
      for key in self.gridLinesStore:
        if(self.gridVisible[key]):
          for entry in self.gridLinesStore[key]:
            entry.set_path_effects(baseEffect)
        if(key in self.gridLinesStore_resid):
          for entry in self.gridLinesStore_resid[key]:
            entry.set_path_effects(baseEffect)

      # modify tick lines
      for entry in (self.ax.yaxis.get_ticklines() + self.ax.yaxis.get_ticklines(minor=True) + self.ax_div.yaxis.get_ticklines() + self.ax_div.yaxis.get_ticklines(minor=True)):
        entry.set_path_effects(baseEffect)
      for entry in (self.ax_resid.xaxis.get_ticklines() + self.ax_resid.xaxis.get_ticklines(minor=True) + self.ax_resid_div.xaxis.get_ticklines() + self.ax_resid_div.xaxis.get_ticklines(minor=True)):
        entry.set_path_effects(baseEffect)
      for entry in (self.ax_resid.yaxis.get_ticklines() + self.ax_resid.yaxis.get_ticklines(minor=True) + self.ax_resid_div.yaxis.get_ticklines() + self.ax_resid_div.yaxis.get_ticklines(minor=True)):
        entry.set_path_effects(baseEffect)
      for entry in (self.ax2.xaxis.get_ticklines() + self.ax2.xaxis.get_ticklines(minor=True) + self.ax2_div.xaxis.get_ticklines() + self.ax2_div.xaxis.get_ticklines(minor=True)):
        entry.set_path_effects(baseEffect)
      for entry in (self.ax2.yaxis.get_ticklines() + self.ax2.yaxis.get_ticklines(minor=True) + self.ax2_div.yaxis.get_ticklines() + self.ax2_div.yaxis.get_ticklines(minor=True)):
        entry.set_path_effects(baseEffect)

      # zero line
      for handleResidZero in [self.handleResidZero, self.handleResidZero_div]:
        if(handleResidZero != None):
          handleResidZero.set_path_effects(baseEffect)
        
      # and the axes labels
      for entry in [self.ax2, self.ax_resid, self.ax2_div, self.ax_resid_div]:
        entry.xaxis.label.set_path_effects(baseEffectText)
        entry.yaxis.label.set_path_effects(baseEffectText)
      self.ax.yaxis.label.set_path_effects(baseEffectText)
      self.ax_div.yaxis.label.set_path_effects(baseEffectText)

      # and the tick labels
      tickLabels = []
      for entry in [self.ax, self.ax_resid, self.ax2, self.ax_div, self.ax_resid_div, self.ax2_div]:
        tickLabels.extend(entry.get_xticklabels(which='both'))
        tickLabels.extend(entry.get_yticklabels(which='both'))
        tickLabels.append(entry.xaxis.get_offset_text())
        tickLabels.append(entry.yaxis.get_offset_text())
      for entry in tickLabels:
        entry.set_path_effects(baseEffectText)

      # and the cursor
      for cursor in [self.cursor, self.cursor_div, self.cursor2, self.cursor2_div]:
        if(cursor != None):
          handles = cursor.getHandles()
          for entry in handles:
            # check for text
            if(hasattr(entry, 'get_text')):
              entry.set_path_effects(baseEffectText)
            else:
              entry.set_path_effects(baseEffect)

    # introduces path effects for new plot items
    tempDict = {}
    tempDict['path.effects'] = baseEffect
    matplotlib.rcParams.update(tempDict)

    # update plot
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()
    
  def setExportSize(self, width=None, height=None, redraw=True):
    # sets canvas size
    if(width != None):
      self.exportWidth = width
    if(height != None):
      self.exportHeight = height
    
    # update plot
    redraw = redraw and ((width != None) or (height != None))
    redraw = redraw and (self.arrowVisible['x'] or self.arrowVisible['y'])
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

  def setXkcdSetting(self, value=1.0, item='scale', redraw=True):
    # update xckd setttings
    if(item in ['scale', 'length', 'random']):
      if(item == 'scale'):
        if(self.xkcdScale == value):
          redraw = False
        else:
          self.xkcdScale = value
      elif(item == 'length'):
        if(self.xkcdLength == value):
          redraw = False
        else:
          self.xkcdLength = value
      elif(item == 'random'):
        if(self.xkcdRandomness == value):
          redraw = False
        else:
          self.xkcdRandomness = value
          
      # do the xkcdify?
      if((redraw) and (self.xkcd)):
        self.xkcdify(state=self.xkcd, redraw=redraw)

  def xkcdify(self, state=True, redraw=True):
    # set xkcd-like parameters
    # store previous parameters
    if((not self.xkcd) and (state)):
      if ('font.sans-serif' in matplotlib.rcParams):
        self.xkcdStoreFonts = matplotlib.rcParams['font.sans-serif']
      else:
        self.xkcdStoreFonts = ['DejaVu Sans']
    
    # set new parameters
    self.xkcd = state
    tempDict = {}
    if(self.xkcd):
      xkcdScale = self.xkcdScale
      xkcdLength = self.xkcdLength
      xkcdRandomness = self.xkcdRandomness

      # check for presence of funny fonts
      addFonts = []
      fontCandidates = 'Humor Sans,Comic Sans MS'.split(',')
      for entry in fontCandidates:
        if entry in self.parent.fontNames:
          addFonts.append(entry)
          
      if(len(addFonts)):
        tempDict['font.sans-serif'] = addFonts
        tempDict['font.sans-serif'].extend(self.xkcdStoreFonts)
    else:
      xkcdScale = 0
      xkcdLength = 0
      xkcdRandomness = 0
      
      # restore original fonts
      tempDict['font.sans-serif'] = self.xkcdStoreFonts

    # introduces xkcd-type style for new plot items
    tempDict['path.sketch'] = (xkcdScale, xkcdLength, xkcdRandomness)
    matplotlib.rcParams.update(tempDict)
    
    # modify existing extras
    for entry in self.parent.extras:
      relevantHandles = [entry.handle, entry.handle_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handle', 'handle_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleExtra in relevantHandles:
        if(handleExtra != None):
          # modify object directly (for line)
          if(hasattr(handleExtra, 'set_sketch_params')):
            handleExtra.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
          # set bbox if present
          if(hasattr(handleExtra, 'get_bbox_patch')):
            handle = handleExtra.get_bbox_patch()
            if(handle != None):
              handle.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
          # set arrow patch if present
          if(hasattr(handleExtra, 'arrow_patch')):
            handle = handleExtra.arrow_patch
            if(handle != None):
              handle.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    
    # modify existing curves
    for entry in self.parent.fit:
      relevantHandles = [entry.handlePlot, entry.handlePlot_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handlePlot', 'handlePlot_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handlePlot in relevantHandles:
        if(handlePlot != None):
          handlePlot.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    
    # modify existing data sets
    for entry in self.parent.data:
      relevantHandles = [entry.handleData, entry.handleData_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleData', 'handleData_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleData in relevantHandles:
        if(handleData != None):
          handleData.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      for handleResid in [entry.handleResid, entry.handleResid_div]:
        if(handleResid != None):
          handleResid.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      relevantHandles = [entry.handleBar, entry.handleBar_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleBar', 'handleBar_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleBar in relevantHandles:
        if(handleBar != None):
          children = handleBar.get_children()
          for entry2 in children:
            entry2.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      relevantHandles = [entry.handleStack, entry.handleStack_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleStack', 'handleStack_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleStack in relevantHandles:
        if(handleStack != None):
          handleStack.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      relevantHandles = [entry.handleStackNeg, entry.handleStackNeg_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleStackNeg', 'handleStackNeg_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleStackNeg in relevantHandles:
        if(handleStackNeg != None):
          handleStackNeg.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      for handleResidBar in [entry.handleResidBar, entry.handleResidBar_div]:
        if(handleResidBar != None):
          children = handleResidBar.get_children()
          for entry2 in children:
            entry2.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      for handleResidStack in [entry.handleResidStack, entry.handleResidStack_div]:
        if(handleResidStack != None):
          handleResidStack.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      for handleResidStackNeg in [entry.handleResidStackNeg, entry.handleResidStackNeg_div]:
        if(handleResidStackNeg != None):
          handleResidStackNeg.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      relevantHandles = [entry.handleErr, entry.handleErr_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleErr', 'handleErr_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleErr in relevantHandles:
        if(handleErr != None):
          handleErr[0].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
          for entry2 in handleErr[1]:
            entry2.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
          for entry2 in handleErr[2]:
            entry2.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

      relevantHandles = [entry.handleViolin, entry.handleViolin_div]
      if(self.splitY):
        relevantHandles += [entry.handlesBothAxes[handle] for handle in ['handleViolin', 'handleViolin_div'] if ((handle in entry.handlesBothAxes) and (entry.handlesBothAxes != None))]
      for handleViolin in relevantHandles:
        if(handleViolin != None):
          for subtarget in handleViolin:
            if(type(handleViolin[subtarget]) == type([])):
              itemList = handleViolin[subtarget]
            else:
              itemList = [handleViolin[subtarget]]
            # cycle through all items
            for item in itemList:
              if(hasattr(item, 'set_sketch_params')):
                item.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # modify axes and background
    for entry in self.ax.spines:
      if(entry in ['left', 'right']):
        self.ax.spines[entry].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in self.ax_resid.spines:
      self.ax_resid.spines[entry].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in self.ax2.spines:
      self.ax2.spines[entry].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    self.ax2.patch.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    self.ax_resid.patch.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in self.ax_div.spines:
      if(entry in ['left', 'right']):
        self.ax_div.spines[entry].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in self.ax_resid_div.spines:
      self.ax_resid_div.spines[entry].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in self.ax2_div.spines:
      self.ax2_div.spines[entry].set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    self.ax2_div.patch.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    self.ax_resid_div.patch.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # modify grid lines
    for key in self.gridLinesStore:
      if(self.gridVisible[key]):
        for entry in self.gridLinesStore[key]:
          entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
      if(key in self.gridLinesStore_resid):
        for entry in self.gridLinesStore_resid[key]:
          entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # modify tick lines (somehow not heeded by matplotlib?!)
    for entry in (self.ax.yaxis.get_ticklines() + self.ax.yaxis.get_ticklines(minor=True) + self.ax_div.yaxis.get_ticklines() + self.ax_div.yaxis.get_ticklines(minor=True)):
      entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in (self.ax_resid.xaxis.get_ticklines() + self.ax_resid.xaxis.get_ticklines(minor=True) + self.ax_resid_div.xaxis.get_ticklines() + self.ax_resid_div.xaxis.get_ticklines(minor=True)):
      entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in (self.ax_resid.yaxis.get_ticklines() + self.ax_resid.yaxis.get_ticklines(minor=True) + self.ax_resid_div.yaxis.get_ticklines() + self.ax_resid_div.yaxis.get_ticklines(minor=True)):
      entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in (self.ax2.xaxis.get_ticklines() + self.ax2.xaxis.get_ticklines(minor=True) + self.ax2_div.xaxis.get_ticklines() + self.ax2_div.xaxis.get_ticklines(minor=True)):
      entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    for entry in (self.ax2.yaxis.get_ticklines() + self.ax2.yaxis.get_ticklines(minor=True) + self.ax2_div.yaxis.get_ticklines() + self.ax2_div.yaxis.get_ticklines(minor=True)):
      entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # zero line
    for handleResidZero in [self.handleResidZero, self.handleResidZero_div]:
      if(handleResidZero != None):
        handleResidZero.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
      
    # and the cursor
    for cursor in [self.cursor, self.cursor_div, self.cursor2, self.cursor2_div]:
      if(cursor != None):
        handles = cursor.getHandles()
        for entry in handles:
          entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # and the axes labels
    for entry in [self.ax2, self.ax_resid, self.ax2_div, self.ax_resid_div]:
      entry.xaxis.label.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
      entry.yaxis.label.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    self.ax.yaxis.label.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)
    self.ax_div.yaxis.label.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # and the tick labels
    tickLabels = []
    for entry in [self.ax, self.ax2, self.ax_resid, self.ax_div, self.ax2_div, self.ax_resid_div]:
      tickLabels.extend(entry.get_xticklabels(which='both'))
      tickLabels.extend(entry.get_yticklabels(which='both'))
    for entry in tickLabels:
      entry.set_sketch_params(xkcdScale, xkcdLength, xkcdRandomness)

    # update plot
    if(redraw):
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()
    
  def initPlot(self, initialize=True):
    if(initialize):
      plt.ioff()
      # initialize data plot
      self.ax2 = self.matplot.add_subplot(221)
      self.ax2.autoscale(enable=False, axis='both')
      self.ax2.xaxis.set_label_text('x')
      self.ax2.yaxis.set_label_text('y')
      self.ax2.yaxis.set_label_position('right')
      self.ax2.yaxis.set_ticks_position('right')
      self.ax2.grid(False, which='both')
      self.ax2.patch.set_facecolor('none')
      
      # set up axes
      self.ax2_div = self.matplot.add_subplot(222)
      self.ax2_div.set_xscale(self.modeX_div)
      self.ax2_div.set_xlim((self.minX_div, self.maxX_div))
      # hide the extra axis
      self.ax2_div.grid(False, which='both')
      self.ax2_div.set_visible(False)
      self.ax2_div.yaxis.set_label_position('right')

      # we cannot set entire axis invisible as this also removes canvas etc.
      self.toggleSecondAxesItems(state=False)
      
      # ax object should be twinned to ax2 (not vice versa), such that it ends on top
      self.ax = self.matplot.add_subplot(223)

      self.ax.autoscale(enable=False, axis='both')
      self.ax.xaxis.set_visible(False)
      self.ax.yaxis.set_label_position('left')
      self.ax.yaxis.set_ticks_position('left')
      self.ax.grid(False, which='both')
      # for good measure, reset ticks position on ax2
      self.ax2.yaxis.set_ticks_position('right')
      for entry in ['top', 'bottom']:
        self.ax.spines[entry].set_visible(False)
      self.matplot.patch.set_facecolor(self.canvasColor)

      # set up extra axes
      self.ax_div = self.matplot.add_subplot(224)

      self.ax_div.set_xscale(self.modeX_div)
      self.ax_div.set_xlim((self.minX_div, self.maxX_div))
      # hide diverse elements
      for entry in ['top', 'bottom', 'left', 'right']:
        self.ax_div.spines[entry].set_visible(False)
      self.ax_div.patch.set_facecolor('none')
      self.ax_div.grid(False, which='both')
      self.ax_div.xaxis.set_ticks_position('none')
      self.ax_div.yaxis.set_ticks_position('none')
      tickLabels = self.ax_div.get_xticklabels(which='both')
      tickLabels.append(self.ax_div.xaxis.get_offset_text())
      tickLabels.extend(self.ax_div.get_yticklabels(which='both'))
      tickLabels.append(self.ax_div.yaxis.get_offset_text())
      for entry in tickLabels:
        entry.set_visible(False)
      # hide the extra axis
      self.ax_div.set_visible(False)

      # adjust z-order to make original axes appear in front (important for mouse cursor)
      self.ax.set_zorder(self.ax_div.get_zorder() + 1)
      self.ax2.set_zorder(self.ax2_div.get_zorder() + 1)
      
      # generate additional plot object for colored background
      self.colorPlane = self.matplot.add_subplot(111, label='tilt')
      self.colorPlane.autoscale(enable=False, axis='both')
      # hide essentially all elements of this new plot
      self.colorPlane.xaxis.set_visible(False)
      self.colorPlane.yaxis.set_ticks_position('none')
      for entry in ['top', 'bottom', 'left', 'right']:
        self.colorPlane.spines[entry].set_visible(False)
      self.colorPlane.xaxis.set_label_text('')
      self.colorPlane.yaxis.set_label_text('')
      self.colorPlane.xaxis.label.set_visible(False)
      self.colorPlane.yaxis.label.set_visible(False)
      self.colorPlane.set_xlim(0, 1)
      self.colorPlane.set_ylim(0, 1)
      self.colorPlane.set_xticks([])
      self.colorPlane.set_yticks([])
      self.colorPlane.patch.set_facecolor([0.0] * 4)
      self.colorPlane.grid(False, which='both')
      # initialize handle for colored background
      if(self.canvasGradientVisible):
        self.setCanvasGradient(redraw=False)
      else:
        self.colorBackground = None
      self.colorPlane.set_zorder(self.ax_div.get_zorder() - 1)
        
      # initalize some values
      self.handleData = None
      self.handlePlot = None
      self.handleErr = None
      self.handleErrShady = None
    
      # initialize resid plot
      self.ax_resid = self.residplot.add_subplot(211)
      self.ax_resid.autoscale(enable=False, axis='both')
      self.ax_resid.grid(False, which='both')
      self.residplot.patch.set_facecolor(self.canvasColor)
      
      # set up extra axes
      self.ax_resid_div = self.residplot.add_subplot(212)
      self.ax_resid_div.set_xscale(self.modeX_div)
      self.ax_resid_div.set_xlim((self.minX_div, self.maxX_div))
      self.ax_resid_div.grid(False, which='both')
      tickLabels = self.ax_resid_div.get_yticklabels(which='both')
      tickLabels.append(self.ax_resid_div.yaxis.get_offset_text())
      for entry in tickLabels:
        entry.set_visible(False)
      # hide the extra axis
      self.ax_resid_div.set_visible(False)

      # adjust z-order to make original axes appear in front
      self.ax_resid.set_zorder(self.ax_resid_div.get_zorder() + 1)

      # refresh plots
      self.dataplotwidget.myRefresh()
      self.residplotwidget.myRefresh()

      # generate additional plot object for colored background
      self.colorPlane_resid = self.residplot.add_subplot(111, label='tilt_resid')
      self.colorPlane_resid.autoscale(enable=False, axis='both')
      # hide essentially all elements of this new plot
      self.colorPlane_resid.xaxis.set_visible(False)
      self.colorPlane_resid.yaxis.set_ticks_position('none')
      for entry in ['top', 'bottom', 'left', 'right']:
        self.colorPlane_resid.spines[entry].set_visible(False)
      self.colorPlane_resid.xaxis.set_label_text('')
      self.colorPlane_resid.yaxis.set_label_text('')
      self.colorPlane_resid.xaxis.label.set_visible(False)
      self.colorPlane_resid.yaxis.label.set_visible(False)
      self.colorPlane_resid.set_xlim(0, 1)
      self.colorPlane_resid.set_ylim(0, 1)
      self.colorPlane_resid.set_xticks([])
      self.colorPlane_resid.set_yticks([])
      self.colorPlane_resid.patch.set_facecolor([0.0] * 4)
      self.colorPlane_resid.grid(False, which='both')
      # initialize handle for colored background
      if(self.canvasGradientVisible):
        self.setCanvasGradient(redraw=False, target='resid')
      else:
        self.colorBackground_resid = None
      self.colorPlane_resid.set_zorder(self.ax_resid_div.get_zorder() - 1)

      # initalize some values
      self.handleResid = None
      self.handleResidZero = None
      self.handleResidZero_div = None
      self.handlesAbout = []
      
      # initialize the plot rectangle and lasso selector
      # (this is the new format with ad hoc selectors rather than several persistent instances)
      self.lassoSelector, self.rectangleSelector = None, None

      # set up cursor
      self.matplot.canvas.mpl_connect('button_press_event', self.toggleCrossHairEncore)
      self.matplot.canvas.mpl_connect('scroll_event', self.mouseScrolled)
      self.matplot.canvas.mpl_connect('motion_notify_event', self.moveHandler)
      self.matplot.canvas.mpl_connect('button_release_event', self.releaseHandler)
      
      # set up click handler in residplot
      self.residplot.canvas.mpl_connect('button_press_event', self.pickHandlerResid)

    # beware that setTickMarkLabelShow() may also alter self.ticksXShow etc.
    # => need to store original values and restore further down
    restoreXShow, restoreYShow, restoreY2Show, restoreResidYShow = self.ticksXShow, self.ticksYShow, self.ticksY2Show, self.ticksResidYShow

    for target in ['plot', 'resid']:
      # labels etc.
      self.toggleAxisLabel(self.labelXShow, axis='x', redraw=False, target=target)
      self.setAxisLabel(self.labelX, axis='x', redraw=False, target=target)
      self.setAxisLabelColor(value = self.labelXColor, axis = 'x', redraw = False, target=target)
      self.setAxisLabelSize(value = self.labelXSize, axis = 'x', redraw = False, target=target)
      self.setAxisLabelBold(value = self.labelXWeight, axis = 'x', redraw = False, target=target)
      self.setAxisLabelItalic(value = self.labelXStyle, axis = 'x', redraw = False, target=target)
      self.setAxisLabelVariant(value = self.labelXVariant, axis = 'x', redraw = False, target=target)
      self.setAxisLabelAlignment(value = self.labelXAlignment, axis = 'x', redraw = False, target=target)
      self.setAxisLabelAlignmentVertical(value = self.labelXAlignmentVertical, axis = 'x', redraw = False, target=target)
      self.setAxisLabelPad(value = self.labelXPad, axis = 'x', redraw = False, target=target)
      self.setAxisLabelPos(value = self.labelXPos, axis = 'x', redraw = False, target=target)
      self.setAxisLabelAngle(value = self.labelXAngle, axis = 'x', redraw = False, target=target)
      self.setAxisFont(value = self.axisFont['x'], axis = 'x', redraw = False, target=target)
      self.toggleAxisLabel(self.labelYShow, axis='y', redraw=False, target=target)
      self.setAxisLabel(labeltext=self.labelY, axis='y', redraw=False, target=target)
      if(target == 'plot'):
        self.setAxisLabel(labeltext=self.labelY2, axis='y2', redraw=False, target=target)
        self.toggleAxisLabel(self.labelY2Show, axis='y2', redraw=False, target=target)
      self.setAxisLabelColor(value = self.labelYColor, axis = 'y', redraw = False, target=target)
      self.setAxisLabelSize(value = self.labelYSize, axis = 'y', redraw = False, target=target)
      self.setAxisLabelBold(value = self.labelYWeight, axis = 'y', redraw = False, target=target)
      self.setAxisLabelItalic(value = self.labelYStyle, axis = 'y', redraw = False, target=target)
      self.setAxisLabelVariant(value = self.labelYVariant, axis = 'y', redraw = False, target=target)
      self.setAxisLabelAlignment(value = self.labelYAlignment, axis = 'y', redraw = False, target=target)
      self.setAxisLabelAlignmentVertical(value = self.labelYAlignmentVertical, axis = 'y', redraw = False, target=target)
      self.setAxisLabelPad(value = self.labelYPad, axis = 'y', redraw = False, target=target)
      self.setAxisLabelPos(value = self.labelYPos, axis = 'y', redraw = False, target=target)
      self.setAxisLabelAngle(value = self.labelYAngle, axis = 'y', redraw = False, target=target)
      self.setAxisFont(value = self.axisFont['y'], axis = 'y', redraw = False, target=target)
      if(target == 'plot'):
        self.setAxisLabelColor(value = self.labelY2Color, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelSize(value = self.labelY2Size, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelBold(value = self.labelY2Weight, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelItalic(value = self.labelY2Style, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelVariant(value = self.labelY2Variant, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelAlignment(value = self.labelY2Alignment, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelAlignmentVertical(value = self.labelY2AlignmentVertical, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelPad(value = self.labelY2Pad, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelPos(value = self.labelY2Pos, axis = 'y2', redraw = False, target=target)
        self.setAxisLabelAngle(value = self.labelY2Angle, axis = 'y2', redraw = False, target=target)
        self.setAxisFont(value = self.axisFont['y2'], axis = 'y2', redraw = False, target=target)
      
      # set axes properties
      for key in self.axisVisible:
        self.setAxisVisibility(value = self.axisVisible[key], axis = key, redraw = False, target=target)
      for key in self.axisWidth:
        self.setAxisWidth(value = self.axisWidth[key], axis = key, redraw = False, target=target)
      for key in self.axisStyle:
        self.setAxisStyle(value = self.axisStyle[key], axis = key, redraw = False, target=target)
      for key in self.axisDashStyle:
        self.setAxisDashStyle(value = self.axisDashStyle[key], axis = key, redraw = False, target=target)
      for key in self.axisColor:
        self.setAxisColor(value = self.axisColor[key], axis = key, redraw = False, target=target)

      if(target == 'plot'):
        useKeys = ['left', 'right', 'bottom', 'top', 'left2', 'right2']
      else:
        useKeys = ['left', 'right', 'bottom', 'top']
      if(not initialize):
        for key in useKeys:
          self.setAxisPosition(value=self.axisPosition[key], axis=key, redraw=False, target=target)
          self.setAxisPositionValue(value=self.axisPositionValue[key], axis=key, redraw=False, target=target)
      for key in useKeys:
        self.setAxisBoundaryValue(value=self.axisBoundary[key][0], lower=True, axis=key, redraw=False, target=target)
        self.setAxisBoundaryValue(value=self.axisBoundary[key][1], lower=False, axis=key, redraw=False, target=target)
        self.setAxisBoundary(value=self.axisBoundaryCheck[key], axis=key, redraw=False, target=target)
        
      # set axes arrows
      for axis in ['x', 'y']:
        self.setAxisArrowColor(value=self.arrowColor[axis], axis=axis, item='line', redraw=False)
        self.setAxisArrowColor(value=self.arrowFill[axis], axis=axis, item='fill', redraw=False)
        self.setAxisArrowHeadWidth(value=self.arrowHeadWidth[axis], axis=axis, redraw=False)
        self.setAxisArrowHeadLength(value=self.arrowHeadLength[axis], axis=axis, redraw=False)
        self.setAxisArrowOverhang(value=self.arrowOverhang[axis], axis=axis, redraw=False)
        self.setAxisArrow(state=self.arrowVisible[axis], axis=axis, item='all', redraw=False, target=target)
        self.setAxisArrow(state=self.arrowEdgeShow[axis], axis=axis, item='edge', redraw=False, target=target)
        self.setAxisArrow(state=self.arrowFillShow[axis], axis=axis, item='fill', redraw=False, target=target)
  
      # set color and size of ticks
      self.toggleTicksLabel(value=self.ticksXShow, axis='x', redraw=False, target=target)
      self.setTickLabelColor(value = self.ticksXColor, axis = 'x', redraw = False, target=target)
      self.setTickLabelSize(value = self.ticksXSize, axis = 'x', redraw = False, target=target)
      self.setTickLabelBold(value = self.ticksXWeight, axis = 'x', redraw = False, target=target)
      self.setTickLabelItalic(value = self.ticksXStyle, axis = 'x', redraw = False, target=target)
      self.setTickLabelAngle(value = self.ticksXAngle, axis = 'x', redraw = False, target=target)
      self.setTickLabelAlignment(value = self.ticksXAlignment, axis = 'x', redraw = False, target=target)
      self.setTickLabelAlignmentVertical(value = self.ticksXAlignmentVertical, axis = 'x', redraw = False, target=target)
      self.setTickLabelPad(value = self.ticksXPad, axis = 'x', redraw = False, target=target)
      self.setTickLabelPad2(value = self.ticksXPad2, axis = 'x', redraw = False, target=target)
      self.setTickFont(value = self.tickFont['x'], axis = 'x', redraw = False, target=target)
      ###self.setTickFormat(axis='x', value=self.ticksXFormat, redraw=False, target=target)
      ### do we need to call tick formatting at all? (will be updated on draw time)

      if(target == 'plot'):
        self.toggleTicksLabel(value=self.ticksYShow, axis='y', redraw=False, target=target)
      else:
        self.toggleTicksLabel(value=self.ticksResidYShow, axis='y', redraw=False, target=target)
      self.setTickLabelColor(value = self.ticksYColor, axis = 'y', redraw = False, target=target)
      self.setTickLabelSize(value = self.ticksYSize, axis = 'y', redraw = False, target=target)
      self.setTickLabelBold(value = self.ticksYWeight, axis = 'y', redraw = False, target=target)
      self.setTickLabelItalic(value = self.ticksYStyle, axis = 'y', redraw = False, target=target)
      self.setTickLabelAngle(value = self.ticksYAngle, axis = 'y', redraw = False, target=target)
      self.setTickLabelAlignment(value = self.ticksYAlignment, axis = 'y', redraw = False, target=target)
      self.setTickLabelAlignmentVertical(value = self.ticksYAlignmentVertical, axis = 'y', redraw = False, target=target)
      self.setTickLabelPad(value = self.ticksYPad, axis = 'y', redraw = False, target=target)
      self.setTickLabelPad2(value = self.ticksYPad2, axis = 'y', redraw = False, target=target)
      self.setTickFont(value = self.tickFont['y'], axis = 'y', redraw = False, target=target)
      if(target == 'plot'):
        self.toggleTicksLabel(value=self.ticksY2Show, axis='y2', redraw=False, target=target)
        self.setTickLabelColor(value = self.ticksY2Color, axis = 'y2', redraw = False, target=target)
        self.setTickLabelSize(value = self.ticksY2Size, axis = 'y2', redraw = False, target=target)
        self.setTickLabelBold(value = self.ticksY2Weight, axis = 'y2', redraw = False, target=target)
        self.setTickLabelItalic(value = self.ticksY2Style, axis = 'y2', redraw = False, target=target)
        self.setTickLabelAngle(value = self.ticksY2Angle, axis = 'y2', redraw = False, target=target)
        self.setTickLabelAlignment(value = self.ticksY2Alignment, axis = 'y2', redraw = False, target=target)
        self.setTickLabelAlignmentVertical(value = self.ticksY2AlignmentVertical, axis = 'y2', redraw = False, target=target)
        self.setTickLabelPad(value = self.ticksY2Pad, axis = 'y2', redraw = False, target=target)
        self.setTickLabelPad2(value = self.ticksY2Pad2, axis = 'y2', redraw = False, target=target)
        self.setTickFont(value = self.tickFont['y2'], axis = 'y2', redraw = False, target=target)
  
      # set tick properties
      if(target == 'plot'):
        validItems = ['left', 'right', 'top', 'bottom', 'left2', 'right2']
      else:
        validItems = ['left', 'right', 'top', 'bottom']
      for key in self.ticksVisible:
        if(key in validItems):
         self.setTickMarkVisibility(value = self.ticksVisible[key], axis = key, redraw = False, target=target, initMode=True)
      for key in self.ticksWidth:
        if(key in validItems):
          self.setTickMarkWidth(value = self.ticksWidth[key], axis = key, redraw = False, target=target)
      for key in self.ticksLength:
        if(key in validItems):
          self.setTickMarkLength(value = self.ticksLength[key], axis = key, redraw = False, target=target, forceRedraw=False)
      for key in self.ticksColor:
        if(key in validItems):
          self.setTickMarkColor(value = self.ticksColor[key], axis = key, redraw = False, target=target)
      for key in self.ticksDirection:
        if(key in validItems):
          self.setTickMarkDirection(value = self.ticksDirection[key], axis = key, redraw = False, target=target)
      for key in self.ticksLabelShow:
        if(key in validItems):
          self.setTickMarkLabelShow(value = self.ticksLabelShow[key], axis = key, redraw = False, target=target)

      # set grid properties
      for key in self.gridVisible:
         self.setGridVisibility(value = self.gridVisible[key], axis = key, redraw = False, target=target)
      for key in self.gridWidth:
        self.setGridWidth(value = self.gridWidth[key], axis = key, redraw = False, target=target)
      for key in self.gridStyle:
        self.setGridStyle(value = self.gridStyle[key], axis = key, redraw = False, target=target)
      for key in self.gridDashStyle:
        self.setGridDashStyle(value = self.gridDashStyle[key], axis = key, redraw = False, target=target)
      for key in self.gridColor:
        self.setGridColor(value = self.gridColor[key], axis = key, redraw = False, target=target)
      for key in self.gridOrder:
        self.setGridOrder(value = self.gridOrder[key], axis = key, redraw = False, target=target)
      # set minor grid properties
      for key in self.gridMinorVisible:
         self.setGridMinorVisibility(value = self.gridMinorVisible[key], axis = key, redraw = False, target=target)
      for key in self.gridMinorWidth:
        self.setGridMinorWidth(value = self.gridMinorWidth[key], axis = key, redraw = False, target=target)
      for key in self.gridMinorStyle:
        self.setGridMinorStyle(value = self.gridMinorStyle[key], axis = key, redraw = False, target=target)
      for key in self.gridMinorDashStyle:
        self.setGridMinorDashStyle(value = self.gridMinorDashStyle[key], axis = key, redraw = False, target=target)
      for key in self.gridMinorColor:
        self.setGridMinorColor(value = self.gridMinorColor[key], axis = key, redraw = False, target=target)
      for key in self.gridMinorOrder:
        self.setGridMinorOrder(value = self.gridMinorOrder[key], axis = key, redraw = False, target=target)

      # canvas color etc.
      if(self.splitY):
        # activate patch background
        self.ax.patch.set_visible(True)
        self.ax_div.patch.set_visible(True)
      else:
        self.ax.patch.set_visible(False)
        self.ax_div.patch.set_visible(False)
        
      # deal with the innner situation
      self.updateInnerYSituation()
      self.setCanvasColor(value=self.canvasColor, redraw=False, target=target)
      self.toggleCanvasFill(value=self.canvasFill, redraw=False, target=target)
      self.toggleFigureFill(value=self.figureFill, redraw=False, target=target, silent=True)
      self.toggleFrameDraw(value=self.frameDraw, redraw=False, target=target)
      self.setFigureColor(value=self.figureColor, redraw=False, target=target, silent=True)
      self.setCanvasGradientStyle(value=self.canvasGradientStyle, redraw=False, target=target)
      self.setCanvasGradientColor(value=self.canvasGradientColor1, redraw=False, target=target, color=0)
      self.setCanvasGradientColor(value=self.canvasGradientColor2, redraw=False, target=target, color=1)
      self.setCanvasGradientCenter(value=self.canvasGradientCenter[0], redraw=False, target=target, axis='x')
      self.setCanvasGradientCenter(value=self.canvasGradientCenter[1], redraw=False, target=target, axis='y')
      self.setCanvasGradientWidth(value=self.canvasGradientWidth, redraw=False, target=target)
      self.setCanvasGradientAngle(value=self.canvasGradientAngle, redraw=False, target=target)
      self.setCanvasGradient(state=self.canvasGradientVisible, redraw=False, target=target)
      
      # set padding
      self.setPadding(value=self.padSize['bottom'], axis='bottom', redraw=False, target=target)
      
      # frame
      self.setFrameColor(value=self.frameColor, redraw=False, target=target)
      self.setFrameWidth(value=self.frameWidth, redraw=False, target=target)
      self.setFrameStyle(value=self.frameStyle, redraw=False, target=target)
      self.setFrameDashStyle(value=self.frameDashStyle, redraw=False, target=target)

    # slaved x axis controls
    self.toggleSlavedX(state=self.slavedX, redraw=False)
    self.toggleSlavedXLabel(state=self.slavedXLabelShow, redraw=False)
    self.changeSlavedXLabel(value=self.slavedXLabel, redraw=False)

    # now restore visibility of tick labels
    self.toggleTicksLabel(value=restoreXShow, axis='x', redraw=False, target='plot')
    self.toggleTicksLabel(value=restoreYShow, axis='y', redraw=False, target='plot')
    self.toggleTicksLabel(value=restoreY2Show, axis='y2', redraw=False, target='plot')
    self.toggleTicksLabel(value=restoreXShow, axis='x', redraw=False, target='resid')
    self.toggleTicksLabel(value=restoreResidYShow, axis='y', redraw=False, target='resid')

    # the inner situation
    self.changeSplitFraction(value=self.splitFraction, redraw=False)
    self.changeSplitPad(value=self.splitPad, redraw=False)
    self.setInnerParameter(param='axes', state=self.innerAxes, redraw=False)
    self.setInnerParameter(param='ticks', state=self.innerTicks, redraw=False)
    for target in ['plot', 'resid']:
      self.toggleTicksLabel(value=self.ticksXShow_div, axis='x2', redraw=False, target=target)

    # xkcd etc.
    self.xkcdify(state=self.xkcd, redraw=False)
    self.setPathEffects(redraw=False)
    
    # deal with axis ticks
    if(self.ticksXAuto):
      self.setAutoTicks(axis='x', redraw=False, target='plot')
      self.setAutoTicks(axis='x', redraw=False, target='resid')
    else:
      ticksXLabel = self.ticksXLabel
      self.setAxisTicks(value=self.ticksX, axis='x', redraw=False, target='plot')
      self.setAxisTicks(value=self.ticksX, axis='x', redraw=False, target='resid')
      # apply axis labels?
      self.ticksXLabel = ticksXLabel
      if(len(self.ticksXLabel) and (len(self.ticksXLabel) == len(self.ticksX))):
        for axisobject in [self.ax, self.ax_resid]:
          axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksX))
          axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksXLabel))
    if(self.ticksXAuto_div):
      self.setAutoTicks(axis='x2', redraw=False, target='plot')
      self.setAutoTicks(axis='x2', redraw=False, target='resid')
    else:
      ticksXLabel = self.ticksXLabel_div
      self.setAxisTicks(value=self.ticksX_div, axis='x2', redraw=False, target='plot')
      self.setAxisTicks(value=self.ticksX_div, axis='x2', redraw=False, target='resid')
      # apply axis labels?
      self.ticksXLabel_div = ticksXLabel
      if(len(self.ticksXLabel_div) and (len(self.ticksXLabel_div) == len(self.ticksX_div))):
        for axisobject in [self.ax2_div, self.ax_resid_div]:
          axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksX_div))
          axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksXLabel_div))
      
    if(self.ticksYAuto):
      self.setAutoTicks(axis='y', redraw=False, target='plot')
    else:
      ticksYLabel = self.ticksYLabel
      self.setAxisTicks(value=self.ticksY, axis='y', redraw=False, target='plot')
      # apply axis labels?
      self.ticksYLabel = ticksYLabel
      if(len(self.ticksYLabel) and (len(self.ticksYLabel) == len(self.ticksY))):
        for axisobject in [self.ax]:
          axisobject.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksY))
          axisobject.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksYLabel))
      
    if(self.ticksY2Auto):
      self.setAutoTicks(axis='y2', redraw=False, target='plot')
    else:
      ticksY2Label = self.ticksY2Label
      self.setAxisTicks(value=self.ticksY2, axis='y2', redraw=False, target='plot')
      # apply axis labels?
      self.ticksY2Label = ticksY2Label
      if(len(self.ticksY2Label) and (len(self.ticksY2Label) == len(self.ticksY2))):
        for axisobject in [self.ax2]:
          axisobject.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.ticksY2))
          axisobject.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.ticksY2Label))
      
    if(self.ticksResidYAuto):
      self.setAutoTicks(axis='resid', redraw=False, target='resid')
    else:
      self.setAxisTicks(value=self.ticksResidY, axis='resid', redraw=False, target='resid')

    # retrieve axis ticks
    self.ticksX = self.getAxisTicks(axis='x')
    self.ticksY = self.getAxisTicks(axis='y')
    self.ticksY2 = self.getAxisTicks(axis='y2')
    self.ticksResidY = self.getAxisTicks(axis='resid')
    
    # issue call to setTickFormatHelper for slaved x axis to get its formatting right when processing style sheet
    self.setTickFormatHelper(axis='xs', redraw=False)
    self.setTickFormatHelper(axis='residxs', redraw=False)

    # issue plot redraw
    # the follwing call not needed as we will draw a curve afterwards
    self.handleResidZero = self.plotResidZero(self.handleResidZero, redraw=False)
    self.handleResidZero_div = self.plotResidZero(self.handleResidZero_div, redraw=False, splitX=True)
    self.setAxisLimits(lower=self.minResidY, upper=self.maxResidY, axis='y', updateLabel=False, target='resid', redraw=initialize, updateGrid=True)
    self.updateTickEntryField(axis='y')

  def pickHandlerResid(self, event):
    # handles click events in resid plot
    if((event.button == 1) and event.dblclick):
      # first check for double click on certain plot items
      searchItems = {}
      searchItems['xlabel'] = self.ax_resid.xaxis.label
      searchItems['ylabel'] = self.ax_resid.yaxis.label
      # need to expand for slaved second x axis
      if(self.handleSlavedXLabelResid != None):
        searchItems['slavedxlabel'] = self.handleSlavedXLabelResid

      # need to expand for slaved second x axis
      # these should come first b/c otherwise the regular upper labels win out
      for number, label in enumerate(self.secondXLabelsResid):
        searchItems['labliSlavedX_' + str(number)] = label
      for number, label in enumerate(self.secondXLabelsResid_div):
        searchItems['labliSlavedX2_' + str(number)] = label

      labli = self.ax_resid.get_xticklabels(which='both')
      for number, label in enumerate(labli):
        searchItems['labliX_' + str(number)] = label
      labli = self.ax_resid.get_yticklabels(which='both')
      for number, label in enumerate(labli):
        searchItems['labliResid_' + str(number)] = label
      if(self.splitShow):
        labli = self.ax_resid_div.get_xticklabels(which='both')
        for number, label in enumerate(labli):
          searchItems['labliX2_' + str(number)] = label

      # are split axes active?
      if(self.splitShow):
        if(self.handleDividerResid != None):
          for number, item in enumerate(self.handleDividerResid):
            searchItems['dividerX_' + str(number)] = item

      # need to expand for slaved second x axis
      # these should come first b/c otherwise the regular upper ticks win out
      for number, tick in enumerate(self.secondXLinesResid):
        searchItems['tickliSlavedX_' + str(number)] = tick
      for number, tick in enumerate(self.secondXLinesResid_div):
        searchItems['tickliSlavedX2_' + str(number)] = tick

      tickli = self.ax_resid.xaxis.get_ticklines() + self.ax_resid.xaxis.get_minorticklines()
      for number, tick in enumerate(tickli):
        searchItems['tickliX_' + str(number)] = tick
      if(self.splitShow):
        tickli = self.ax_resid_div.xaxis.get_ticklines() + self.ax_resid_div.xaxis.get_minorticklines()
        for number, tick in enumerate(tickli):
          searchItems['tickliX2_' + str(number)] = tick
      tickli = self.ax_resid.yaxis.get_ticklines() + self.ax_resid.yaxis.get_minorticklines()
      for number, tick in enumerate(tickli):
        searchItems['tickliY_' + str(number)] = tick

      axes = ['x', 'y']
      if(self.splitShow):
        axes += ['x2']
      for axis in axes:
        for number, line in enumerate(self.gridLinesStore_resid[axis]):
          if(self.gridOrder_resid[axis] == 'front'):
            searchItems['gridli' + axis.upper() + '_' + str(number)] = line
      for axis in axes:
        for number, line in enumerate(self.gridMinorLinesStore_resid[axis]):
          if(self.gridMinorOrder_resid[axis] == 'front'):
            searchItems['minorGridli' + axis.upper() + '_' + str(number)] = line

      # somehow axis spines don't respond to .contains() => work around via canvas click
      ###searchItems['spine2'] = self.ax2.spines['bottom']
      ###searchItems['spine'] = self.ax.spines['top']
      ###searchItems['canvas'] = self.ax2.patch
      
      fallThrough = True
      for itemType, item in searchItems.items():
        if(item.contains(event)[0]):
          fallThrough = False
          self.menu = ConfigPlotMenu(self, item=item, itemType=itemType)
          
      # now need an extra check to decide between axis spines (which don't react to .contains) and the canvas
      if(fallThrough):
        figX, figY = self.residplot.get_figwidth() * self.residplot.dpi, self.residplot.get_figheight() * self.residplot.dpi
        relX, relY = event.x / figX, (figY - event.y) / figY
        tolerance = 0.02
        # check closeness to axis spines
        if(abs(relX - self.padSize['left']) <= tolerance):
          self.menu = ConfigPlotMenu(self, item=self.ax_resid.spines['left'], itemType='spine_left')
          fallThrough = False
        elif(abs(relX - self.padSize['right']) <= tolerance):
          self.menu = ConfigPlotMenu(self, item=self.ax_resid.spines['right'], itemType='spine_right')
          fallThrough = False
        elif(abs(relY - 1 + self.padSize['bottom']) <= tolerance):
          self.menu = ConfigPlotMenu(self, item=self.ax_resid.spines['bottom'], itemType='spine_bottom')
          fallThrough = False
        elif(abs(relY - 1 + self.padSize['top']) <= tolerance):
          self.menu = ConfigPlotMenu(self, item=self.ax_resid.spines['top'], itemType='spine_top')
          fallThrough = False
        # implement checks for split x axis
        if(self.splitShow and fallThrough):
          xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
          if((abs(relX - xbreak - self.padSize['left']) <= tolerance) and (relY <= 1 - self.padSize['bottom']) and (relY >= 1 - self.padSize['top'])):
            self.menu = ConfigPlotMenu(self, item=None, itemType='splitX')
            fallThrough = False
          elif((abs(relX - xbreak - self.padSize['left'] - self.splitPad) <= tolerance) and (relY <= 1 - self.padSize['bottom']) and (relY >= 1 - self.padSize['top'])):
            self.menu = ConfigPlotMenu(self, item=None, itemType='splitX')
            fallThrough = False

      # hit on a graphics item, so should format config menu
      if(not fallThrough):
        needStyling = True
      else:
        needStyling = False
        # compile list of z-ordered items
        zOrderedItems = {}
        for index, entry in enumerate(self.parent.data):
          if((hasattr(entry, 'visibilityResid')) and entry.visibilityResid):
            zOrderedItems[entry.zorderResid] = ['resid', index]
        if(self.visibilityResidLine):
          zOrderedItems[self.zorderResidLine] = ['residLine', 0]
        # now check for click in descending z order
        zKeys = sorted(list(zOrderedItems.keys()))
        currIndex = len(zKeys) - 1
        while(currIndex >= 0):
          currKey = zKeys[currIndex]
          currItem = zOrderedItems[currKey]
          if(currItem[0] == 'resid'):
            relevantHandles = ['handleResid', 'handleResidStack', 'handleResidStackNeg', 'handleResid_div', 'handleResidStack_div', 'handleResidStackNeg_div']
            for handle in relevantHandles:
              if((self.parent.data[currItem[1]].__dict__[handle] != None) and (self.parent.data[currItem[1]].__dict__[handle].contains(event)[0])):
                # found an item that has been clicked on
                currIndex = -1
            # handle bars extra
            for handle in ['handleResidBar', 'handleResidBar_div']:
              if(self.parent.data[currItem[1]].__dict__[handle] != None):
                for patch in self.parent.data[currItem[1]].__dict__[handle].patches:
                  if(patch.contains(event)[0]):
                    # found an item that has been clicked on
                    currIndex = -1
            # did we hit the item?
            if(currIndex == -1):
              self.parent.objectsarea.changeStyle(self.parent.data[currItem[1]], False, True, callButton=None)
          elif(currItem[0] == 'residLine'):
            for handle in ['handleResidZero', 'handleResidZero_div']:
              if((self.__dict__[handle] != None) and (self.__dict__[handle].contains(event)[0])):
                # found an item that has been clicked on
                currIndex = -1
                
            if(currIndex == -1):
              self.parent.objectsarea.changeResidZeroStyle(callButton=None)

          if(currIndex == -1):
            # we hit an item, so don't fall through
            fallThrough = False
    
          # advance item
          currIndex -= 1

      # if still no item found, check for click on plot items
      if(fallThrough):
        searchItems = {}
        axes = ['x', 'y']
        if(self.splitShow):
          axes += ['x2']
        for axis in axes:
          for number, line in enumerate(self.gridLinesStore_resid[axis]):
            if(self.gridOrder_resid[axis] == 'back'):
              searchItems['gridli' + axis.upper() + '_' + str(number)] = line
        for axis in axes:
          for number, line in enumerate(self.gridMinorLinesStore_resid[axis]):
            if(self.gridMinorOrder_resid[axis] == 'back'):
              searchItems['minorGridli' + axis.upper() + '_' + str(number)] = line
  
        for itemType, item in searchItems.items():
          if(item.contains(event)[0]):
            fallThrough = False
            self.menu = ConfigPlotMenu(self, item=item, itemType=itemType)
        
        if(fallThrough):
          self.menu = ConfigPlotMenu(self, item=self.ax_resid.patch, itemType='canvas')
        needStyling = True
        
      # is there a context menu that would require styling?
      if(needStyling):
        # apply styles to popup window
        if(QSTYLE != None):
          self.menu.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.menu.setStyleSheet(QSTYLESHEET)
        # first need to display QMenu to get reliable size (even sizeHint fails)
        menuPos = QtGui.QCursor.pos()
        self.menu.popup(menuPos)
        # now move window to new position
        menuX = menuPos.x() - self.menu.width() / 2
        menuX = max(menuX, 0)
        menuPos.setX(int(menuX))
        self.menu.move(menuPos)

  def dynamicLegend(self, deltaX, deltaY, pickedAxes, pickedBackground, transient=True):
    # function called by interactive drag and resize
    if((pickedAxes != None) and (pickedBackground != None)):
      renderer = self.parent.plotArea.matplot.canvas.renderer
      
      # restore background
      pickedAxes.figure.canvas.restore_region(pickedBackground)

      # update object
      if(hasattr(self.legendHandle, 'draw')):
        self.legendHandle.set_loc((deltaX, deltaY))
        self.legendHandle.draw(renderer)
    
      # and blit just the redrawn area
      pickedAxes.figure.canvas.blit(pickedAxes.bbox)
      
      # accept these changes?
      if(not transient):
        self.legendX, self.legendY = deltaX, deltaY
        # update dialog
        self.parent.graphicsarea.configLegendX.setText(self.parent.formatNumber(self.legendX))
        self.parent.graphicsarea.configLegendY.setText(self.parent.formatNumber(self.legendY))
        
        # issue a refresh to accurately deal with cursor
        self.dataplotwidget.myRefresh()

  def releaseHandler(self, event):
    # called to turn off extra picking
    if(self.pickedLegend and (self.startX != None) and (self.startY != None)):
      width, height = self.matplot.get_size_inches() * self.matplot.dpi
      xdata, ydata = (event.x - self.startX + self.legendOrigX) / width, (event.y - self.startY + self.legendOrigY) / height
      self.dynamicLegend(xdata, ydata, pickedAxes=self.pickedAxes, pickedBackground=self.pickedBackground, transient=False)
    elif(self.pickedExtra != None):
      if(event.inaxes == self.inAxes):
        # map to correct axis
        xdata, ydata = self.pickedAxes.transData.inverted().transform((event.x, event.y))
        xdata, ydata = xdata - self.startX, ydata - self.startY
      else:
        xdata, ydata = 0.0, 0.0
      self.parent.extras[self.pickedExtra].updateMe(xdata, ydata, pickedAxes=self.pickedAxes,\
                        pickedBackground=self.pickedBackground, pickedMode=self.pickedMode,\
                        quadrantX=self.quadrantX, quadrantY=self.quadrantY, transient=False)
      if(self.splitY and self.parent.extras[self.pickedExtra].onBothAxes):
        self.parent.extras[self.pickedExtra].duplicateForSplit()
      self.parent.extras[self.pickedExtra].drawMe(redraw=True)
    self.pickedExtra, self.pickedAxes, self.pickedBackground, self.pickedMode, self.inAxes = None, None, None, 0, None
    self.pickedLegend = False
    # do we need to restore a rectangle selector?
    if(self.selector2Restore != None):
      self.selector2Restore.set_active(True)
      self.selector2Restore = None
    # do we need to restore cursors?
    for cursor in self.cursors2Restore:
      cursor.visible = True
      cursor.linev.set_visible(True)
      cursor.lineh.set_visible(True)
      cursor.label.set_visible(True)
    self.cursors2Restore = []

  def moveHandler(self, event):
    # called to allow moving and resizing of extras
    if(self.pickedLegend and (self.startX != None) and (self.startY != None)):
      width, height = self.matplot.get_size_inches() * self.matplot.dpi
      xdata, ydata = (event.x - self.startX + self.legendOrigX) / width, (event.y - self.startY + self.legendOrigY) / height
      self.dynamicLegend(xdata, ydata, pickedAxes=self.pickedAxes, pickedBackground=self.pickedBackground, transient=True)
    elif(self.pickedExtra != None):
      if(event.inaxes == self.inAxes):
        # map to correct axis
        xdata, ydata = self.pickedAxes.transData.inverted().transform((event.x, event.y))
        xdata, ydata = xdata - self.startX, ydata - self.startY
        self.parent.extras[self.pickedExtra].updateMe(xdata, ydata, pickedAxes=self.pickedAxes,\
                          pickedBackground=self.pickedBackground, pickedMode=self.pickedMode,\
                          quadrantX=self.quadrantX, quadrantY=self.quadrantY, transient=True)

  def toggleCrossHairEncore(self, event):
    # combined handler for all click events
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    # need to save event modifiers at time of click to get handlers right
    self.toggleShift, self.toggleCtrl = modifiers & QtCore.Qt.KeyboardModifier.ShiftModifier, modifiers & QtCore.Qt.KeyboardModifier.ControlModifier
    # determine which axis we are on
    inaxes = event.inaxes
    # and then on with all the rest
    fallThrough = False
    if(((modifiers & QtCore.Qt.KeyboardModifier.ControlModifier) or (modifiers & QtCore.Qt.KeyboardModifier.ShiftModifier) or self.drawFreehand) and (event.button == 1)):
      # determine mode
      if(self.drawFreehand):
        # implement check that a valid axes has been selected
        if(inaxes in [self.ax, self.ax2, self.ax_div, self.ax2_div]):
          # for myterious reasons, older Python version under Linux does not correctly assign pickedMode (it reverts to zero!)
          # => go via extra flag pick2Freehand
          self.pickedMode, self.pick2Freehand = 3, True
          # switch to ad hoc lasso to avoid shenanigans with the rectangle selector
          self.lassoSelector = MyLassoli(ax=inaxes, xy=(event.xdata, event.ydata), callback=self.lassoCallback, useblit=True, parent=self)
          self.lassoSelector.line.set_color([0.2, 0.9, 0.15, 1.0])
      else:
        if((modifiers & QtCore.Qt.KeyboardModifier.ControlModifier) and (modifiers & QtCore.Qt.KeyboardModifier.ShiftModifier)):
          # Ctrl/shift for rotation
          self.pickedMode = 2
        elif(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier):
          # Ctrl for resizing
          self.pickedMode = 1
        else:
          # shift for moving
          self.pickedMode = 0
          # check whether click was on legend
          if(hasattr(self.legendHandle, 'contains')):
            if(self.legendHandle.contains(event)[0]):
              self.pickedLegend = True
              
              # save canvas for blitting
              if(inaxes == None):
                # default to self.ax to enable moving legend outside of canvas
                self.pickedAxes = self.ax
              else:
                self.pickedAxes = inaxes
                
              # save canvas for blitting
              self.pickedBackground = self.pickedAxes.figure.canvas.copy_from_bbox(self.pickedAxes.bbox)
              
              # find where legend is currently displayed
              # still somewhat buggy for 'best' legend placement, reason unclear
              bbox = self.legendHandle.get_tightbbox()
              self.legendOrigX, self.legendOrigY = bbox.x0, bbox.y0
              self.startX, self.startY = event.x, event.y
              
              # finally, toggle legend and menu entry to custom location
              if(self.legendPlacement != 'custom'):
                self.legendPlacement = 'custom'
                self.parent.graphicsarea.configLegendPlacement.setCurrentText('custom')
                self.parent.statusbar.showMessage('Switched to custom placement of legend.', self.parent.STATUS_TIME, color='blue')

              # if cursor active, temporarily disable it, to avoid blitting catastrophe
              if(self.cursorVisible):
                # toggle visibility (in correct order to get display of twins right)
                order = [self.cursor2_div, self.cursor2, self.cursor_div, self.cursor]
                
                for cursor in order:
                  if((cursor != None) and cursor.visible):
                    self.cursors2Restore.append(cursor)
                    cursor.visible = False
                    cursor.linev.set_visible(False)
                    cursor.lineh.set_visible(False)
                    cursor.label.set_visible(False)
          else:
            # for good measure, reinitialize a bunch of values that would be used in move and release handlers
            self.legendOrigX, self.legendOrigY, self.startX, self.startY = None, None, None, None
        if(not self.pickedLegend):
          # compile list of z-ordered extras
          zOrderedExtras = {}
          for index, entry in enumerate(self.parent.extras):
            if((hasattr(entry, 'visibility')) and entry.visibility):
              zOrderedExtras[entry.zorder] = index
          # now check for click in descending z order
          zKeys = sorted(list(zOrderedExtras.keys()))
          currIndex = len(zKeys) - 1
          while(currIndex >= 0):
            currKey = zKeys[currIndex]
            currItem = zOrderedExtras[currKey]          
    
            relevantHandles = ['handle', 'handle_div']
            # check for items on splitY
            if(self.splitY):
              # determine whether the event originated from the same axis as the object is on
              flag = False
              if((inaxes in [self.ax, self.ax_div]) and (not self.parent.extras[currItem].onSecondAxes)):
                flag = True
              if((inaxes in [self.ax2, self.ax2_div]) and self.parent.extras[currItem].onSecondAxes):
                flag = True
            else:
              flag = True
    
            check = False
            for handle in relevantHandles:
              if(flag):
                if((self.parent.extras[currItem].__dict__[handle] != None) and (self.parent.extras[currItem].__dict__[handle].contains(event)[0]) and (currIndex != -1)):
                  # found an item that has been clicked on - break out of loop
                  currIndex = -1
                  self.pickedExtra = currItem
              elif((handle in self.parent.extras[currItem].handlesBothAxes) and (self.parent.extras[currItem].handlesBothAxes[handle] != None) and (self.parent.extras[currItem].handlesBothAxes[handle].contains(event)[0]) and (currIndex != -1)):
                # found an item that has been clicked on - break out of loop
                currIndex = -1
                self.pickedExtra = currItem
                  
              if((currIndex == -1) and (not check)):
                check = True
                # determine which axes to draw on
                self.pickedAxes = inaxes
                self.inAxes = inaxes
                  
                # save canvas for blitting
                self.pickedBackground = self.pickedAxes.figure.canvas.copy_from_bbox(self.pickedAxes.bbox)
    
                # map to correct axis
                self.startX, self.startY = self.pickedAxes.transData.inverted().transform((event.x, event.y))
                # determine quadrant on which extra was clicked
                if(self.startX < self.parent.extras[self.pickedExtra].x):# + 0.5 * self.parent.extras[self.pickedExtra].x2)):
                  self.quadrantX = 'left'
                else:
                  self.quadrantX = 'right'
                if(self.startY < self.parent.extras[self.pickedExtra].y):# + 0.5 * self.parent.extras[self.pickedExtra].y2)):
                  self.quadrantY = 'bottom'
                else:
                  self.quadrantY = 'top'
                
                # temporarily make extra semi-transparent
                self.parent.extras[self.pickedExtra].tempTransparency()
                #print(self.parent.extras[self.pickedExtra].x, self.parent.extras[self.pickedExtra].y, self.parent.extras[self.pickedExtra].x2, self.parent.extras[self.pickedExtra].y2)
                
                # if cursor active, temporarily disable it, to avoid blitting catastrophe
                if(self.cursorVisible):
                  # toggle visibility (in correct order to get display of twins right)
                  order = [self.cursor2_div, self.cursor2, self.cursor_div, self.cursor]
                  
                  for cursor in order:
                    if((cursor != None) and cursor.visible):
                      self.cursors2Restore.append(cursor)
                      cursor.visible = False
                      cursor.linev.set_visible(False)
                      cursor.lineh.set_visible(False)
                      cursor.label.set_visible(False)
    
            # advance item
            currIndex -= 1
            
          # now check whether an extra has been picked
          # if no, we can launch our lassoli
          if((currIndex == -1) and (modifiers & QtCore.Qt.KeyboardModifier.ShiftModifier)):
            # implement check that a valid axes has been selected
            if(inaxes in [self.ax, self.ax2, self.ax_div, self.ax2_div]):
              # switch to ad hoc lasso to avoid shenanigans with the rectangle selector
              self.lassoSelector = MyLassoli(ax=inaxes, xy=(event.xdata, event.ydata), callback=self.lassoCallback, useblit=True, parent=self)
          
    elif((event.button == 1) and not event.dblclick):
      # implement check that a valid axes has been selected
      if(inaxes in [self.ax, self.ax2, self.ax_div, self.ax2_div]):
        self.rectangleSelector = MyRectangli(ax=inaxes, xy=(event.xdata, event.ydata), callback=self.rectangleCallback, useblit=True, parent=self)

    elif(event.button == 3):
      # right mouse button => unzoom
      if(len(self.storeCoord) > 0):
        coords = self.storeCoord[-8:]
        self.storeCoord = self.storeCoord[:-8]

        # if cursor active, temporarily disable it, lest new matplotlib slows down immensely
        if(self.cursorVisible):
          # toggle visibility (in correct order to get display of twins right)
          order = [self.cursor2_div, self.cursor2, self.cursor_div, self.cursor]
          
          for cursor in order:
            if((cursor != None) and cursor.visible):
              self.cursors2Restore.append(cursor)
              if(True):
                cursor.visible = False
                cursor.linev.set_visible(False)
                cursor.lineh.set_visible(False)
                cursor.label.set_visible(False)
        
        # set axes limits if required
        self.rectSelectorCallbackHelper(coords=coords, divAxis=False, restore=True)
        
    elif((event.button == 1) and event.dblclick):
      fallThrough = True
      # should first check for certain display items (because curves somehow hog click events even when not under cursor)
      searchItems = {}
      searchItems['xlabel'] = self.ax2.xaxis.label
      searchItems['ylabel'] = self.ax.yaxis.label
      if(self.isSecondAxesActive()):
        searchItems['ylabel2'] = self.ax2.yaxis.label
      # need to expand for slaved second x axis
      searchItems['slavedxlabel'] = self.handleSlavedXLabel
      searchItems['legend'] = self.legendHandle

      # need to expand for slaved second x axis
      # these should come first b/c otherwise the regular upper labels win out
      for number, label in enumerate(self.secondXLabels):
        searchItems['labliSlavedX_' + str(number)] = label
      for number, label in enumerate(self.secondXLabels_div):
        searchItems['labliSlavedX2_' + str(number)] = label

      labli = self.ax2.get_xticklabels(which='both')
      for number, label in enumerate(labli):
        searchItems['labliX_' + str(number)] = label
      if(self.splitShow):
        labli = self.ax2_div.get_xticklabels(which='both')
        for number, label in enumerate(labli):
          searchItems['labliX2_' + str(number)] = label
      labli = self.ax.get_yticklabels(which='both')
      for number, label in enumerate(labli):
        searchItems['labliY_' + str(number)] = label
      if(self.isSecondAxesActive()):
        labli = self.ax2.get_yticklabels(which='both')
        for number, label in enumerate(labli):
          searchItems['labliY2_' + str(number)] = label

      # are split axes active?
      if(self.splitShow):
        if(self.handleDivider != None):
          for number, item in enumerate(self.handleDivider):
            searchItems['dividerX_' + str(number)] = item
      if(self.splitY):
        if(self.handleDividerY != None):
          for number, item in enumerate(self.handleDividerY):
            searchItems['dividerY_' + str(number)] = item

      # need to expand for slaved second x axis
      # these should come first b/c otherwise the regular upper ticks win out
      for number, tick in enumerate(self.secondXLines):
        searchItems['tickliSlavedX_' + str(number)] = tick
      for number, tick in enumerate(self.secondXLines_div):
        searchItems['tickliSlavedX2_' + str(number)] = tick

      tickli = self.ax2.xaxis.get_ticklines() + self.ax2.xaxis.get_minorticklines()
      for number, tick in enumerate(tickli):
        searchItems['tickliX_' + str(number)] = tick
      if(self.splitShow):
        tickli = self.ax2_div.xaxis.get_ticklines() + self.ax2_div.xaxis.get_minorticklines()
        for number, tick in enumerate(tickli):
          searchItems['tickliX2_' + str(number)] = tick
      tickli = self.ax.yaxis.get_ticklines() + self.ax.yaxis.get_minorticklines()
      for number, tick in enumerate(tickli):
        searchItems['tickliY_' + str(number)] = tick
      if(self.isSecondAxesActive()):
        tickli = self.ax2.yaxis.get_ticklines() + self.ax2.yaxis.get_minorticklines()
        for number, tick in enumerate(tickli):
          searchItems['tickliY2_' + str(number)] = tick
          
      # need to check for gridOrder and gridMinorOrder => only consider grid lines here that are displayed in front
      axes = ['x', 'y']
      if(self.splitShow):
        axes += ['x2']
      if(self.isSecondAxesActive()):
        axes += ['y2']
      for axis in axes:
        for number, line in enumerate(self.gridLinesStore[axis]):
          if(self.gridOrder[axis] == 'front'):
            searchItems['gridli' + axis.upper() + '_' + str(number)] = line
      for axis in axes:
        for number, line in enumerate(self.gridMinorLinesStore[axis]):
          if(self.gridMinorOrder[axis] == 'front'):
            searchItems['minorGridli' + axis.upper() + '_' + str(number)] = line

      # somehow axis spines don't respond to .contains() => work around via canvas click
      ###searchItems['spine2'] = self.ax2.spines['bottom']
      ###searchItems['spine'] = self.ax.spines['top']
      ###searchItems['canvas'] = self.ax2.patch
      
      for itemType, item in searchItems.items():
        if(fallThrough and hasattr(item, 'contains') and item.contains(event)[0]):
          fallThrough = False
          self.menu = ConfigPlotMenu(self, item=item, itemType=itemType)
          
      # now need an extra check to decide between axis spines (which don't react to .contains) and the canvas
      if(fallThrough):
        figX, figY = self.matplot.get_figwidth() * self.matplot.dpi, self.matplot.get_figheight() * self.matplot.dpi
        relX, relY = event.x / figX, (figY - event.y) / figY
        tolerance = 0.02
        # check closeness to axis spines
        if(self.axisVisible['left'] and (abs(relX - self.padSize['left']) <= tolerance) and (relY <= 1 - self.padSize['bottom']) and (relY >= 1 - self.padSize['top'])):
          self.menu = ConfigPlotMenu(self, item=self.ax.spines['left'], itemType='spine_left')
          fallThrough = False
        elif(self.axisVisible['right'] and (abs(relX - self.padSize['right']) <= tolerance) and (relY <= 1 - self.padSize['bottom']) and (relY >= 1 - self.padSize['top'])):
          self.menu = ConfigPlotMenu(self, item=self.ax.spines['right'], itemType='spine_right')
          fallThrough = False
        elif(self.axisVisible['bottom'] and (abs(relY - 1 + self.padSize['bottom']) <= tolerance) and (relX >= self.padSize['left']) and (relX <= self.padSize['right'])):
          self.menu = ConfigPlotMenu(self, item=self.ax2.spines['bottom'], itemType='spine_bottom')
          fallThrough = False
        elif(self.axisVisible['top'] and (abs(relY - 1 + self.padSize['top']) <= tolerance) and (relX >= self.padSize['left']) and (relX <= self.padSize['right'])):
          self.menu = ConfigPlotMenu(self, item=self.ax2.spines['top'], itemType='spine_top')
          fallThrough = False
        # implement checks for split x and y axes
        if(self.splitShow and fallThrough):
          xbreak = (self.padSize['right'] - self.padSize['left'] - self.splitPad) * self.splitFraction / (1.0 + self.splitFraction)
          if((abs(relX - xbreak - self.padSize['left']) <= tolerance) and (relY <= 1 - self.padSize['bottom']) and (relY >= 1 - self.padSize['top'])):
            self.menu = ConfigPlotMenu(self, item=None, itemType='splitX')
            fallThrough = False
          elif((abs(relX - xbreak - self.padSize['left'] - self.splitPad) <= tolerance) and (relY <= 1 - self.padSize['bottom']) and (relY >= 1 - self.padSize['top'])):
            self.menu = ConfigPlotMenu(self, item=None, itemType='splitX')
            fallThrough = False
        if(self.splitY and fallThrough):
          ybreak = (self.padSize['top'] - self.padSize['bottom'] - self.splitYPad) * self.splitYFraction / (1.0 + self.splitYFraction)
          if((abs(1 - self.padSize['bottom'] - ybreak - relY) <= tolerance) and (relX >= self.padSize['left']) and (relX <= self.padSize['right'])):
            self.menu = ConfigPlotMenu(self, item=None, itemType='splitY')
            fallThrough = False
          elif((abs(1 - self.padSize['bottom'] - ybreak - self.splitYPad - relY) <= tolerance) and (relX >= self.padSize['left']) and (relX <= self.padSize['right'])):
            self.menu = ConfigPlotMenu(self, item=None, itemType='splitY')
            fallThrough = False

      # hit on a graphics item, so should format config menu
      if(not fallThrough):
        needStyling = True
      else:
        needStyling = False
        # check for click on plot items
        if(fallThrough):
          # compile list of z-ordered items
          zOrderedItems = {}
          for index, entry in enumerate(self.parent.fit):
            if((hasattr(entry, 'visibility')) and entry.visibility):
              zOrderedItems[entry.zorder] = ['fit', index]
          for index, entry in enumerate(self.parent.data):
            if((hasattr(entry, 'visibility')) and entry.visibility):
              zOrderedItems[entry.zorder] = ['data', index]
          for index, entry in enumerate(self.parent.extras):
            if((hasattr(entry, 'visibility')) and entry.visibility):
              zOrderedItems[entry.zorder] = ['extra', index]
          # now check for click in descending z order
          zKeys = sorted(list(zOrderedItems.keys()))
          currIndex = len(zKeys) - 1
          while(currIndex >= 0):
            currKey = zKeys[currIndex]
            currItem = zOrderedItems[currKey]
            if(currItem[0] == 'fit'):
              relevantHandles = ['handlePlot', 'handlePlot_div', 'handleBoot', 'handleBoot_div']
              # check for items on splitY
              if(self.splitY):
                # determine whether the event originated from the same axis as the object is on
                flag = False
                if((inaxes in [self.ax, self.ax_div]) and (not self.parent.fit[currItem[1]].onSecondAxes)):
                  flag = True
                if((inaxes in [self.ax2, self.ax2_div]) and self.parent.fit[currItem[1]].onSecondAxes):
                  flag = True
                # check whether we have item on both axes
                if(self.parent.fit[currItem[1]].onBothAxes and (not flag)):
                  for handle in relevantHandles:
                    if((handle in self.parent.fit[currItem[1]].handlesBothAxes) and (self.parent.fit[currItem[1]].handlesBothAxes[handle] != None) and (self.parent.fit[currItem[1]].handlesBothAxes[handle].contains(event)[0])):
                      # found an item that has been clicked on - break out of loop
                      currIndex = -1
              else:
                flag = True
              # normal click handling
              if(flag):
                for handle in relevantHandles:
                  if((self.parent.fit[currItem[1]].__dict__[handle] != None) and (self.parent.fit[currItem[1]].__dict__[handle].contains(event)[0])):
                    # found an item that has been clicked on - break out of loop
                    currIndex = -1
              if(currIndex == -1):
                self.parent.objectsarea.changeStyle(self.parent.fit[currItem[1]], False, False, callButton=None)
            elif(currItem[0] == 'data'):
              relevantHandles = ['handleData', 'handleStack', 'handleStackNeg', 'handleData_div', 'handleStack_div', 'handleStackNeg_div']
              # check for items on splitY
              if(self.splitY):
                # determine whether the event originated from the same axis as the object is on
                flag = False
                if((inaxes in [self.ax, self.ax_div]) and (not self.parent.data[currItem[1]].onSecondAxes)):
                  flag = True
                if((inaxes in [self.ax2, self.ax2_div]) and self.parent.data[currItem[1]].onSecondAxes):
                  flag = True
                flag2 = (not flag) and self.parent.data[currItem[1]].onBothAxes
              else:
                flag, flag2 = True, False
              # cycle through items
              for handle in relevantHandles:
                if(flag):
                  if((self.parent.data[currItem[1]].__dict__[handle] != None) and (self.parent.data[currItem[1]].__dict__[handle].contains(event)[0])):
                    # found an item that has been clicked on
                    currIndex = -1
                if(flag2):
                  if((handle in self.parent.data[currItem[1]].handlesBothAxes) and (self.parent.data[currItem[1]].handlesBothAxes[handle] != None) and (self.parent.data[currItem[1]].handlesBothAxes[handle].contains(event)[0])):
                    # found an item that has been clicked on - break out of loop
                    currIndex = -1
              # handle bars extra
              for handle in ['handleBar', 'handleBar_div']:
                if(flag):
                  if(self.parent.data[currItem[1]].__dict__[handle] != None):
                    for patch in self.parent.data[currItem[1]].__dict__[handle].patches:
                      if(patch.contains(event)[0]):
                        # found an item that has been clicked on
                        currIndex = -1
                if(flag2):
                  if((handle in self.parent.data[currItem[1]].handlesBothAxes) and (self.parent.data[currItem[1]].handlesBothAxes[handle] != None)):
                    for patch in self.parent.data[currItem[1]].handlesBothAxes[handle].patches:
                      if(patch.contains(event)[0]):
                        # found an item that has been clicked on
                        currIndex = -1
              # handle violins extra
              for handle in ['handleViolin', 'handleViolin_div']:
                if(flag):
                  if(self.parent.data[currItem[1]].__dict__[handle] != None):
                    for entry in self.parent.data[currItem[1]].__dict__[handle]:
                      if(type(self.parent.data[currItem[1]].__dict__[handle][entry]) == type([])):
                        for item in self.parent.data[currItem[1]].__dict__[handle][entry]:
                          if(item.contains(event)[0]):
                            # found an item that has been clicked on
                            currIndex = -1
                      else:
                        if(self.parent.data[currItem[1]].__dict__[handle][entry].contains(event)[0]):
                          # found an item that has been clicked on
                          currIndex = -1
                if(flag2):
                  if((handle in self.parent.data[currItem[1]].handlesBothAxes) and (self.parent.data[currItem[1]].handlesBothAxes[handle] != None)):
                    for entry in self.parent.data[currItem[1]].handlesBothAxes[handle]:
                      if(type(self.parent.data[currItem[1]].handlesBothAxes[handle][entry]) == type([])):
                        for item in self.parent.data[currItem[1]].handlesBothAxes[handle][entry]:
                          if(item.contains(event)[0]):
                            # found an item that has been clicked on
                            currIndex = -1
                      else:
                        if(self.parent.data[currItem[1]].handlesBothAxes[handle][entry].contains(event)[0]):
                          # found an item that has been clicked on
                          currIndex = -1
                        
              # did we hit the item?
              if(currIndex == -1):
                self.parent.objectsarea.changeStyle(self.parent.data[currItem[1]], True, False, callButton=None)
            elif(currItem[0] == 'extra'):
              relevantHandles = ['handle', 'handle_div']
              # check for items on splitY
              if(self.splitY):
                # determine whether the event originated from the same axis as the object is on
                flag = False
                if((inaxes in [self.ax, self.ax_div]) and (not self.parent.extras[currItem[1]].onSecondAxes)):
                  flag = True
                if((inaxes in [self.ax2, self.ax2_div]) and self.parent.extras[currItem[1]].onSecondAxes):
                  flag = True
                # check whether we have item on both axes
                if(self.parent.extras[currItem[1]].onBothAxes and (not flag)):
                  for handle in relevantHandles:
                    if((handle in self.parent.extras[currItem[1]].handlesBothAxes) and (self.parent.extras[currItem[1]].handlesBothAxes[handle] != None) and (self.parent.extras[currItem[1]].handlesBothAxes[handle].contains(event)[0])):
                      # found an item that has been clicked on - break out of loop
                      currIndex = -1
              else:
                flag = True
              # normal click handling
              if(flag):
                for handle in relevantHandles:
                  if((self.parent.extras[currItem[1]].__dict__[handle] != None) and (self.parent.extras[currItem[1]].__dict__[handle].contains(event)[0])):
                    # found an item that has been clicked on - break out of loop
                    currIndex = -1
              if(currIndex == -1):
                self.parent.objectsarea.changeStyleExtra(currItem[1], callButton=None)
    
            if(currIndex == -1):
              # we hit an item, so don't fall through
              fallThrough = False
    
            # advance item
            currIndex -= 1
       
      # if still no item found, check for grid in the back, and then configure canvas
      if(fallThrough):
        # check grid lines that are in the back of the plot objects
        searchItems = {}
        axes = ['x', 'y']
        if(self.splitShow):
          axes += ['x2']
        if(self.isSecondAxesActive()):
          axes += ['y2']
        for axis in axes:
          for number, line in enumerate(self.gridLinesStore[axis]):
            if(self.gridOrder[axis] == 'back'):
              searchItems['gridli' + axis.upper() + '_' + str(number)] = line
        for axis in axes:
          for number, line in enumerate(self.gridMinorLinesStore[axis]):
            if(self.gridMinorOrder[axis] == 'back'):
              searchItems['minorGridli' + axis.upper() + '_' + str(number)] = line

        for itemType, item in searchItems.items():
          if(fallThrough and hasattr(item, 'contains') and item.contains(event)[0]):
            fallThrough = False
            self.menu = ConfigPlotMenu(self, item=item, itemType=itemType)
        
        if(fallThrough):
          self.menu = ConfigPlotMenu(self, item=self.ax2.patch, itemType='canvas')
        needStyling = True
        
      # is there a context menu that would require styling?
      if(needStyling):
        # apply styles to popup window
        if(QSTYLE != None):
          self.menu.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.menu.setStyleSheet(QSTYLESHEET)
        # first need to display QMenu to get reliable size (even sizeHint fails)
        menuPos = QtGui.QCursor.pos()
        self.menu.popup(menuPos)
        # now move window to new position
        menuX = menuPos.x() - self.menu.width() / 2
        menuX = max(menuX, 0)
        menuPos.setX(int(menuX))
        self.menu.move(menuPos)
            
    # disable double-click for toggling cross hair (due to config of plot elements on double click, see above)
    fallThrough = False
    if((event.button == 2) or fallThrough):
      # toggle cross hair on middle mouse button -- cannot use QtCore.Qt.MidButton as this equates to 4?!
      self.cursorVisible = not self.cursorVisible
      # check whether cursor already exists
      if(self.cursor == None):
        self.cursor = MyCursor(self.ax, useblit=True, color='black', linewidth=1)
        self.cursor.setParent(self)
        self.cursor.setAx2(self.ax2)
      if(self.cursor_div == None):
        if(self.splitShow):
          self.cursor_div = MyCursor(self.ax_div, useblit=True, color='black', linewidth=1)
          self.cursor_div.setParent(self)
          self.cursor_div.setAx2(self.ax2_div)
          
      # link cursors
      # as of matplotlib 3.1.2. we have to make sure that split axes are displayed before we can initialize the div cursor
      if(self.cursor_div != None):
        self.cursor.setTwin(self.cursor_div)
        self.cursor_div.setTwin(self.cursor)
      
      # deal with splity cursors
      if((self.cursor2 == None) and self.splitY):
        self.cursor2 = MyCursor(self.ax2, useblit=True, color='black', linewidth=1)
        self.cursor2.setParent(self)
        self.cursor2.setAx2(self.ax2)
      if((self.cursor2_div == None) and self.splitY and self.splitShow):
        self.cursor2_div = MyCursor(self.ax2_div, useblit=True, color='black', linewidth=1)
        self.cursor2_div.setParent(self)
        self.cursor2_div.setAx2(self.ax2_div)
    
      # link cursors
      if(self.cursor2 != None):
        self.cursor.setTwinVertical(self.cursor2)
        self.cursor2.setTwinVertical(self.cursor)
        self.cursor2.setTwin(self.cursor2_div)

      if(self.cursor2_div != None):
        self.cursor_div.setTwinVertical(self.cursor2_div)
        self.cursor2_div.setTwinVertical(self.cursor_div)
        self.cursor2_div.setTwin(self.cursor2)

      # toggle visibility (in correct order to get display of twins right)
      if(event.inaxes == self.ax):
        order = [self.cursor2_div, self.cursor2, self.cursor_div, self.cursor]
      elif(event.inaxes == self.ax_div):
        order = [self.cursor2, self.cursor2_div, self.cursor, self.cursor_div]
      elif(event.inaxes == self.ax2):
        order = [self.cursor_div, self.cursor2_div, self.cursor, self.cursor2]
      else:
        order = [self.cursor, self.cursor2, self.cursor_div, self.cursor2_div]
        
      if(self.splitY):
        for cursor in order:
          if(cursor != None):
            cursor.toggleVisibility(self.cursorVisible, event)
            
        # extra call to turn off surplus vertical lines
        if(order[0] != None):
          order[0].linev.set_visible(False)
          order[0].lineh.set_visible(False)
          order[0]._update()
      else:
        for cursor in order[-2:]:
          if(cursor != None):
            cursor.toggleVisibility(self.cursorVisible, event)
            
    # reset freehand toggle
    if(self.drawFreehand):
      self.drawFreehand = False
      self.parent.objectsarea.extrasCreateFreehandButton.setChecked(False)
      
  def mouseScrolled(self, event):
    # adjusts zoom level when Ctrl is pressed also
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if((modifiers & QtCore.Qt.KeyboardModifier.ControlModifier) and (event.button in ['down', 'up'])):
      if(event.button == 'down'):
        self.incZoomLevel(1)
      else:
        self.incZoomLevel(-1)

  def destructAboutLogo(self):
    # destroys about logo
    counter = self.dataplotwidget.getDestructionCounter()
    if(counter >= 0):
      counter -= 1
      self.dataplotwidget.setDestructionCounter(np.max((counter, 0)))
      if((counter <= 0) and (len(self.handlesAbout))):
        for entry in self.handlesAbout:
          if(hasattr(entry, 'remove')):
            entry.remove()
        self.handlesAbout = []

  def drawAboutLogo(self, aspect=0, destructCounter=1):
    # draws program info on canvas
    # helper function that transforms coordinates according to axis settings
    def processCoordSet(coords, minX, maxX, modeX, minY, maxY, modeY, relWidth, relHeight, relOffsetX, relOffsetY):
      coords /= 100.0
      # process X coords
      if(modeX == 'linear'):
        coords[:,0] *= (maxX - minX) * relWidth
        coords[:,0] += minX + (maxX - minX) * relOffsetX
      else:
        minX, maxX = np.log(minX), np.log(maxX)
        coords[:,0] *= (maxX - minX) * relWidth
        coords[:,0] += minX + (maxX - minX) * relOffsetX
        coords[:,0] = np.exp(coords[:,0])
      # process Y coords
      if(modeY == 'linear'):
        coords[:,1] *= (maxY - minY) * relHeight
        coords[:,1] += minY + (maxY - minY) * relOffsetY
      else:
        minY, maxY = np.log(minY), np.log(maxY)
        coords[:,1] *= (maxY - minY) * relHeight
        coords[:,1] += minY + (maxY - minY) * relOffsetY
        coords[:,1] = np.exp(coords[:,1])
      return coords

    # helper function that transforms coordinates according to axis settings
    def processCoordSingle(minX, maxX, modeX, minY, maxY, modeY, relOffsetX, relOffsetY):
      # process X coords
      if(modeX == 'linear'):
        x = minX + (maxX - minX) * relOffsetX
      else:
        minX, maxX = np.log(minX), np.log(maxX)
        x = minX + (maxX - minX) * relOffsetX
        x = np.exp(x)
      # process Y coords
      if(modeY == 'linear'):
        y = minY + (maxY - minY) * relOffsetY
      else:
        minY, maxY = np.log(minY), np.log(maxY)
        y = minY + (maxY - minY) * relOffsetY
        y = np.exp(y)
      return [x, y]
    
    # check whether a previous logo is still displayed?
    if(not len(self.handlesAbout)):
      # settings
      zOffset = 2001 # should be in front of selectedData which are at 2000
      ubtCol = [0.051, 0.306, 0.702, 1.0]
      greyCol = [0.3, 0.3, 0.3, 1.0]
      blackCol = [0.0, 0.0, 0.0, 1.0]
      
      # retrieve axis info
      targetAxis = self.ax
      minX, maxX = self.minX, self.maxX
      minY, maxY = self.minY, self.maxY
      modeX, modeY = self.modeX, self.modeY

      # calculate draw dimensions (will depend on split axes and padding of plot)
      if(self.splitShow):
        if(modeX == 'linear'):
          # account for ratio of split axes
          deltaX = (maxX - minX) * (1 + self.splitFraction) / self.splitFraction
          # correct for padding in the middle
          try:
            deltaX /= (1 - self.padSize['left'] - (1 - self.padSize['right']) - self.splitPad)
            deltaX *= (1 - self.padSize['left'] - (1 - self.padSize['right']))
          except:
            pass
          maxX = minX + deltaX
        else:
          deltaX = (maxX / minX) ** ((1 + self.splitFraction) / self.splitFraction)
          try:
            factor = 1 / (1 - self.padSize['left'] - (1 - self.padSize['right']) - self.splitPad)
            factor *= (1 - self.padSize['left'] - (1 - self.padSize['right']))
            deltaX = deltaX ** factor
          except:
            pass
          maxX = minX * deltaX
      if(self.splitY):
        # account for ratio of split axes
        if(modeY == 'linear'):
          deltaY = (maxY - minY) * (1 + self.splitYFraction)
          # correct for padding in the middle
          try:
            deltaY /= (1 - self.padSize['bottom'] - (1 - self.padSize['top']) - self.splitYPad)
            deltaY *= (1 - self.padSize['bottom'] - (1 - self.padSize['top']))
          except:
            pass
          minY = maxY - deltaY
        else:
          deltaY = (maxY / minY) ** (1 + self.splitYFraction)
          try:
            factor = 1 / (1 - self.padSize['bottom'] - (1 - self.padSize['top']) - self.splitYPad)
            factor *= (1 - self.padSize['bottom'] - (1 - self.padSize['top']))
            deltaY = deltaY ** factor
          except:
            pass
          minY = maxY / deltaY
        
      # need to compensate for padding
      if(modeX == 'linear'):
        deltaX = (maxX - minX)
        minX, maxX = maxX - deltaX * (1 + self.padSize['left'] / (self.padSize['right'] - self.padSize['left'])),\
                      minX + deltaX * (1 + (1 - self.padSize['right']) / (self.padSize['right'] - self.padSize['left']))
      else:
        deltaX = (maxX / minX)
        minX, maxX = maxX / deltaX ** (1 + self.padSize['left'] / (self.padSize['right'] - self.padSize['left'])),\
                      minX * deltaX ** (1 + (1 - self.padSize['right']) / (self.padSize['right'] - self.padSize['left']))

      if(modeY == 'linear'):
        deltaY = (maxY - minY)
        minY, maxY = maxY - deltaY * (1 + self.padSize['bottom'] / (self.padSize['top'] - self.padSize['bottom'])),\
                      minY + deltaY * (1 + (1 - self.padSize['top']) / (self.padSize['top'] - self.padSize['bottom']))
      else:
        deltaY = (maxY / minY)
        minY, maxY = maxY / deltaY ** (1 + self.padSize['bottom'] / (self.padSize['top'] - self.padSize['bottom'])),\
                      minY * deltaY ** (1 + (1 - self.padSize['top']) / (self.padSize['top'] - self.padSize['bottom']))
      
      # no aspect ratio specified, calculate own (don't do on first call since widgets have not resized properly yet)      
      currWidth, currHeight = self.matplot.get_size_inches()
      if(not aspect):
        aspect = currWidth / currHeight
      scaleFont = 100.0 / self.matplot.get_dpi()
        
      # define coordinate sets for graphics ...
      coords0 = np.array([[0.0, 0.0], [0.0, 100.0], [100.0, 100.0], [100.0, 0.0]])
      # ... and process them
      scaleWidth = 1
      scaleHeight = 1 / aspect
      scaleMin = np.min((scaleWidth, scaleHeight))
      scaleHeight = np.min((2.5, scaleHeight))
      scaleHeight = np.max((1.0, scaleHeight))
      widthBox = 0.8 * scaleWidth
      heightBox = 0.5 * scaleHeight
      coords0 = processCoordSet(coords0, minX, maxX, modeX, minY, maxY, modeY, widthBox, heightBox, 0.1, (1 - heightBox) / 2.0)
  
      # calculate coords for text labels
      text0 = processCoordSingle(minX, maxX, modeX, minY, maxY, modeY, 0.5, 0.63)
      text1 = processCoordSingle(minX, maxX, modeX, minY, maxY, modeY, 0.5, 0.4)
      text2 = processCoordSingle(minX, maxX, modeX, minY, maxY, modeY, 0.5, 0.3)
      text3 = processCoordSingle(minX, maxX, modeX, minY, maxY, modeY, 0.82, 0.64)
      text4 = processCoordSingle(minX, maxX, modeX, minY, maxY, modeY, 0.84, 0.61)
  
      # draw the background
      currZoomLevel = self.matplot.get_dpi()
      linewidth = 1.0 * 100.0 / currZoomLevel
      polyPatch0 = matplotlib.patches.Polygon(coords0, closed=True, facecolor=[1.0, 1.0, 1.0, 0.7],\
        edgecolor = greyCol, linestyle='solid', linewidth=linewidth, zorder=zOffset)
      retv = targetAxis.add_patch(polyPatch0)
      retv.set_clip_on(False)
      self.handlesAbout.append(retv)
      zOffset += 1
      
      # determine scale factor for matplotlib
      MPL_SCALEFACTOR = myapp.devicePixelRatio()
      
      # print some information
      # according to docu, positional argument would be 'text' but Matplotlib wants 's' => better don't use positional arguments
      #retv = self.ax.text(x=self.minX + 0.5 * width, y=self.minY + 0.5 * height, s='Fit-o-mat',\
      #  horizontalalignment='center', zorder=zOffset)
      retv = targetAxis.text(text0[0], text0[1], 'Fit-o-mat',\
        horizontalalignment='center', zorder=zOffset, style='normal', fontsize=scaleFont * int(20 * SCALEFONT * MPL_SCALEFACTOR), fontweight='normal', color=ubtCol)
      self.handlesAbout.append(retv)
      zOffset += 1
      
      retv = targetAxis.text(text1[0], text1[1],\
        '\u00A9' + ' by A.M. 2017-2025 @MoeglichLab\nandreas.moeglich@uni-bayreuth.de\nGNU General Public License v3.0',\
        horizontalalignment='center', zorder=zOffset, style='normal', fontsize=scaleFont * int(10 * SCALEFONT * MPL_SCALEFACTOR), fontweight='normal', color=greyCol)
      self.handlesAbout.append(retv)
      zOffset += 1
      
      retv = targetAxis.text(text2[0], text2[1], 'version ' + VERSION, horizontalalignment='center', zorder=zOffset,\
        style='italic', fontsize=scaleFont * int(8 * SCALEFONT * MPL_SCALEFACTOR), fontweight='normal', color=greyCol)
      self.handlesAbout.append(retv)
      zOffset += 1
      
      retv = targetAxis.text(text3[0], text3[1], 'f', horizontalalignment='center', zorder=zOffset, family='serif',\
        style='italic', fontsize=scaleFont * int(20 * SCALEFONT * MPL_SCALEFACTOR), fontweight='normal', color=blackCol)
      self.handlesAbout.append(retv)
      zOffset += 1
      
      retv = targetAxis.text(text4[0], text4[1], 'x', horizontalalignment='center', zorder=zOffset, family='serif',\
        style='italic', fontsize=scaleFont * int(20 * SCALEFONT * MPL_SCALEFACTOR), fontweight='normal', color=blackCol)
      self.handlesAbout.append(retv)
      zOffset += 1
      
      # draw fit-o-mat logo
      logo = matplotlib.path.Path([[-0.05682998,  0.47677547], [-0.05682998,  0.68146271],
       [ 0.04833121,  0.84012554], [ 0.15428282,  0.99999958], [ 0.29107145,  0.99999958], [ 0.43181316,  0.99999958],
       [ 0.53697435,  0.84254788], [ 0.64213553,  0.68630738], [ 0.64213553,  0.47677547], [ 0.64213553,  0.26603271],
       [ 0.53855567,  0.1049474 ], [ 0.43576671, -0.05492643], [ 0.30055941, -0.05492643], [ 0.15586415, -0.05492643],
       [ 0.04912164,  0.10373591], [-0.05682998,  0.26239896], [-0.05682998,  0.47677547], [-0.05682998,  0.47677547],
       [-0.05682998,  0.47677547], [ 0.50930045,  0.47677547], [ 0.50930045,  0.60394799], [ 0.44604556,  0.69720786],
       [ 0.38279067,  0.79167887], [ 0.29581543,  0.79167887], [ 0.20646797,  0.79167887], [ 0.14163175,  0.69599673],
       [ 0.07758643,  0.60152573], [ 0.07758643,  0.46950853], [ 0.07758643,  0.34354735], [ 0.14242265,  0.24907614],
       [ 0.2080493 ,  0.15460492], [ 0.29581543,  0.15460492], [ 0.38120935,  0.15460492], [ 0.44525513,  0.2514984 ],
       [ 0.50930045,  0.34839189], [ 0.50930045,  0.47677547], [ 0.50930045,  0.47677547], [ 0.50930045,  0.47677547],
       [-0.6736831 ,  1.        ], [-0.08462276,  1.        ], [-0.12768349,  0.74262702], [-0.54674644,  0.74262702],
       [-0.62774753,  0.25816052], [-0.21066112,  0.25816052], [-0.25393759, -0.00224054], [-0.67003573, -0.00224054],
       [-0.82604952, -0.92878325], [-1.        , -0.92878325], [-0.6736831 ,  1.        ], [-0.6736831 ,  1.        ],
       [ 0.66980989,  0.99987871], [ 1.        , -0.92878325], [ 0.83870013, -0.92878325], [ 0.63849871,  0.24992411],
       [ 0.29407666, -1.        ], [-0.05129464,  0.25864427], [-0.25339365, -0.92878325], [-0.41469353, -0.92878325],
       [-0.08450342,  0.99987871], [ 0.29407666, -0.38085174], [ 0.66980989,  0.99987871], [ 0.66980989,  0.99987871]],
      [ 1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
        2, 79,  1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,
        3,  3,  2, 79,  1,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 79,  1,
        2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 79])
      logo.vertices[:, 0] -= np.min(logo.vertices[:, 0])
      logo.vertices[:, 1] -= np.min(logo.vertices[:, 1])
      # multiply by 100 to counteract division by 100 in processCoordSet()
      logo.vertices *= 100.
      logo.vertices = processCoordSet(logo.vertices, minX, maxX, modeX, minY, maxY, modeY, 0.06 * scaleMin, 0.06 * scaleMin, 0.13, .61)
      polyPatch4 = matplotlib.patches.PathPatch(logo, facecolor='black', edgecolor=None, linewidth=0, zorder=zOffset)
      retv = targetAxis.add_patch(polyPatch4)
      retv.set_clip_on(False)
      self.handlesAbout.append(retv)
      zOffset += 1
    else:
      self.dataplotwidget.setDestructionCounter(1)
      self.destructAboutLogo()
      destructCounter = 0
      
    # refresh the plot to draw everything
    self.dataplotwidget.myRefresh()
    
    # set us up for ready destruction
    self.dataplotwidget.setDestructionCounter(destructCounter)

  def initLegend(self):
    # initializes legend (has to occur after function is drawn)
    self.setLegend(value=self.legendVisible, redraw=False, target='plot')
    self.setLegendPlacement(value=self.legendPlacement, redraw=False, target='plot')
    for prop in ['face', 'edge']:
      self.setLegendColor(value=self.legendColor[prop], prop=prop, redraw=False, target='plot')
    self.setLegendEdgeWidth(value=self.legendEdgeWidth, redraw=False, target='plot')
    self.setLegendShadow(value=self.legendShadow, redraw=False, target='plot')
    self.setLegendLabelColor(value=self.legendLabelColor, redraw=False, target='plot')
    self.setLegendLabelSize(value=self.legendLabelSize, redraw=False, target='plot')
    self.setLegendLabelBold(value=self.legendLabelWeight, redraw=False, target='plot')
    self.setLegendLabelItalic(value=self.legendLabelStyle, redraw=False, target='plot')
    self.setLegendLabelFont(value=self.legendLabelFont, redraw=False, target='plot')
    self.setLegendLabelLinespacing(value=self.legendLabelLinespacing, redraw=False, target='plot')
    self.setLegendMarkerFirst(value=self.legendMarkerFirst, redraw=False, target='plot')
    self.setLegendMarkerScale(value=self.legendMarkerScale, redraw=False, target='plot')
    self.setLegendNCol(value=self.legendNumCol, redraw=False, target='plot')
    self.toggleLegendFill(value=self.legendFill, redraw=False, target='plot')
    self.toggleLegendEdge(value=self.legendEdge, redraw=False, target='plot')
    self.setLegendMarkerNumPoints(value=self.legendNumPoints, redraw=False, target='plot')
    self.setLegendPadBorder(value=self.legendBorderPad, redraw=False, target='plot')
    self.setLegendPadRow(value=self.legendLabelSpacing, redraw=False, target='plot')
    self.setLegendPadCol(value=self.legendColumnSpacing, redraw=False, target='plot')
    self.setLegendPadLabel(value=self.legendLabelPad, redraw=False, target='plot')
   
  def changeAxisLimits(self, axis='x', target='plot', redraw=True):
    # check whether to operate on data or resid plot
    if(target in ['plot', 'resid']):
      # changes limits of axis
      if(axis == 'x'):
        # retrieve values from field
        upperx = self.upperLimitx.text()
        try:
          upperx = float(upperx)
        except:
          upperx = 0.0
  
        lowerx = self.lowerLimitx.text()
        try:
          lowerx = float(lowerx)
        except:
          lowerx = 0.0
        # check whether any value changed
        if((upperx != self.maxX) or (lowerx != self.minX)):
          # do some checks and update axis (don't redraw, plot functions will take care of)
          self.setAxisLimits(lower=lowerx, upper=upperx, axis='x', updateLabel=False, target='plot', redraw=False, updateGrid=True, preserveInverted=False)
          self.setAxisLimits(lower=lowerx, upper=upperx, axis='x', updateLabel=False, target='resid', redraw=False, updateGrid=True, preserveInverted=False)
          self.updateTickEntryField(axis='x')
          # replot function over current x-interval
          self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.plotFunction(fitobject=self.parent.fit[self.parent.activeFit], x=[],\
                         handlePlot=self.parent.fit[self.parent.activeFit].handlePlot, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
          # copy in case split axes are shown
          curve = self.parent.fit[self.parent.activeFit]
          if(self.parent.plotArea.splitY and curve.onBothAxes):
            curve.duplicateForSplit()
          if(redraw):
            self.parent.plotArea.dataplotwidget.myRefresh()
          self.handleResidZero = self.plotResidZero(handleResidZero=self.handleResidZero, redraw=redraw)
      elif(axis == 'x2'):
        # retrieve values from field
        upperx = self.xSplitUpperLimitx.text()
        try:
          upperx = float(upperx)
        except:
          upperx = 0.0
  
        lowerx = self.xSplitLowerLimitx.text()
        try:
          lowerx = float(lowerx)
        except:
          lowerx = 0.0
        # check whether any value changed
        if((upperx != self.maxX_div) or (lowerx != self.minX_div)):
          # do some checks and update axis (don't redraw, plot functions will take care of)
          self.setAxisLimits(lower = lowerx, upper = upperx, axis = 'x2', updateLabel = False, target='plot', redraw=False, updateGrid=True, preserveInverted=False)
          self.setAxisLimits(lower = lowerx, upper = upperx, axis = 'x2', updateLabel = False, target='resid', redraw=False, updateGrid=True, preserveInverted=False)
          self.updateTickEntryField(axis='x2')
          # replot function over current x-interval
          self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[self.parent.activeFit], x=[],\
                         handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
          # copy in case split axes are shown
          curve = self.parent.fit[self.parent.activeFit]
          if(self.parent.plotArea.splitY and curve.onBothAxes):
            curve.duplicateForSplit()
          if(redraw):
            self.parent.plotArea.dataplotwidget.myRefresh()
          self.handleResidZero_div = self.plotResidZero(handleResidZero=self.handleResidZero_div, redraw=redraw, splitX=True)
      elif(axis == 'y'):
        # retrieve values from field
        if(target == 'plot'):
          uppery = self.upperLimity.text()
          lowery = self.lowerLimity.text()
        else:
          uppery = self.upperLimitResidy.text()
          lowery = self.lowerLimitResidy.text()

        try:
          uppery = float(uppery)
        except:
          uppery = 0.0
  
        try:
          lowery = float(lowery)
        except:
          lowery = 0.0
        
        # check whether any value changed
        if(target == 'plot'):
          if((uppery != self.maxY) or (lowery != self.minY)):
            # do some checks and update axis
            self.setAxisLimits(lower=lowery, upper=uppery, axis='y', updateLabel=False, target=target, redraw=redraw, updateGrid=True, preserveInverted=False)
            self.updateTickEntryField(axis='y')
        else:
          if((uppery != self.maxResidY) or (lowery != self.minResidY)):
            # do some checks and update axis
            self.setAxisLimits(lower=lowery, upper=uppery, axis='y', updateLabel=False, target=target, redraw=redraw, updateGrid=True, preserveInverted=False)
            self.updateTickEntryField(axis='resid')
      elif(axis == 'y2'):
        # modify second y axis
        uppery = self.secondUpperLimit.text()
        lowery = self.secondLowerLimit.text()

        try:
          uppery = float(uppery)
        except:
          uppery = 0.0
  
        try:
          lowery = float(lowery)
        except:
          lowery = 0.0
        
        # check whether any value changed
        if((uppery != self.maxY2) or (lowery != self.minY2)):
          # do some checks and update axis
          self.setAxisLimits(lower=lowery, upper=uppery, axis='y2', updateLabel=False, target=target, redraw=redraw, updateGrid=True, preserveInverted=False)
          self.updateTickEntryField(axis='y2')
      
  def setAxisLimits(self, lower=0, upper=1, axis='x', updateLabel=False, target='plot', redraw=True, updateTicks=True, updateGrid=True, preserveInverted=True):
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        plotobject, axisobject, axisobject2 = self.dataplotwidget, self.ax, self.ax_div
        if(axis == 'x2'):
          axisobject = [self.ax_div, self.ax2_div]
      else:
        plotobject, axisobject, axisobject2 = self.residplotwidget, self.ax_resid, self.ax_resid_div
        if(axis == 'x2'):
          axisobject = [self.ax_resid_div]
    else:
      axis = 'abort'
    # ensure safe distance from largest positive/negative number
    # => otherwise matplotlib will crash when calculating tick locations
    MONSTER_FLOAT = 1.0e300
    lower, upper = min(lower, MONSTER_FLOAT), min(upper, MONSTER_FLOAT)
    lower, upper = max(lower, -MONSTER_FLOAT), max(upper, -MONSTER_FLOAT)
    # performs checks and then sets axis limits
    if(axis == 'x'):
      # check for inverted axis limits
      if(preserveInverted and (self.minX > self.maxX)):
        invertMe = True
      else:
        invertMe = False
      # have to prevent nan and inf here
      if(np.isfinite(lower)):
        self.minX = lower
      if(np.isfinite(upper)):
        self.maxX = upper
      # check current axis mode and take care of log values
      axis_mode = str(self.modeSelectorx.currentText())
      if (axis_mode == 'log'):
        if ((self.minX <= 0) or (self.maxX <= 0)):
          # look whether data are loaded
          data = self.parent.data[self.parent.activeData].value()
          if ('x' in data):
            xdata = np.array(data['x'])
            xdata = xdata[xdata > 0]
            if(len(xdata)):
              min_xdata = np.min(xdata); max_xdata = np.max(xdata)
              if((self.data_spacer > 0) and (len(xdata) > 1)):
                difference = np.log(max_xdata / min_xdata)
                min_xdata = np.exp(np.log(min_xdata) - self.data_spacer * difference)
                max_xdata = np.exp(np.log(max_xdata) + self.data_spacer * difference)
            else:
              min_xdata, max_xdata = 0, 0
            self.minX = np.max((self.EPSILON, self.minX, min_xdata))
            self.maxX = np.max((self.EPSILON, self.maxX, max_xdata))
          else:
            # ensure that min and max values are positive
            self.minX = np.max((self.EPSILON, self.minX))
            self.maxX = np.max((self.EPSILON, self.maxX))
          updateLabel = True

      if(invertMe):
        self.minX, self.maxX = np.max((self.minX, self.maxX)), np.min((self.minX, self.maxX))
        
      # update axis
      if(self.minX == self.maxX):
        if(axis_mode == 'log'):
          self.minX, self.maxX = self.minX / 1.1, self.maxX * 1.1
        else:
          self.minX, self.maxX = self.minX - self.EPSILON, self.maxX + self.EPSILON
        updateLabel = True

      # update labels?
      if(updateLabel):
        lowerText, upperText = self.parent.formatPairedNumbers(self.minX, self.maxX)
        # we should let the entry fields lose focus lest we get unintended autozoom updates afterwards
        self.upperLimitx.blockSignals(True)
        self.upperLimitx.clearFocus()
        self.upperLimitx.blockSignals(False)
        self.lowerLimitx.blockSignals(True)
        self.lowerLimitx.clearFocus()
        self.lowerLimitx.blockSignals(False)
        self.upperLimitx.setText(upperText)
        self.lowerLimitx.setText(lowerText)

      axisobject.set_xlim([self.minX, self.maxX])
      if(target == 'plot'):
        self.ax2.set_xlim([self.minX, self.maxX])
      # update push button
      self.flipAxisButtonX.setChecked(bool(self.maxX < self.minX))
      if(updateTicks):
        self.setTickOne4All(axis=axis, redraw=False, target=target)

      # do we have a grid to draw?
      if(updateGrid):
        for item in ('x', 'y', 'y2'):
          if(self.gridVisible[item]):
            if((item != 'y2') or self.isSecondAxesActive()):
              self.drawAxisGrid(axis=item, redraw=False, target=target)

      for axis in ['left', 'right']:
        if((axis_mode == 'log') and (self.axisPosition[axis] == 'data')):
          # only required for log plots which we had to cast to axes coords
          if(target == 'plot'):
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax, target=target, secondAxes=False)
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax_div, target=target, secondAxes=False, splitX=True)
          else:
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax_resid, target=target, secondAxes=False)
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax_resid_div, target=target, secondAxes=False, splitX=True)

      for spine in ['left2', 'right2']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.matplot, axisobject=self.ax2, target='plot', secondAxes=True)
          self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.matplot, axisobject=self.ax2_div, target='plot', secondAxes=True, splitX=True)

      if(redraw):
        plotobject.myRefresh()
    elif(axis == 'x2'):
      # check for inverted axis limits
      if(preserveInverted and (self.minX_div > self.maxX_div)):
        invertMe = True
      else:
        invertMe = False
      # have to prevent nan and inf here
      if(np.isfinite(lower)):
        self.minX_div = lower
      if(np.isfinite(upper)):
        self.maxX_div = upper
      # check current axis mode and take care of log values
      axis_mode = str(self.xSplitModeSelectorx.currentText())
      if (axis_mode == 'log'):
        if ((self.minX_div <= 0) or (self.maxX_div <= 0)):
          # look whether data are loaded
          data = self.parent.data[self.parent.activeData].value()
          if ('x' in data):
            xdata = np.array(data['x'])
            xdata = xdata[xdata > 0]
            if(len(xdata)):
              min_xdata = np.min(xdata); max_xdata = np.max(xdata)
              if((self.data_spacer > 0) and (len(xdata) > 1)):
                difference = np.log(max_xdata / min_xdata)
                min_xdata = np.exp(np.log(min_xdata) - self.data_spacer * difference)
                max_xdata = np.exp(np.log(max_xdata) + self.data_spacer * difference)
            else:
              min_xdata, max_xdata = 0, 0
            self.minX_div = np.max((self.EPSILON, self.minX_div, min_xdata))
            self.maxX_div = np.max((self.EPSILON, self.maxX_div, max_xdata))
          else:
            # ensure that min and max values are positive
            self.minX_div = np.max((self.EPSILON, self.minX_div))
            self.maxX_div = np.max((self.EPSILON, self.maxX_div))
          updateLabel = True
      
      if(invertMe):
        self.minX_div, self.maxX_div = np.max((self.minX_div, self.maxX_div)), np.min((self.minX_div, self.maxX_div))
        
      # update axis
      if(self.minX_div == self.maxX_div):
        if(axis_mode == 'log'):
          self.minX_div, self.maxX_div = self.minX_div / 1.1, self.maxX_div * 1.1
        else:
          self.minX_div, self.maxX_div = self.minX_div - self.EPSILON, self.maxX_div + self.EPSILON
        updateLabel = True

      # update labels?
      if(updateLabel):
        lowerText, upperText = self.parent.formatPairedNumbers(self.minX_div, self.maxX_div)
        # we should let the entry fields lose focus lest we get unintended autozoom updates afterwards
        self.xSplitUpperLimitx.blockSignals(True)
        self.xSplitUpperLimitx.clearFocus()
        self.xSplitUpperLimitx.blockSignals(False)
        self.xSplitLowerLimitx.blockSignals(True)
        self.xSplitLowerLimitx.clearFocus()
        self.xSplitLowerLimitx.blockSignals(False)
        self.xSplitUpperLimitx.setText(upperText)
        self.xSplitLowerLimitx.setText(lowerText)

      for entry in axisobject:
        entry.set_xlim([self.minX_div, self.maxX_div])
      # update push button
      self.flipAxisButtonX2.setChecked(bool(self.maxX_div < self.minX_div))
      # should be able to use the same call which updates both regular x axis and divided one
      if(updateTicks):
        self.setTickOne4All(axis='x2', redraw=False, target=target)

      # do we have a grid to draw?
      if(updateGrid):
        for item in ('x2', 'y', 'y2'):
          if(self.gridVisible[item]):
            if((item != 'y2') or self.isSecondAxesActive()):
              self.drawAxisGrid(axis=item, redraw=False, target=target)

      '''
      for axis in ['left', 'right']:
        if((axis_mode == 'log') and (self.axisPosition[axis] == 'data')):
          # only required for log plots which we had to cast to axes coords
          if(target == 'plot'):
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax_div, target=target, secondAxes=False, splitX=True)
          else:
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax_resid_div, target=target, secondAxes=False, splitX=True)

      for spine in ['left2', 'right2']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.matplot, axisobject=self.ax2_div, target='plot', secondAxes=True, splitX=True)
      '''
      
      if(redraw):
        plotobject.myRefresh()
    elif(axis == 'y'):
      # beware -- as we had to abandon make_axes_locatable, self.ax is no longer y-slaved to self.ax_div
      # => we need to do this ourselves
      if(target == 'plot'):
        # check for inverted axis limits
        if(preserveInverted and (self.minY > self.maxY)):
          invertMe = True
        else:
          invertMe = False
        # have to prevent nan and inf here
        if(np.isfinite(lower)):
          self.minY = lower
        if(np.isfinite(upper)):
          self.maxY = upper
        # check current axis mode and take care of log values
        axis_mode = str(self.modeSelectory.currentText())
        if ((axis_mode == 'log') and (target == 'plot')):
          if ((self.minY <= 0) or (self.maxY <= 0)):
            # look whether data are loaded
            data = self.parent.data[self.parent.activeData].value()
            if ('y' in data):
              ydata = np.array(data['y'])
              ydata = ydata[ydata > 0]
              if(len(ydata)):
                min_ydata = np.min(ydata); max_ydata = np.max(ydata)
                if((self.data_spacer > 0) and (len(ydata) > 1)):
                  difference = np.log(max_ydata / min_ydata)
                  min_ydata = np.exp(np.log(min_ydata) - self.data_spacer * difference)
                  max_ydata = np.exp(np.log(max_ydata) + self.data_spacer * difference)
              else:
                min_ydata, max_ydata = 0, 0
              self.minY = np.max((self.EPSILON, self.minY, min_ydata))
              self.maxY = np.max((self.EPSILON, self.maxY, max_ydata))
            else:
              # ensure that min and max values are positive
              self.minY = np.max((self.EPSILON, self.minY))
              self.maxY = np.max((self.EPSILON, self.maxY))
            updateLabel = True
        
        if(invertMe):
          self.minY, self.maxY = np.max((self.minY, self.maxY)), np.min((self.minY, self.maxY))

        # update axis
        if(self.minY == self.maxY):
          if(axis_mode == 'log'):
            self.minY, self.maxY = self.minY / 1.1, self.maxY * 1.1
          else:
            self.minY, self.maxY = self.minY - self.EPSILON, self.maxY + self.EPSILON
          updateLabel = True

        # update labels?
        if(updateLabel):
          lowerText, upperText = self.parent.formatPairedNumbers(self.minY, self.maxY)
          # we should let the entry fields lose focus lest we get unintended autozoom updates afterwards
          self.upperLimity.blockSignals(True)
          self.upperLimity.clearFocus()
          self.upperLimity.blockSignals(False)
          self.lowerLimity.blockSignals(True)
          self.lowerLimity.clearFocus()
          self.lowerLimity.blockSignals(False)
          self.upperLimity.setText(upperText)
          self.lowerLimity.setText(lowerText)

        axisobject.set_ylim([self.minY, self.maxY])
        axisobject2.set_ylim([self.minY, self.maxY])
        # update push button
        self.flipAxisButtonY.setChecked(bool(self.maxY < self.minY))
        if(updateTicks):
          self.setTickOne4All(axis=axis, redraw=False, target=target)

        # do we have a grid to draw?
        if(updateGrid):
          for item in ('x', 'x2', 'y'):
            if(self.gridVisible[item]):
              self.drawAxisGrid(axis=item, redraw=False, target=target)
  
        # adjust spine lines if needed
        for axis in ['top', 'bottom']:
          if(self.axisPosition[axis] == 'data'):
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax2, target=target, secondAxes=False)
            self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax2_div, target=target, secondAxes=False, splitX=True)

        if(redraw):
          plotobject.myRefresh()
      else:
        # don't check for inverted as this should not be possible for resid plot
        # have to prevent nan and inf here
        if(np.isfinite(lower)):
          self.minResidY = lower
        if(np.isfinite(upper)):
          self.maxResidY = upper

        # update axis
        if(self.minResidY == self.maxResidY):
          self.minResidY, self.maxResidY = self.minResidY - self.EPSILON, self.maxResidY + self.EPSILON
          updateLabel = True

        # update labels?
        if(updateLabel):
          lowerText, upperText = self.parent.formatPairedNumbers(self.minResidY, self.maxResidY)
          # we should let the entry fields lose focus lest we get unintended autozoom updates afterwards
          self.upperLimitResidy.blockSignals(True)
          self.upperLimitResidy.clearFocus()
          self.upperLimitResidy.blockSignals(False)
          self.lowerLimitResidy.blockSignals(True)
          self.lowerLimitResidy.clearFocus()
          self.lowerLimitResidy.blockSignals(False)
          self.upperLimitResidy.setText(upperText)
          self.lowerLimitResidy.setText(lowerText)

        axisobject.set_ylim([self.minResidY, self.maxResidY])
        axisobject2.set_ylim([self.minResidY, self.maxResidY])
        if(updateTicks):
          self.setTickOne4All(axis=axis, redraw=False, target=target)

        # do we have a grid to draw?
        if(updateGrid):
          for item in ('x', 'x2', 'y'):
            if(self.gridVisible[item]):
              self.drawAxisGrid(axis=item, redraw=False, target=target)

        if(redraw):
          plotobject.myRefresh()
    elif(axis == 'y2'):
      axisobject, axisobject2 = self.ax2, self.ax2_div
      # check for inverted axis limits
      if(preserveInverted and (self.minY2 > self.maxY2)):
        invertMe = True
      else:
        invertMe = False
      # have to prevent nan and inf here
      if(np.isfinite(lower)):
        self.minY2 = lower
      if(np.isfinite(upper)):
        self.maxY2 = upper
      # check current axis mode and take care of log values
      axis_mode = str(self.secondModeSelector.currentText())
      if(axis_mode == 'log'):
        if ((self.minY2 <= 0) or (self.maxY2 <= 0)):
          # look whether data are loaded
          data = self.parent.data[self.parent.activeData].value()
          if ('y' in data):
            ydata = np.array(data['y'])
            ydata = ydata[ydata > 0]
            if(len(ydata)):
              min_ydata = np.min(ydata); max_ydata = np.max(ydata)
              if((self.data_spacer > 0) and (len(ydata) > 1)):
                difference = np.log(max_ydata / min_ydata)
                min_ydata = np.exp(np.log(min_ydata) - self.data_spacer * difference)
                max_ydata = np.exp(np.log(max_ydata) + self.data_spacer * difference)
            else:
              min_ydata, max_ydata = 0, 0
            self.minY2 = np.max((self.EPSILON, self.minY2, min_ydata))
            self.maxY2 = np.max((self.EPSILON, self.maxY2, max_ydata))
          else:
            # ensure that min and max values are positive
            self.minY2 = np.max((self.EPSILON, self.minY2))
            self.maxY2 = np.max((self.EPSILON, self.maxY2))
          updateLabel = True
      
      if(invertMe):
        self.minY2, self.maxY2 = np.max((self.minY2, self.maxY2)), np.min((self.minY2, self.maxY2))

      # update axis
      if(self.minY2 == self.maxY2):
        if(axis_mode == 'log'):
          self.minY2, self.maxY2 = self.minY2 / 1.1, self.maxY2 * 1.1
        else:
          self.minY2, self.maxY2 = self.minY2 - self.EPSILON, self.maxY2 + self.EPSILON
        updateLabel = True

      # update labels?
      if(updateLabel):
        lowerText, upperText = self.parent.formatPairedNumbers(self.minY2, self.maxY2)
        # we should let the entry fields lose focus lest we get unintended autozoom updates afterwards
        self.secondUpperLimit.blockSignals(True)
        self.secondUpperLimit.clearFocus()
        self.secondUpperLimit.blockSignals(False)
        self.secondLowerLimit.blockSignals(True)
        self.secondLowerLimit.clearFocus()
        self.secondLowerLimit.blockSignals(False)
        self.secondUpperLimit.setText(upperText)
        self.secondLowerLimit.setText(lowerText)

      axisobject.set_ylim([self.minY2, self.maxY2])
      axisobject2.set_ylim([self.minY2, self.maxY2])
      # update push button
      self.flipAxisButtonY2.setChecked(bool(self.maxY2 < self.minY2))
      if(updateTicks):
        self.setTickOne4All(axis=axis, redraw=False, target=target)

      # do we have a grid to draw?
      if(updateGrid):
        for item in ('x', 'x2', 'y2'):
          if(self.gridVisible[item]):
            if((item != 'y2') or self.isSecondAxesActive()):
              self.drawAxisGrid(axis=item, redraw=False, target=target)

      # adjust spine lines if needed
      for axis in ['top', 'bottom']:
        if(self.axisPosition[axis] == 'data'):
          self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax2, target=target, secondAxes=False)
          self.setAxisPositionHelper(axis=axis, plotobject=plotobject, axisobject=self.ax2_div, target=target, secondAxes=False, splitX=True)

      if(redraw):
        plotobject.myRefresh()

  def drawAxisGrid(self, axis='x', target='plot', redraw=False):
    # draws custom grid lines
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        # draw grid on 1st set of axes
        plotobject = self.dataplotwidget; axisobject = [self.ax, self.ax_div]
        if(axis == 'x'):
          axisobject = [self.ax]
          if(self.splitY):
            axisobject += [self.ax2]
        elif(axis == 'x2'):
          axisobject =[self.ax2_div]
          if(self.splitY):
            axisobject += [self.ax_div]
        elif(axis == 'y2'):
          axisobject = [self.ax2, self.ax2_div]
        useVisible, useColor, useWidth, useStyle, useDashStyle, useGridOrder = self.gridVisible[axis], self.gridColor[axis], self.gridWidth[axis], self.gridStyle[axis], self.gridDashStyle[axis], self.gridOrder[axis]
      else:
        plotobject = self.residplotwidget; axisobject = [self.ax_resid, self.ax_resid_div]
        if(axis == 'x'):
          axisobject = [self.ax_resid]
        elif(axis == 'x2'):
          axisobject =[self.ax_resid_div]
        useVisible, useColor, useWidth, useStyle, useDashStyle, useGridOrder = self.gridVisible_resid[axis], self.gridColor_resid[axis], self.gridWidth_resid[axis], self.gridStyle_resid[axis], self.gridDashStyle_resid[axis], self.gridOrder_resid[axis]
      
      if(useGridOrder == 'back'):
        useZ = 1
      else:
        useZ = 500
        
      # remove previous lines for this axis
      if(target == 'plot'):
        for entry in self.gridLinesStore[axis]:
          entry.remove()
        self.gridLinesStore[axis] = []
      else:
        for entry in self.gridLinesStore_resid[axis]:
          entry.remove()
        self.gridLinesStore_resid[axis] = []

      # replace grid lines by custom lines
      allLineHandles = []
      if(useVisible):
        for targetAxis in axisobject:
          lines = []
          # determine positions for drawing new ones
          if(axis in ['x', 'x2']):
            ticks = np.array(targetAxis.xaxis.get_majorticklocs())
            if(axis == 'x2'):
              ticks = np.array(targetAxis.xaxis.get_majorticklocs())
            if(axis == 'x'):
              vmin, vmax = min(self.minX, self.maxX), max(self.minX, self.maxX)
            else:
              vmin, vmax = min(self.minX_div, self.maxX_div), max(self.minX_div, self.maxX_div)
            # convert ticks to axis coordinates
            ticks_norm = [targetAxis.transData.transform((i, 1))[0] for i in ticks]
            vmin_norm, vmax_norm = targetAxis.transData.transform((vmin, 1))[0], targetAxis.transData.transform((vmax, 1))[0]
            # filter for visible ticks
            cond1 = ticks >= vmin
            cond2 = ticks <= vmax
            cond3 = ~np.isclose(ticks_norm, vmin_norm)
            cond4 = ~np.isclose(ticks_norm, vmax_norm)
            ticks = ticks.compress(cond1 & cond2 & cond3 & cond4)
            # create plot coordinates
            if(target == 'resid'):
              minY, maxY = min(self.minResidY, self.maxResidY), max(self.minResidY, self.maxResidY)
            elif(targetAxis in [self.ax, self.ax_div]):
              minY, maxY = min(self.minY, self.maxY), max(self.minY, self.maxY)
            else:
              minY, maxY = min(self.minY2, self.maxY2), max(self.minY2, self.maxY2)
            for entry in ticks:
              lines.append([[entry, entry], [minY, maxY]])
          else:
            # don't draw axis lines for non-displayed second axes
            if((axis != 'y2') or self.isSecondAxesActive()):
              ticks = targetAxis.yaxis.get_majorticklocs()
              if(target == 'resid'):
                vmin, vmax = min(self.minResidY, self.maxResidY), max(self.minResidY, self.maxResidY)
              elif(axis == 'y'):
                vmin, vmax = min(self.minY, self.maxY), max(self.minY, self.maxY)
              else:
                vmin, vmax = min(self.minY2, self.maxY2), max(self.minY2, self.maxY2)
              # convert ticks to axis coordinates
              ticks_norm = [targetAxis.transData.transform((1, i))[1] for i in ticks]
              vmin_norm, vmax_norm = targetAxis.transData.transform((1, vmin))[1], targetAxis.transData.transform((1, vmax))[1]
              # filter for visible ticks
              cond1 = ticks >= vmin
              cond2 = ticks <= vmax
              cond3 = ~np.isclose(ticks_norm, vmin_norm)
              cond4 = ~np.isclose(ticks_norm, vmax_norm)
              ticks = ticks.compress(cond1 & cond2 & cond3 & cond4)
              # create plot coordinates
              if(targetAxis in [self.ax, self.ax2, self.ax_resid]):
                minX, maxX = min(self.minX, self.maxX), max(self.minX, self.maxX)
              else:
                minX, maxX = min(self.minX_div, self.maxX_div), max(self.minX_div, self.maxX_div)
              for entry in ticks:
                lines.append([[minX, maxX], [entry, entry]])
  
          # draw these lines
          for entry in lines:
            drawLine = matplotlib.lines.Line2D(entry[0], entry[1])
            allLineHandles.append(targetAxis.add_line(drawLine))
            # apply styles
            drawLine.set_color(useColor)
            drawLine.set_linewidth(useWidth)
            drawLine.set_linestyle(useStyle)
            drawLine.set_dash_capstyle(useDashStyle)
            drawLine.set_solid_capstyle(useDashStyle)
            drawLine.set_zorder(useZ)
            
        # store them for future manipulation
        if(target == 'plot'):
          self.gridLinesStore[axis].extend(allLineHandles)
        else:
          self.gridLinesStore_resid[axis].extend(allLineHandles)
          
      # call draw minor grid
      self.drawMinorAxisGrid(axis=axis, target=target, redraw=False)

      # now take care of axis shading
      if(target == 'plot'):
        for entry in self.gridRectStore[axis]:
          entry.remove()
        self.gridRectStore[axis] = []
      else:
        for entry in self.gridRectStore_resid[axis]:
          entry.remove()
        self.gridRectStore_resid[axis] = []
      
      useVisible, useColor = self.gridRectVisible[axis] and self.gridVisible[axis], self.gridRectColor[axis]
      if(axis in ['x', 'x2']):
        if(useVisible):
          if(target == 'plot'):
            ymin, ymax = min(self.minY, self.maxY), max(self.minY, self.maxY)
            if(axis == 'x'):
              candidateAxes = [self.ax]
            else:
              candidateAxes = [self.ax_div]
          else:
            ymin, ymax = min(self.minResidY, self.maxResidY), max(self.minResidY, self.maxResidY)
            if(axis == 'x'):
              candidateAxes = [self.ax_resid]
            else:
              candidateAxes = [self.ax_resid_div]
  
          for targetAxis in candidateAxes:
            ticks = np.array(targetAxis.xaxis.get_majorticklocs())
            allRectHandles = []
            for i in range(len(ticks))[::2]:
              if(i + 1 < len(ticks)):
                drawRect = matplotlib.patches.Rectangle(xy=(ticks[i], ymin), width=ticks[i + 1] - ticks[i], height=ymax - ymin)
                allRectHandles.append(targetAxis.add_patch(drawRect))
                drawRect.set_facecolor(useColor)
                drawRect.set_linewidth(0)
                drawRect.set_zorder(useZ + .1)
            if(target == 'plot'):
              self.gridRectStore[axis].extend(allRectHandles)
            else:
              self.gridRectStore_resid[axis].extend(allRectHandles)
            
          # consider split y axis
          if((target == 'plot') and self.splitY):
            ymin, ymax = min(self.minY2, self.maxY2), max(self.minY2, self.maxY2)
            if(axis == 'x'):
              candidateAxes = [self.ax2]
            else:
              candidateAxes = [self.ax2_div]
            
            for targetAxis in candidateAxes:
              ticks = np.array(targetAxis.xaxis.get_majorticklocs())
              allRectHandles = []
              for i in range(len(ticks))[::2]:
                if(i + 1 < len(ticks)):
                  drawRect = matplotlib.patches.Rectangle(xy=(ticks[i], ymin), width=ticks[i + 1] - ticks[i], height=ymax - ymin)
                  allRectHandles.append(targetAxis.add_patch(drawRect))
                  drawRect.set_facecolor(useColor)
                  drawRect.set_linewidth(0)
                  drawRect.set_zorder(useZ + .1)
              self.gridRectStore[axis].extend(allRectHandles)
      else:
        # y axes here
        # don't draw axis lines for non-displayed second axes
        if((axis != 'y2') or self.isSecondAxesActive()):
          if(useVisible):
            xmin, xmax = min(self.minX, self.maxX), max(self.minX, self.maxX)
            if(target == 'plot'):
              if(axis == 'y'):
                candidateAxes = [self.ax]
              else:
                candidateAxes = [self.ax2]
            else:
              candidateAxes = [self.ax_resid]
    
            for targetAxis in candidateAxes:
              ticks = np.array(targetAxis.yaxis.get_majorticklocs())
              allRectHandles = []
              for i in range(len(ticks))[::2]:
                if(i + 1 < len(ticks)):
                  drawRect = matplotlib.patches.Rectangle(xy=(xmin, ticks[i]), height=ticks[i + 1] - ticks[i], width=xmax - xmin)
                  allRectHandles.append(targetAxis.add_patch(drawRect))
                  drawRect.set_facecolor(useColor)
                  drawRect.set_linewidth(0)
                  drawRect.set_zorder(useZ + .1)
              if(target == 'plot'):
                self.gridRectStore[axis].extend(allRectHandles)
              else:
                self.gridRectStore_resid[axis].extend(allRectHandles)
              
            # consider split x axis
            if(self.splitShow):
              xmin, xmax = min(self.minX_div, self.maxX_div), max(self.minX_div, self.maxX_div)
              
              if(target == 'plot'):
                if(axis == 'y'):
                  candidateAxes = [self.ax_div]
                else:
                  candidateAxes = [self.ax2_div]
              else:
                candidateAxes = [self.ax_resid_div]
      
              for targetAxis in candidateAxes:
                ticks = np.array(targetAxis.yaxis.get_majorticklocs())
                allRectHandles = []
                for i in range(len(ticks))[::2]:
                  if(i + 1 < len(ticks)):
                    drawRect = matplotlib.patches.Rectangle(xy=(xmin, ticks[i]), height=ticks[i + 1] - ticks[i], width=xmax - xmin)
                    allRectHandles.append(targetAxis.add_patch(drawRect))
                    drawRect.set_facecolor(useColor)
                    drawRect.set_linewidth(0)
                    drawRect.set_zorder(useZ + .1)
                if(target == 'plot'):
                  self.gridRectStore[axis].extend(allRectHandles)
                else:
                  self.gridRectStore_resid[axis].extend(allRectHandles)

      # redraw
      if(redraw):
        plotobject.myRefresh()

  def drawMinorAxisGrid(self, axis='x', target='plot', redraw=False):
    # draws custom grid lines
    if(target in ['plot', 'resid']):
      if(target == 'plot'):
        # draw grid on 1st set of axes
        plotobject, axisobject = self.dataplotwidget, [self.ax, self.ax_div]
        if(axis == 'x'):
          axisobject = [self.ax]
          if(self.splitY):
            axisobject += [self.ax2]
        elif(axis == 'x2'):
          axisobject =[self.ax2_div]
          if(self.splitY):
            axisobject += [self.ax_div]
        elif(axis == 'y2'):
          axisobject = [self.ax2, self.ax2_div]
        useVisible, useColor, useWidth, useStyle, useDashStyle, useGridOrder = self.gridMinorVisible[axis], self.gridMinorColor[axis], self.gridMinorWidth[axis], self.gridMinorStyle[axis], self.gridMinorDashStyle[axis], self.gridMinorOrder[axis]
      else:
        plotobject, axisobject = self.residplotwidget, [self.ax_resid, self.ax_resid_div]
        if(axis == 'x'):
          axisobject = [self.ax_resid]
        elif(axis == 'x2'):
          axisobject =[self.ax_resid_div]
        useVisible, useColor, useWidth, useStyle, useDashStyle, useGridOrder = self.gridMinorVisible_resid[axis], self.gridMinorColor_resid[axis], self.gridMinorWidth_resid[axis], self.gridMinorStyle_resid[axis], self.gridMinorDashStyle_resid[axis], self.gridMinorOrder_resid[axis]
      
      if(useGridOrder == 'back'):
        useZ = 1
      else:
        useZ = 500
        
      # remove previous lines for this axis
      if(target == 'plot'):
        for entry in self.gridMinorLinesStore[axis]:
          entry.remove()
        self.gridMinorLinesStore[axis] = []
      else:
        for entry in self.gridMinorLinesStore_resid[axis]:
          entry.remove()
        self.gridMinorLinesStore_resid[axis] = []

      # replace grid lines by custom lines
      allLineHandles = []
      if(useVisible):
        for targetAxis in axisobject:
          lines = []
          # determine positions for drawing new ones
          if(axis in ['x', 'x2']):
            ticks = np.array(targetAxis.xaxis.get_minorticklocs())
            if(type(ticks) == type([])):
              ticks = np.array(ticks)
            if(axis == 'x2'):
              ticks = np.array(targetAxis.xaxis.get_minorticklocs())
            if(axis == 'x'):
              vmin, vmax = min(self.minX, self.maxX), max(self.minX, self.maxX)
            else:
              vmin, vmax = min(self.minX_div, self.maxX_div), max(self.minX_div, self.maxX_div)
            # convert ticks to axis coordinates
            ticks_norm = [targetAxis.transData.transform((i, 1))[0] for i in ticks]
            vmin_norm, vmax_norm = targetAxis.transData.transform((vmin, 1))[0], targetAxis.transData.transform((vmax, 1))[0]
            # filter for visible ticks
            cond1 = ticks >= vmin
            cond2 = ticks <= vmax
            cond3 = ~np.isclose(ticks_norm, vmin_norm)
            cond4 = ~np.isclose(ticks_norm, vmax_norm)
            ticks = ticks.compress(cond1 & cond2 & cond3 & cond4)
            # create plot coordinates
            if(target == 'resid'):
              minY, maxY = min(self.minResidY, self.maxResidY), max(self.minResidY, self.maxResidY)
            elif(targetAxis in [self.ax, self.ax_div]):
              minY, maxY = min(self.minY, self.maxY), max(self.minY, self.maxY)
            else:
              minY, maxY = min(self.minY2, self.maxY2), max(self.minY2, self.maxY2)
            for entry in ticks:
              lines.append([[entry, entry], [minY, maxY]])
          else:
            # don't draw axis lines for non-displayed second axes
            if((axis != 'y2') or self.isSecondAxesActive()):
              ticks = targetAxis.yaxis.get_minorticklocs()
              if(type(ticks) == type([])):
                ticks = np.array(ticks)
              if(target == 'resid'):
                vmin, vmax = min(self.minResidY, self.maxResidY), max(self.minResidY, self.maxResidY)
              elif(axis == 'y'):
                vmin, vmax = min(self.minY, self.maxY), max(self.minY, self.maxY)
              else:
                vmin, vmax = min(self.minY2, self.maxY2), max(self.minY2, self.maxY2)
              # convert ticks to axis coordinates
              ticks_norm = [targetAxis.transData.transform((1, i))[1] for i in ticks]
              vmin_norm, vmax_norm = targetAxis.transData.transform((1, vmin))[1], targetAxis.transData.transform((1, vmax))[1]
              # filter for visible ticks
              cond1 = ticks >= vmin
              cond2 = ticks <= vmax
              cond3 = ~np.isclose(ticks_norm, vmin_norm)
              cond4 = ~np.isclose(ticks_norm, vmax_norm)
              ticks = ticks.compress(cond1 & cond2 & cond3 & cond4)
              # create plot coordinates
              if(targetAxis in [self.ax, self.ax2, self.ax_resid]):
                minX, maxX = min(self.minX, self.maxX), max(self.minX, self.maxX)
              else:
                minX, maxX = min(self.minX_div, self.maxX_div), max(self.minX_div, self.maxX_div)
              for entry in ticks:
                lines.append([[minX, maxX], [entry, entry]])
  
          # draw these lines
          for entry in lines:
            drawLine = matplotlib.lines.Line2D(entry[0], entry[1])
            allLineHandles.append(targetAxis.add_line(drawLine))
            # apply styles
            drawLine.set_color(useColor)
            drawLine.set_linewidth(useWidth)
            drawLine.set_linestyle(useStyle)
            drawLine.set_dash_capstyle(useDashStyle)
            drawLine.set_solid_capstyle(useDashStyle)
            drawLine.set_zorder(useZ)
          
        # store them for future manipulation
        if(target == 'plot'):
          self.gridMinorLinesStore[axis].extend(allLineHandles)
        else:
          self.gridMinorLinesStore_resid[axis].extend(allLineHandles)

      # redraw
      if(redraw):
        plotobject.myRefresh()

  def updateTickEntryField(self, axis='x'):
    # updates tick entry field in graphics area when auto is selected
    if(axis in ['x', 'x2', 'y', 'y2', 'resid']):
      # check for existence of graphics tab (during initialization)
      if(axis == 'y2'):
        graphicsarea = 'graphicsarea2'
      else:
        graphicsarea = 'graphicsarea'
      if(hasattr(self.parent, graphicsarea)):
        if(axis == 'x'):
          tickAuto, tickEntry = self.parent.graphicsarea.configTickXAuto, self.parent.graphicsarea.configTickXEntry
        elif(axis == 'x2'):
          tickAuto, tickEntry = self.parent.graphicsarea.configInnerTickXAuto, self.parent.graphicsarea.configInnerTickXEntry
        elif(axis in ['y', 'y2']):
          tickAuto, tickEntry = self.parent.__dict__[graphicsarea].configTickYAuto, self.parent.__dict__[graphicsarea].configTickYEntry
        else:
          tickAuto, tickEntry = self.parent.graphicsarea.configTickResidYAuto, self.parent.graphicsarea.configTickResidYEntry
  
        if(tickAuto.isChecked()):
          nuTicks = self.getAxisTicks(axis)
          tickstr = self.parent.__dict__[graphicsarea].magicTickstring(nuTicks)
          tickEntry.setText(tickstr)

  def changeAxisMode(self, axis='x', redraw=True):
    # changes scale mode of axis
    if(axis == 'x'):
      # adjust axis
      axis_mode = str(self.modeSelectorx.currentText())
      if(axis_mode == 'linear'):
        # in this case, change axis scale first
        self.ax.set_xscale(axis_mode)
        self.ax2.set_xscale(axis_mode)
        self.ax_resid.set_xscale(axis_mode)
      self.setAxisLimits(lower = self.minX, upper = self.maxX, axis = 'x', updateLabel = False, target='plot', redraw=False, updateGrid=True)
      self.setAxisLimits(lower = self.minX, upper = self.maxX, axis = 'x', updateLabel = False, target='resid', redraw=False, updateGrid=True)
      if(axis_mode != 'linear'):
        # in this case, change axis scale last
        self.ax.set_xscale(axis_mode)
        self.ax2.set_xscale(axis_mode)
        self.ax_resid.set_xscale(axis_mode)
      self.modeX = axis_mode
      # trigger redrawing of fit function with new axis limits and minor ticks
      if(self.modeX == 'linear'):
        # reset minor ticks to 1 when changing to log scale
        if(self.ticksXMinor > 1):
          self.ticksXMinor, targetField = 2, self.parent.graphicsarea.configMinorTickX
          targetField.blockSignals(True)
          if(targetField.findText(str(1)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(1)))
          targetField.blockSignals(False)
        minorAutoticks = MyAutoMinorLocator(self.ticksXMinor)
        self.ax.xaxis.set_minor_locator(minorAutoticks)
        self.ax2.xaxis.set_minor_locator(minorAutoticks)
        self.ax_resid.xaxis.set_minor_locator(minorAutoticks)
        self.ax.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax2.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax_resid.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
      elif(self.modeX == 'log'):
        # reset minor ticks to 8 when changing to log scale
        if(self.ticksXMinor > 1):
          self.ticksXMinor, targetField = 9, self.parent.graphicsarea.configMinorTickX
          targetField.blockSignals(True)
          if(targetField.findText(str(8)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(8)))
          targetField.blockSignals(False)
        minorAutoticks = MyAutoMinorLocator(self.ticksXMinor)
        self.ax.xaxis.set_minor_locator(minorAutoticks)
        self.ax2.xaxis.set_minor_locator(minorAutoticks)
        self.ax_resid.xaxis.set_minor_locator(minorAutoticks)
        self.ax.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax2.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax_resid.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        
      self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.plotFunction(fitobject=self.parent.fit[self.parent.activeFit], x=[],\
                     handlePlot=self.parent.fit[self.parent.activeFit].handlePlot, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
      # copy in case split axes are shown
      curve = self.parent.fit[self.parent.activeFit]
      if(self.parent.plotArea.splitY and curve.onBothAxes):
        curve.duplicateForSplit()
      self.handleResidZero = self.plotResidZero(handleResidZero=self.handleResidZero, redraw=False)
      # adjust spine lines if needed
      for spine in ['left', 'right']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine, plotobject=self.matplot, axisobject=self.ax, target='plot', secondAxes=False)
          self.setAxisPositionHelper(axis=spine, plotobject=self.residplot, axisobject=self.ax_resid, target='resid', secondAxes=False)
      for spine in ['left2', 'right2']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.matplot, axisobject=self.ax2, target='plot', secondAxes=True)
      
      # update axis grid
      for target in ('plot', 'resid'):
        for item in ('x', 'y', 'y2'):
          if(self.gridVisible[item]):
            if((item != 'y2') or self.isSecondAxesActive()):
              self.drawAxisGrid(axis=item, redraw=False, target=target)
      
      # update tick entry field
      self.updateTickEntryField(axis=axis)
      # redraw
      if(redraw):
        self.dataplotwidget.myRefresh()
        self.residplotwidget.myRefresh()
    elif(axis == 'x2'):
      # adjust axis
      axis_mode = str(self.xSplitModeSelectorx.currentText())
      if(axis_mode == 'linear'):
        # in this case, change axis scale first
        self.ax_div.set_xscale(axis_mode)
        self.ax2_div.set_xscale(axis_mode)
        self.ax_resid_div.set_xscale(axis_mode)
      self.setAxisLimits(lower = self.minX_div, upper = self.maxX_div, axis = 'x2', updateLabel = False, target='plot', redraw=False, updateGrid=True)
      self.setAxisLimits(lower = self.minX_div, upper = self.maxX_div, axis = 'x2', updateLabel = False, target='resid', redraw=False, updateGrid=True)
      if(axis_mode != 'linear'):
        # in this case, change axis scale last
        self.ax_div.set_xscale(axis_mode)
        self.ax2_div.set_xscale(axis_mode)
        self.ax_resid_div.set_xscale(axis_mode)
      self.modeX_div = axis_mode
      # trigger redrawing of fit function with new axis limits and minor ticks
      if(self.modeX_div == 'linear'):
        # reset minor ticks to 1 when changing to log scale
        if(self.ticksXMinor_div > 1):
          self.ticksXMinor_div, targetField = 2, self.parent.graphicsarea.configInnerMinorTickX
          targetField.blockSignals(True)
          if(targetField.findText(str(1)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(1)))
          targetField.blockSignals(False)
        minorAutoticks2 = MyAutoMinorLocator(self.ticksXMinor_div)
        self.ax2_div.xaxis.set_minor_locator(minorAutoticks2)
        self.ax_resid_div.xaxis.set_minor_locator(minorAutoticks2)
        self.ax2_div.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax_resid_div.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
      elif(self.modeX_div == 'log'):
        # reset minor ticks to 9 when changing to log scale
        if(self.ticksXMinor_div > 1):
          self.ticksXMinor_div, targetField = 9, self.parent.graphicsarea.configInnerMinorTickX
          targetField.blockSignals(True)
          if(targetField.findText(str(8)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(8)))
          targetField.blockSignals(False)
        minorAutoticks2 = MyAutoMinorLocator(self.ticksXMinor_div + 1)
        self.ax2_div.xaxis.set_minor_locator(minorAutoticks2)
        self.ax_resid_div.xaxis.set_minor_locator(minorAutoticks2)
        self.ax2_div.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        # need to also call null formatter on overlaid axis to prevent minor labels from showing
        self.ax_div.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax_resid_div.xaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        
      self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.plotFunction(fitobject=self.parent.fit[self.parent.activeFit], x=[],\
                     handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
      # copy in case split axes are shown
      curve = self.parent.fit[self.parent.activeFit]
      if(self.parent.plotArea.splitY and curve.onBothAxes):
        curve.duplicateForSplit()
      self.handleResidZero_div = self.plotResidZero(handleResidZero=self.handleResidZero_div, redraw=False, splitX=True)

      # adjust spine lines if needed
      for spine in ['left', 'right']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine, plotobject=self.matplot, axisobject=self.ax_div, target='plot', secondAxes=False, splitX=True)
          self.setAxisPositionHelper(axis=spine, plotobject=self.residplot, axisobject=self.ax_resid_div, target='resid', secondAxes=False, splitX=True)
      for spine in ['left2', 'right2']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine[:-1], plotobject=self.matplot, axisobject=self.ax2_div, target='plot', secondAxes=True, splitX=True)

      # update axis grid
      for target in ('plot', 'resid'):
        for item in ('x2', 'y', 'y2'):
          if(self.gridVisible[item]):
            if((item != 'y2') or self.isSecondAxesActive()):
              self.drawAxisGrid(axis=item, redraw=False, target=target)
      
      # update tick entry field
      self.updateTickEntryField(axis=axis)
      # redraw
      if(redraw):
        self.dataplotwidget.myRefresh()
        self.residplotwidget.myRefresh()
    elif(axis == 'y'):
      # adjust axis
      axis_mode = str(self.modeSelectory.currentText())
      if(axis_mode == 'linear'):
        # in this case, change axis scale first
        self.ax.set_yscale(axis_mode)
        self.ax_div.set_yscale(axis_mode)
      self.setAxisLimits(lower = self.minY, upper = self.maxY, axis = 'y', updateLabel = False, redraw=False, updateGrid=True)
      if(axis_mode != 'linear'):
        # in this case, change axis scale last
        self.ax.set_yscale(axis_mode)
        self.ax_div.set_yscale(axis_mode)
      self.modeY = axis_mode
      if(self.modeY == 'linear'):
        # reset minor ticks to 1 when changing to log scale
        if(self.ticksYMinor > 1):
          self.ticksYMinor, targetField = 2, self.parent.graphicsarea.configMinorTickY
          targetField.blockSignals(True)
          if(targetField.findText(str(1)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(1)))
          targetField.blockSignals(False)
        minorAutoticks = MyAutoMinorLocator(self.ticksYMinor)
        self.ax.yaxis.set_minor_locator(minorAutoticks)
        self.ax_div.yaxis.set_minor_locator(minorAutoticks)
        self.ax.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax_div.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
      else:
        # reset minor ticks to 9 when changing to log scale
        if(self.ticksYMinor > 1):
          self.ticksYMinor, targetField = 9, self.parent.graphicsarea.configMinorTickY
          targetField.blockSignals(True)
          if(targetField.findText(str(8)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(8)))
          targetField.blockSignals(False)
        minorAutoticks = MyAutoMinorLocator(self.ticksYMinor + 1)
        self.ax.yaxis.set_minor_locator(minorAutoticks)
        self.ax_div.yaxis.set_minor_locator(minorAutoticks)
        self.ax.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax_div.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
      # adjust spine lines if needed
      for spine in ['bottom', 'top']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine, plotobject=self.matplot, axisobject=self.ax, target='plot', secondAxes=False)
          self.setAxisPositionHelper(axis=spine, plotobject=self.matplot, axisobject=self.ax_div, target='plot', secondAxes=False, splitX=True)

      # update axis grid
      target = 'plot'
      for item in ('x', 'x2', 'y'):
        if(self.gridVisible[item]):
          self.drawAxisGrid(axis=item, redraw=False, target=target)
      
      # update tick entry field
      self.updateTickEntryField(axis=axis)
      # redraw
      if(redraw):
        self.dataplotwidget.myRefresh()
    elif(axis == 'y2'):
      # adjust axis
      axis_mode = str(self.secondModeSelector.currentText())
      if(axis_mode == 'linear'):
        # in this case, change axis scale first
        self.ax2.set_yscale(axis_mode)
        self.ax2_div.set_yscale(axis_mode)
      self.setAxisLimits(lower=self.minY2, upper=self.maxY2, axis='y2', updateLabel=False, redraw=False, updateGrid=True)
      if(axis_mode != 'linear'):
        # in this case, change axis scale last
        self.ax2.set_yscale(axis_mode)
        self.ax2_div.set_yscale(axis_mode)
      self.modeY2 = axis_mode
      if(self.modeY2 == 'linear'):
        # reset minor ticks to 1 when changing to log scale
        if(self.ticksY2Minor > 1):
          self.ticksY2Minor, targetField = 2, self.parent.graphicsarea2.configMinorTickY
          targetField.blockSignals(True)
          if(targetField.findText(str(1)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(1)))
          targetField.blockSignals(False)        
        minorAutoticks = MyAutoMinorLocator(self.ticksY2Minor)
        self.ax2.yaxis.set_minor_locator(minorAutoticks)
        self.ax2_div.yaxis.set_minor_locator(minorAutoticks)
        self.ax2.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax2_div.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
      else:
        # reset minor ticks to 9 when changing to log scale
        if(self.ticksY2Minor > 1):
          self.ticksY2Minor, targetField = 9, self.parent.graphicsarea2.configMinorTickY
          targetField.blockSignals(True)
          if(targetField.findText(str(8)) >= 0):
            targetField.setCurrentIndex(targetField.findText(str(8)))
          targetField.blockSignals(False)        
        minorAutoticks = MyAutoMinorLocator(self.ticksY2Minor + 1)
        self.ax2.yaxis.set_minor_locator(minorAutoticks)
        self.ax2_div.yaxis.set_minor_locator(minorAutoticks)
        self.ax2.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
        self.ax2_div.yaxis.set_minor_formatter(matplotlib.ticker.NullFormatter())
      # adjust spine lines if needed
      for spine in ['bottom', 'top']:
        if(self.axisPosition[spine] == 'data'):
          self.setAxisPositionHelper(axis=spine, plotobject=self.matplot, axisobject=self.ax2, target='plot', secondAxes=False)
          self.setAxisPositionHelper(axis=spine, plotobject=self.matplot, axisobject=self.ax2_div, target='plot', secondAxes=False, splitX=True)

      # update axis grid
      target = 'plot'
      for item in ('x', 'x2', 'y2'):
        if(self.gridVisible[item]):
          if((item != 'y2') or self.isSecondAxesActive()):
            self.drawAxisGrid(axis=item, redraw=False, target=target)
      
      # update tick entry field
      self.updateTickEntryField(axis=axis)
      # redraw
      if(redraw):
        self.dataplotwidget.myRefresh()

  def plotResidZero(self, handleResidZero=None, redraw=False, splitX=False):
    # plots zero line in residuals plot
    if(splitX):
      self.handleResidZero_div = handleResidZero
      # set up data
      xval = [self.minX_div, self.maxX_div]
      yval = [0, 0]
      # do the actual plot
      if(self.handleResidZero_div != None):
        #self.handleData.remove()
        self.handleResidZero_div.set_xdata(xval)
        self.handleResidZero_div.set_ydata(yval)
      else:
        self.handleResidZero_div, = self.ax_resid_div.plot(xval, yval, 'ko', zorder = 0.5  + self.parent.zOffset)
      handleResidZero = self.handleResidZero_div
    else:
      self.handleResidZero = handleResidZero
      # set up data
      xval = [self.minX, self.maxX]
      yval = [0, 0]
      # do the actual plot
      if(self.handleResidZero != None):
        #self.handleData.remove()
        self.handleResidZero.set_xdata(xval)
        self.handleResidZero.set_ydata(yval)
      else:
        self.handleResidZero, = self.ax_resid.plot(xval, yval, 'ko', zorder = 0.5  + self.parent.zOffset)
      handleResidZero = self.handleResidZero
      
    # apply style of fit curve to zero line
    style = self.parent.data[self.parent.activeData].getResidLineStyle()
    for key in style:
      method = 'set_' + key
      if (hasattr(handleResidZero, method)):
        method2call = getattr(handleResidZero, method)
        method2call(style[key])
    if(not style['doGap']):
      if(hasattr(handleResidZero, 'set_gapcolor')):
        handleResidZero.set_gapcolor('none')
    # ensure only line is visible but not markers
    if (hasattr(handleResidZero, 'set_linestyle')):
      if(style['linestyle'] == 'None'):
        value = 'solid'
      else:
        value = style['linestyle']
      method2call = getattr(handleResidZero, 'set_linestyle')
      method2call(value)        
    if (hasattr(handleResidZero, 'set_marker')):
      method2call = getattr(handleResidZero, 'set_marker')
      method2call('None')
        
    # do redraw?
    if(redraw):
      self.residplotwidget.myRefresh()
    
    return handleResidZero

  def plotResid(self, dataobject=None, handleResid=None, handleResidZero=None, handleResidBar=None, handleResidStack=None, handleResidStackNeg=None, handleResidText=None, handleResidScatter=None, redraw=True, splitX=False, rescale=True):
    # was dataobject specified?
    if(dataobject == None):
      dataobject = self.parent.data[self.parent.activeData]

    if(splitX):
      useAxes = self.ax_resid_div
    else:
      useAxes = self.ax_resid
      
    # analyze data
    xval = dataobject.x
    yval = dataobject.resid
    if(yval.size > 0):
      # plot residuals
      if(handleResid != None):
        handleResid.set_xdata(xval)
        handleResid.set_ydata(yval)
      else:
        handleResid, = useAxes.plot(xval, yval, 'ko', zorder = dataobject.zorder + self.parent.zOffset)
        if(not splitX):
          handleResid.set_pickradius(PICK_TOLERANCE)
        
      # draw scatter graphics
      if(handleResidScatter != None):
        handleResidScatter.remove()
      if(dataobject.ResidScatterstyle['showScatter']):
        handleResidScatter = useAxes.scatter(xval, yval, zorder=dataobject.zorder + self.parent.zOffset, marker=dataobject.ResidScatterstyle['marker'])
        if(not splitX):
          handleResidScatter.set_pickradius(PICK_TOLERANCE)
      else:
        handleResidScatter = None
      
      # draw bar?
      if(handleResidBar != None):
        handleResidBar.remove()

      if((dataobject.ResidBarstyle['showBar']) and (dataobject.ResidBarstyle['offset'] != 0)):
        # also use this for error bars
        useOffset = dataobject.ResidBarstyle['offset']
      else:
        useOffset = 0
  
      if(dataobject.ResidBarstyle['showBar']):
        barstyle = dataobject.getResidBarStyle()
        if('width' in barstyle):
          usewidth = barstyle['width']
        else:
          usewidth = 0.5

        handleResidBar = useAxes.bar(xval + useOffset, yval, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, align='center', width=usewidth)
      else:
        handleResidBar = None

      # draw stack?
      if(handleResidStack != None):
        handleResidStack.remove()
      if(handleResidStackNeg != None):
        handleResidStackNeg.remove()

      if(dataobject.ResidStackstyle['showStack']):
        posVals = (yval >= 0)
        posCount = [i for i in posVals if i]
        negCount = [i for i in posVals if (not i)]
        if(len(posCount)):
          handleResidStack, = useAxes.stackplot(xval, yval, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, where=(yval >= 0), interpolate=True)
          if(not splitX):
            handleResidStack.set_pickradius(PICK_TOLERANCE)
        else:
          handleResidStack = None
        if(len(negCount)):
          handleResidStackNeg, = useAxes.stackplot(xval, yval, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, where=(yval < 0), interpolate=True)
          if(not splitX):
            handleResidStackNeg.set_pickradius(PICK_TOLERANCE)
        else:
          handleResidStackNeg = None
      else:
        handleResidStack = None
        handleResidStackNeg = None

      # apply styles
      style = dataobject.getResidStyle()
      for key in style:
        method = 'set_' + key
        if (hasattr(handleResid, method)):
          method2call = getattr(handleResid, method)
          if((key == 'marker') and (style[key] in CUSTOM_MARKERS)):
            method2call(CUSTOM_MARKERS[style[key]])
          elif((key == 'marker') and (not (style[key] in matplotlib.lines.Line2D.markers))):
            # this is to ensure compatibility with future versions that may introduce markers we don't know yet
            method2call('o')
          else:
            method2call(style[key])
      if(not style['doFill']):
        handleResid.set_markerfacecolor('none')

      # apply scatter styles
      style = dataobject.getResidScatterStyle()
      for key in style:
        method = 'set_' + key
        if((key != 'marker') and hasattr(handleResidScatter, method)):
          if(key.startswith('sizes')):
            if(style['sizes__type'] in ['x', 'y']):
              sizesMultiply = dataobject.__dict__[style['sizes__type']]
            elif(style['sizes__type'] == 'labels'):
              sizesMultiply = dataobject.convertLabels2Numbers(labels2=False)
            elif(style['sizes__type'] == 'labels2'):
              sizesMultiply = dataobject.convertLabels2Numbers(labels2=True)
            else:
              sizesMultiply = np.ones(len(dataobject.x))
            
            handleResidScatter.set_sizes((style['sizes'] ** 2) * np.abs(sizesMultiply))
          elif(key.startswith('facecolor') or key.startswith('doFill')):
            if(not style['doFill']):
              # set transparent fill in this case
              facecolors = [[0.] * 4] * len(dataobject.x)
            elif(style['facecolor__type'] == 'const'):
              facecolors = [style['facecolor']] * len(dataobject.x)
            else:
              if(style['facecolor__type'] in ['x', 'y']):
                colorValue = dataobject.__dict__[style['facecolor__type']]
              elif(style['facecolor__type'] == 'labels'):
                colorValue = dataobject.convertLabels2Numbers(labels2=False)
              elif(style['facecolor__type'] == 'labels2'):
                colorValue = dataobject.convertLabels2Numbers(labels2=True)
              else:
                colorValue = np.ones(len(dataobject.x))
              # get color values
              facecolors = dataobject.convertNumbers2ColorMap(colorValue=colorValue, cmap=style['facecolor__cmap'])
            handleResidScatter.set_facecolor(facecolors)
          elif(key.startswith('edgecolor')):
            if(style['edgecolor__type'] == 'const'):
              facecolors = [style['edgecolor']] * len(dataobject.x)
            else:
              if(style['edgecolor__type'] in ['x', 'y']):
                colorValue = dataobject.__dict__[style['edgecolor__type']]
              elif(style['edgecolor__type'] == 'labels'):
                colorValue = dataobject.convertLabels2Numbers(labels2=False)
              elif(style['edgecolor__type'] == 'labels2'):
                colorValue = dataobject.convertLabels2Numbers(labels2=True)
              else:
                colorValue = np.ones(len(dataobject.x))
              # get color values
              facecolors = dataobject.convertNumbers2ColorMap(colorValue=colorValue, cmap=style['edgecolor__cmap'])
            handleResidScatter.set_edgecolor(facecolors)
          else:
            method2call = getattr(handleResidScatter, method)
            method2call([style[key]])

      # draw text labels?
      if(handleResidText != None):
        for entry in handleResidText:
          entry.remove()
        handleResidText = None
          
      if(dataobject.ResidTextstyle['showText']):
        textstyle = dataobject.getResidTextStyle()
        # prepare list of data points
        labels = [''] * len(xval); useYFlag = False
        if(('type' in textstyle) and (textstyle['type'] == 'labels')):
          labels = dataobject.getLabels()
          if(len(labels) != len(xval)):
            labels = [''] * len(xval)
            self.parent.statusbar.showMessage('Cannot use label information, will use y values instead.', self.parent.STATUS_TIME, color='blue')
            useYFlag = True
        comboList = [[i, j, k] for i, j, k  in zip(xval, yval, labels)]
        if(textstyle['skip']):
          comboList = comboList[::textstyle['skip'] + 1]

        if(len(comboList)):
          handleResidText = []
          if(('padX' in textstyle) and ('padY' in textstyle)):
            padX, padY = textstyle['padX'], textstyle['padY']
          else:
            padX, padY = 0.0, 0.0
          for entry in comboList:
            x, y, label = entry
            if(textstyle['type'] == 'x'):
              output = self.parent.formatNumber(x)
            elif((textstyle['type'] == 'y') or useYFlag):
              output = self.parent.formatNumber(y)
            else:
              output = label
              # deal with newline and tab-stop characters
              if(type(output) != type('tse')):
                output = str(output)
              output = output.replace('\n', '\\n')
              output = output.replace('\t', '\\t')
              output = '\n'.join([i for i in output.split('\\n') if (len(i.strip()))])
              output = '\t'.join([i for i in output.split('\\t') if (len(i.strip()))])

            if(len(output)):
              handleResidText.append(useAxes.text(x + padX + useOffset, y + padY, output, horizontalalignment='center', verticalalignment='bottom', zorder=dataobject.zorder + dataobject.relativeZOrderText + self.parent.zOffset))
              handleResidText[-1].set_clip_on(True)

      # plot zero line
      if(splitX):
        self.handleResidZero_div = self.plotResidZero(self.handleResidZero_div, False, splitX=True)
        handleResidZero = self.handleResidZero_div
      else:
        self.handleResidZero = self.plotResidZero(self.handleResidZero, False)
        self.handleResidZero.set_pickradius(PICK_TOLERANCE)
        handleResidZero = self.handleResidZero
          
      # adjust x-axis limits to those of main plot
      if((not splitX) and rescale):
        self.setAxisLimits(lower = self.minX, upper = self.maxX, axis = 'x', updateLabel = False, target='resid', redraw=False, updateGrid=True)
      
        # auto adjust y limits
        # need to take care of potential nan and inf values
        procval = [i for i in yval if ((not np.isnan(i)) and (not np.isinf(i)))]
        # do we have selectedData active?
        if(self.parent.selectedData.isLive):
          values, roles = self.parent.selectedData.getData_n_Fit()
          if('resid' in roles):
            residVal = values[:, roles.index('resid')]
            residVal = [i for i in residVal if ((not np.isnan(i)) and (not np.isinf(i)))]
            if(len(residVal) > 1):
              # require at least two data points here
              procval = residVal
              
        if(len(procval)):
          temp_ylimit = np.max([np.abs(i) for i in procval])
          # ensure minimum limit
          if (temp_ylimit == 0):
            self.maxResidY = self.EPSILON; self.minResidY = -self.EPSILON
          else:
            self.maxResidY = 1.2 * temp_ylimit; self.minResidY = -1.2 * temp_ylimit
          self.setAxisLimits(lower = self.minResidY, upper = self.maxResidY, axis = 'y', updateLabel = True, target='resid', redraw=False, updateGrid=True)
          self.updateTickEntryField(axis='resid')
          
      # apply bar styles
      if(dataobject.ResidBarstyle['showBar']):
        barstyle = dataobject.getResidBarStyle()
        for entry in handleResidBar.patches:
          for key in barstyle:
            method = 'set_' + key
            # treat width differently to avoid recentering of bars upon width change (does not heed center position)
            if ((key != 'width') and (hasattr(entry, method))):
              method2call = getattr(entry, method)
              # extra check b/c of hatchMultiply
              if((key == 'hatch') and ('hatchMultiply' in barstyle)):
                method2call(barstyle[key] * barstyle['hatchMultiply'])
              else:
                method2call(barstyle[key])
            # set alternate color?
            if((entry.get_height() < 0) and ('facecolorAlt' in barstyle)):
              entry.set_facecolor(barstyle['facecolorAlt'])
        # check visibility of entire object
        if(not dataobject.visibilityResid):
          for entry in handleResidBar.patches:
            entry.set_visible(False)

        # special treatment for fill of error markers
        if(not dataobject.ResidBarstyle['doFill']):
          for entry in handleResidBar.patches:
            entry.set_facecolor('none')
 
      # apply stack styles
      if(dataobject.ResidStackstyle['showStack']):
        stackstyle = dataobject.getResidStackStyle()
        for handle in [handleResidStack, handleResidStackNeg]:
          if(handle != None):
            for key in stackstyle:
              method = 'set_' + key
              if (hasattr(handle, method)):
                method2call = getattr(handle, method)
                # extra check b/c of hatchMultiply
                if((key == 'hatch') and ('hatchMultiply' in stackstyle)):
                  method2call(stackstyle[key] * stackstyle['hatchMultiply'])
                else:
                  method2call(stackstyle[key])
            # set alternate color?
            if((handle == handleResidStackNeg) and ('facecolorAlt' in stackstyle)):
              handle.set_facecolor(stackstyle['facecolorAlt'])
            else:
              handle.set_facecolor(stackstyle['facecolor'])
        # check visibility of entire object
        if(not dataobject.visibilityResid):
          handleResidStack.set_visible(False)
          handleResidStackNeg.set_visible(False)

        # special treatment for fill of error markers
        if(not dataobject.ResidStackstyle['doFill']):
          for entry in [handleResidStack, handleResidStackNeg]:
            entry.set_facecolor('none')

      # apply text styles
      if(dataobject.ResidTextstyle['showText']):
        textstyle = dataobject.getResidTextStyle()
        if((handleResidText != None) and (len(handleResidText))):
          # apply styles
          for key in textstyle:
            method = 'set_' + key
            if(hasattr(handleResidText[0], method)):
              value = textstyle[key]###; safeFont = 'DejaVu Sans'
              for item in handleResidText:
                method2call = getattr(item, method)
                method2call(value)
                if(key == 'fontname'):
                  # test last element descent, should be larger than zero
                  _, _, descent = item._get_layout(self.matplot.canvas.renderer)
                  if(not (descent > 0)):
                    self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                    value = SAFE_FONT
                    method2call(value)
          # check visibility of entire object
          if(not dataobject.visibilityResid):
            for item in handleResidText:
              if(hasattr(item, 'set_visible')):
                item.set_visible(False)

      # probably not really needed
      if(1):
        if(splitX):
          # assign handles
          self.handleResid_div = handleResid
          self.handleResidZero_div = handleResidZero
          self.handleResidBar_div = handleResidBar
          self.handleResidStack_div = handleResidStack
          self.handleResidStackNeg_div = handleResidStackNeg
          self.handleResidText_div = handleResidText
          self.handleResidScatter_div = handleResidScatter
        else:
          # assign handles
          self.handleResid = handleResid
          self.handleResidZero = handleResidZero
          self.handleResidBar = handleResidBar
          self.handleResidStack = handleResidStack
          self.handleResidStackNeg = handleResidStackNeg
          self.handleResidText = handleResidText
          self.handleResidScatter = handleResidScatter

      # draw everything
      if(redraw):
        self.residplotwidget.myRefresh()

      return handleResid, handleResidZero, handleResidBar, handleResidStack, handleResidStackNeg, handleResidText, handleResidScatter
    else:
      if(handleResid != None):
        handleResid.remove()
      if(handleResidBar != None):
        handleResidBar.remove()
      if(handleResidStack != None):
        handleResidStack.remove()
      if(handleResidStackNeg != None):
        handleResidStackNeg.remove()
      if(handleResidText != None):
        for entry in handleResidText:
          entry.remove()
      if(handleResidScatter != None):
        handleResidScatter.remove()

      return None, handleResidZero, None, None, None, None, None
    
  def plotData(self, data, dataobject=None, handleData=None, handleErr=None, handleErrShady=None, handleBar=None, handleStack=None, handleStackNeg=None, handleText=None, handleViolin=None, handleScatter=None, redraw=True, rescale=True, splitX=False, autoIgnoreCurrentY=False):
    # was dataobject specified?
    if(dataobject == None):
      dataobject = self.parent.data[self.parent.activeData]
    
    # analyze data
    xerr, yerr = np.array([]), np.array([])
    if (('x' in data) and ('y' in data)):
      # okay found valid data, now assign values
      xval = data['x']
      yval = data['y']
      
      if ('xerr' in data):
        xerr = data['xerr']
        
      if ('yerr' in data):
        yerr = data['yerr']
        
      # determine which axes to draw on
      if(dataobject.onSecondAxes):
        useAxes = self.ax2
        if(splitX):
          useAxes = self.ax2_div
      else:
        useAxes = self.ax
        if(splitX):
          useAxes = self.ax_div
        
      # can do some plotting
      if((handleData != None) and (handleData.axes == useAxes)):
        handleData.set_xdata(xval)
        handleData.set_ydata(yval)
      else:
        if((handleData != None) and (handleData.axes != useAxes)):
          handleData.remove()
        handleData, = useAxes.plot(xval, yval, 'ko', zorder=dataobject.zorder + self.parent.zOffset)
      if(not splitX):
        handleData.set_pickradius(PICK_TOLERANCE)

      # draw scatter graphics
      if(handleScatter != None):
        handleScatter.remove()
      if(dataobject.Scatterstyle['showScatter']):
        handleScatter = useAxes.scatter(xval, yval, zorder=dataobject.zorder + self.parent.zOffset, marker=dataobject.Scatterstyle['marker'])
        if(not splitX):
          handleScatter.set_pickradius(PICK_TOLERANCE)
      else:
        handleScatter = None
      
      # draw bar?
      if(handleBar != None):
        handleBar.remove()

      if((dataobject.Barstyle['showBar']) and (dataobject.Barstyle['offset'] != 0)):
        # also use this for error bars
        useOffset = dataobject.Barstyle['offset']
      else:
        useOffset = 0
  
      if(dataobject.Barstyle['showBar']):
        barstyle = dataobject.getBarStyle()
        if('width' in barstyle):
          usewidth = barstyle['width']
        else:
          usewidth = 0.5
        if('bottom' in barstyle):
          bottom = barstyle['bottom']
        else:
          bottom = 0
        if('horizontal' in barstyle):
          horizontal = barstyle['horizontal']
        else:
          horizontal = False

        if(not horizontal):
          handleBar = useAxes.bar(xval + useOffset, yval - bottom, bottom=bottom, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, align='center', width=usewidth)
        else:
          handleBar = useAxes.barh(xval + useOffset, yval - bottom, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, align='center', height=usewidth, left=bottom)
      else:
        handleBar = None

      # draw stack?
      if(handleStack != None):
        handleStack.remove()
      if(handleStackNeg != None):
        handleStackNeg.remove()

      if(dataobject.Stackstyle['showStack']):
        # do we have a baseline to take care of?
        if(not (np.isclose(dataobject.Stackstyle['bottom'], 0))):
          shiftos, shiftValue = True, dataobject.Stackstyle['bottom']
          posVals = (yval >= shiftValue)
        else:
          shiftos, shiftValue = False, 0.
          posVals = (yval >= 0)
        posCount = [i for i in posVals if i]
        negCount = [i for i in posVals if (not i)]
        if(len(posCount)):
          if(shiftos):
            moep, handleStack, = useAxes.stackplot(xval, [shiftValue for i in yval], yval - shiftValue, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, where=(yval >= shiftValue), interpolate=True)
            moep.remove()
          else:
            handleStack, = useAxes.stackplot(xval, yval, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, where=(yval >= 0), interpolate=True)
          if(not splitX):
            handleStack.set_pickradius(PICK_TOLERANCE)
        else:
          handleStack = None
        if(len(negCount)):
          if(shiftos):
            moep, handleStackNeg, = useAxes.stackplot(xval, [shiftValue for i in yval], yval - shiftValue, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, where=(yval < shiftValue), interpolate=True)
            moep.remove()
          else:
            handleStackNeg, = useAxes.stackplot(xval, yval, zorder=dataobject.zorder + dataobject.relativeZOrderBar + self.parent.zOffset, where=(yval < 0), interpolate=True)
          if(not splitX):
            handleStackNeg.set_pickradius(PICK_TOLERANCE)
        else:
          handleStackNeg = None
      else:
        handleStack = None
        handleStackNeg = None

      # draw error bars?
      if (handleErr != None):
        handleErr[0].remove()
        for entry in handleErr[1]:
          entry.remove()
        for entry in handleErr[2]:
          entry.remove()
          
      # need to ensure that all errors are positive numbers
      if(xerr.size):
        xerr = np.abs(xerr)
      if(yerr.size):
        yerr = np.abs(yerr)
          
      # prepare errors for drawing them in two direction
      drawXerr, drawYerr = xerr, yerr
      if(dataobject.Errorstyle['direction'] != 'both'):
        if(dataobject.Errorstyle['direction'] == 'negative'):
          if(xerr.size):
            drawXerr = [xerr, 0.0 * xerr]
          if(yerr.size):
            drawYerr = [yerr, 0.0 * yerr]
        else:
          if(xerr.size):
            drawXerr = [0.0 * xerr, xerr]
          if(yerr.size):
            drawYerr = [0.0 * yerr, yerr]
      
      if(xerr.size):
        if(yerr.size):
          handleErr = useAxes.errorbar(xval + useOffset, yval, xerr = drawXerr, yerr = drawYerr, zorder = dataobject.zorder + dataobject.relativeZOrderError + self.parent.zOffset, capsize = 1)#, fmt = 'o')
        else:
          handleErr = useAxes.errorbar(xval + useOffset, yval, xerr = drawXerr, zorder = dataobject.zorder + dataobject.relativeZOrderError + self.parent.zOffset, capsize = 1)#, fmt = 'o')
      elif(yerr.size):
        handleErr = useAxes.errorbar(xval + useOffset, yval, yerr = drawYerr, zorder = dataobject.zorder + dataobject.relativeZOrderError  + self.parent.zOffset, capsize = 1)#, fmt = 'o')
      else:
        handleErr = None
        
      if(handleErr != None):
        # don't draw the error curve
        handleErr[0].set_linestyle('None')
        handleErr[0].set_marker('None')
        
      # should we draw the error intervals as shaded region?
      if(handleErrShady != None):
        handleErrShady.remove()
        handleErrShady = None
      if((dataobject.ErrorShadystyle['visible']) and (yerr.size)):
        useYerrPos, useYerrNeg = 0.0 * yerr, 0.0 * yerr
        
        if(dataobject.Errorstyle['direction'] in ['both', 'positive']):
          useYerrPos = yerr
        if(dataobject.Errorstyle['direction'] in ['both', 'negative']):
          useYerrNeg = yerr
        handleErrShady = useAxes.fill_between(xval + useOffset, yval - useYerrNeg, yval + useYerrPos, zorder = dataobject.zorder + dataobject.relativeZOrderError + self.parent.zOffset - 0.05)
            
      # draw text labels?
      if(handleText != None):
        for entry in handleText:
          entry.remove()
        handleText = None
          
      if(dataobject.Textstyle['showText']):
        textstyle = dataobject.getTextStyle()
        # prepare list of data points
        labels = [''] * len(xval); useYFlag = False
        if(('type' in textstyle) and (textstyle['type'] == 'labels')):
          labels = dataobject.getLabels()
          if(len(labels) != len(xval)):
            labels = [''] * len(xval)
            self.parent.statusbar.showMessage('Cannot use label information, will use y values instead.', self.parent.STATUS_TIME, color='blue')
            useYFlag = True
        comboList = [[i, j, k] for i, j, k  in zip(xval, yval, labels)]
        if(textstyle['skip']):
          comboList = comboList[::textstyle['skip'] + 1]

        if(len(comboList)):
          handleText = []
          if(('padX' in textstyle) and ('padY' in textstyle)):
            padX, padY = textstyle['padX'], textstyle['padY']
          else:
            padX, padY = 0.0, 0.0
          for entry in comboList:
            x, y, label = entry
            if(textstyle['type'] == 'x'):
              output = self.parent.formatNumber(x)
            elif((textstyle['type'] == 'y') or useYFlag):
              output = self.parent.formatNumber(y)
            elif(type(label) in [str, np.str_]):
              output = label
              # deal with newline and tab-stop characters
              if(type(output) != type('tse')):
                output = str(output)
              output = output.replace('\n', '\\n')
              output = output.replace('\t', '\\t')
              output = '\n'.join([i for i in output.split('\\n') if (len(i.strip()))])
              output = '\t'.join([i for i in output.split('\\t') if (len(i.strip()))])
            else:
              output = str(label)
            if(len(output)):
              handleText.append(useAxes.text(x + padX + useOffset, y + padY, output, horizontalalignment='center', verticalalignment='bottom', zorder=dataobject.zorder + dataobject.relativeZOrderText + self.parent.zOffset))
              handleText[-1].set_clip_on(True)
     
      # draw violin plot
      if(handleViolin != None):
        for entry in handleViolin:
          if(type(handleViolin[entry]) == type([])):
            for entry2 in handleViolin[entry]:
              if(hasattr(entry2, 'remove')):
                entry2.remove()
          elif(hasattr(handleViolin[entry], 'remove')):
            handleViolin[entry].remove()
        handleViolin = None
          
      if(dataobject.Violinstyle['mode']):
        # get data organized into vectors
        xvector, yvector = dataobject.collateViolinData()
        if(len(xvector) * len(yvector)):
          if(dataobject.Violinstyle['mode'] == 1):
            if(('bodies' in dataobject.Violinstyle) and ('width' in dataobject.Violinstyle['bodies'])):
              useWidth = dataobject.Violinstyle['bodies']['width']
            else:
              useWidth = 0.5
            handleViolin = useAxes.violinplot(yvector, xvector, vert=True, showmeans=True, showextrema=True, showmedians=True, widths=useWidth)
            # enable drawing of half-side violin plot
            if(dataobject.Violinstyle['bodies']['type'] in ['left', 'right']):
              for body in handleViolin['bodies']:
                # get vertices from violin
                vertices = body.get_paths()[0].vertices
                if(len(vertices)):
                  meanx = np.mean(vertices[:, 0])
                  if(dataobject.Violinstyle['bodies']['type'] == 'left'):
                    vertices[:, 0] = [i if (i <= meanx) else meanx for i in vertices[:, 0]]
                  else:
                    vertices[:, 0] = [i if (i >= meanx) else meanx for i in vertices[:, 0]]
          else:
            if(('boxes' in dataobject.Violinstyle) and ('width' in dataobject.Violinstyle['boxes'])):
              useWidth = dataobject.Violinstyle['boxes']['width']
            else:
              useWidth = 0.5
            if(('boxes' in dataobject.Violinstyle) and ('boxFill' in dataobject.Violinstyle['boxes'])):
              useFill = dataobject.Violinstyle['boxes']['boxFill']
            else:
              useFill = False
            # check matplotlib version
            version = matplotlib.__version__.split('.')
            if((len(version) > 1) and ((int(version[0]) > 3) or ((int(version[0]) == 3) and (int(version[1]))))):
              handleViolin = useAxes.boxplot(yvector, positions=xvector, manage_ticks=False, showmeans=True, showfliers=True, sym=None, widths=useWidth, patch_artist=useFill, flierprops={'marker': 'o'})
            else:
              handleViolin = useAxes.boxplot(yvector, positions=xvector, manage_xticks=False, showmeans=True, showfliers=True, sym=None, widths=useWidth, patch_artist=useFill, flierprops={'marker': 'o'})
            # enable drawing of half-side box plot
            if(dataobject.Violinstyle['boxes']['type'] in ['left', 'right']):
              for box in handleViolin['boxes']:
                # get vertices from boxplot
                vertices = box.get_path().vertices
                if(len(vertices)):
                  meanx = np.mean(vertices[1:-1, 0])
                  if(dataobject.Violinstyle['boxes']['type'] == 'left'):
                    vertices[1:, 0] = [i if (i <= meanx) else meanx for i in vertices[1:, 0]]
                  else:
                    vertices[1:, 0] = [i if (i >= meanx) else meanx for i in vertices[1:, 0]]

      # adjust axis limits
      if(rescale):
        if(splitX):
          useModeX, useAxis, useAutoScale = self.modeX_div, 'x2', self.autoScaleX_div
        else:
          useModeX, useAxis, useAutoScale = self.modeX, 'x', self.autoScaleX
        if(useAutoScale):
          if(xerr.size):
            temp_xmin = np.min([i - j for i, j in zip(xval, xerr)])
            temp_xmax = np.max([i + j for i, j in zip(xval, xerr)])
          else:
            temp_xmin = np.min(xval)
            temp_xmax = np.max(xval)
          
          # also consider all visible datasets
          tempx = np.array([])
          if(True):
            for dataset in self.parent.data:
              if((dataset != dataobject) and (dataset.visibility) and (dataset.onBothAxes or (dataset.onSecondAxes == splitX))):
                values, roles = dataset.getData_n_Fit()
                if('x' in roles):
                  datax = list(values[:, roles.index('x')])
                  if('xerr' in roles):
                    dataxerr = list(values[:, roles.index('xerr')])
                    tempx = np.hstack((tempx, [i - j for i, j in zip(datax, dataxerr)]))
                    tempx = np.hstack((tempx, [i + j for i, j in zip(datax, dataxerr)]))
                  else:
                    tempx = np.hstack((tempx, datax))
            
            tempx = np.array([i for i in tempx if ((not np.isnan(i)) and (not np.isinf(i)))])
            if(len(tempx)):
              temp_xmin = np.min((temp_xmin, np.min(tempx)))
              temp_xmax = np.max((temp_xmax, np.max(tempx)))
            
          # check for split x axis
          if(self.splitShow):
            if(splitX):
              temp_xmin = np.max((temp_xmin, self.maxX, self.minX))
            else:
              temp_xmax = np.min((temp_xmax, self.maxX_div, self.minX_div))

          # ensure minimum limit
          if (temp_xmax - temp_xmin < self.EPSILON):
            temp_xmax += self.EPSILON; temp_xmin -= self.EPSILON
          elif(self.data_spacer > 0):
            if(useModeX == 'linear'):
              difference = temp_xmax - temp_xmin

              if((not self.splitShow) or splitX):
                temp_xmax += difference * self.data_spacer
              if((not self.splitShow) or (not splitX)):
                temp_xmin -= difference * self.data_spacer
            else:
              # log scale -- isolate positive data
              pos_x = np.hstack((np.array(xval), tempx))
              pos_x = pos_x[pos_x > 0]
              # recalc. xmin to address error when restoring state
              if(len(pos_x > 1)):
                temp_xmin, temp_xmax = np.min(pos_x), np.max(pos_x)
                if(self.splitShow and splitX and (np.max((self.minX, self.maxX)) > 0)):
                  posLimits = [i for i  in [self.minX, self.maxX] if (i > 0)]
                  temp_xmin = np.max((temp_xmin, np.max(posLimits)))
                if(self.splitShow and (not splitX) and (np.max((self.minX_div, self.maxX_div)) > 0)):
                  posLimits = [i for i  in [self.minX_div, self.maxX_div] if (i > 0)]
                  temp_xmax = np.min((temp_xmax, np.min(posLimits)))
                difference = np.log(temp_xmax / temp_xmin)
                if((not self.splitShow) or (not splitX)):
                  temp_xmin = np.exp(np.log(temp_xmin) - self.data_spacer * difference)
                if((not self.splitShow) or splitX):
                  temp_xmax = np.exp(np.log(temp_xmax) + self.data_spacer * difference)
                
          self.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis=useAxis, updateLabel=True, redraw=False, updateGrid=True)
          self.updateTickEntryField(axis=useAxis)
        
      # determine axis data is plotted on
      if(dataobject.onSecondAxes):
        axis = 'y2'; useMode = self.modeY2; autoCheck = self.autoScaleY2; temp_y = self.y2
      else:
        axis = 'y'; useMode = self.modeY; autoCheck = self.autoScaleY; temp_y = self.y
      # should we ignore the current scale when autozooming?
      if(autoIgnoreCurrentY or (not self.parent.fit[self.parent.activeFit].visibility)):
        temp_y = []

      # it is sufficient to do this for main axes as split axes will follow suit
      if((not splitX) and (autoCheck) and (rescale)):
        if(yerr.size):
          temp_y = np.hstack((temp_y, [i - j for i, j in zip(yval, yerr)], [i + j for i, j in zip(yval, yerr)]))
        else:
          temp_y = np.hstack((temp_y, yval))
        temp_ymin, temp_ymax = np.min(temp_y), np.max(temp_y)
          
        # also consider all visible datasets
        tempy = np.array([])
        if(True):
          for dataset in self.parent.data:
            if((dataset != dataobject) and (dataset.visibility) and (dataset.onBothAxes or (dataset.onSecondAxes == splitX))):
              values, roles = dataset.getData_n_Fit()
              if('y' in roles):
                datay = list(values[:, roles.index('y')])
                if('yerr' in roles):
                  datayerr = list(values[:, roles.index('yerr')])
                  tempy = np.hstack((tempy, [i - j for i, j in zip(datay, datayerr)]))
                  tempy = np.hstack((tempy, [i + j for i, j in zip(datay, datayerr)]))
                else:
                  tempy = np.hstack((tempy, datay))
          
          tempy = np.array([i for i in tempy if ((not np.isnan(i)) and (not np.isinf(i)))])
          if(len(tempy)):
            temp_ymin = np.min((temp_ymin, np.min(tempy)))
            temp_ymax = np.max((temp_ymax, np.max(tempy)))

        # check for split y axis
        if(self.splitY):
          temp_ymin = np.max((temp_ymin, self.maxY2, self.minY2))
  
        # ensure minimum limit
        if (temp_ymax - temp_ymin < self.EPSILON):
          temp_ymax += self.EPSILON; temp_ymin -= self.EPSILON
        elif(self.data_spacer > 0):
          if(useMode == 'linear'):
            difference = temp_ymax - temp_ymin
            temp_ymax += difference * self.data_spacer
            if(not self.splitY):
              temp_ymin -= difference * self.data_spacer
          else:
            # log scale -- isolate positive data
            pos_y = np.hstack((np.array(yval), tempy))
            pos_y = pos_y[pos_y > 0]
            if(len(pos_y > 1)):
              temp_ymin, temp_ymax = np.min(pos_y), np.max(pos_y)
              if(self.splitY and (np.max((self.minY2, self.maxY2)) > 0)):
                posLimits = [i for i  in [self.minY2, self.maxY2] if (i > 0)]
                temp_ymin = np.max((temp_ymin, np.max(posLimits)))
              difference = np.log(temp_ymax / temp_ymin)
              if(not self.splitY):
                temp_ymin = np.exp(np.log(temp_ymin) - self.data_spacer * difference)
              temp_ymax = np.exp(np.log(temp_ymax) + self.data_spacer * difference)

        self.setAxisLimits(lower=temp_ymin, upper=temp_ymax, axis=axis, updateLabel=True, redraw=False, updateGrid=True)
        self.updateTickEntryField(axis=axis)
      
      # apply styles
      dataobject.styleMe(handleData=handleData, handleErr=handleErr, handleErrShady=handleErrShady, handleBar=handleBar,
                         handleStack=handleStack, handleStackNeg=handleStackNeg, handleText=handleText, handleViolin=handleViolin, handleScatter=handleScatter)

      # deal with split y axis
      if(self.splitY and dataobject.onBothAxes):
        dataobject.duplicateForSplit(xval=xval, yval=yval, xerr=xerr, yerr=yerr)
        
      # draw everything
      if(redraw):
        self.dataplotwidget.myRefresh()

    # do we need this at all?
    if(1):
      # assign handles
      if(splitX):
        self.handleData_div = handleData
        self.handleErr_div = handleErr
        self.handleErrShady_div = handleErrShady
        self.handleBar_div = handleBar
        self.handleStack_div = handleStack
        self.handleStackNeg_div = handleStackNeg
        self.handleText_div = handleText
        self.handleViolin_div = handleViolin
        self.handleScatter_div = handleScatter
      else:
        self.handleData = handleData
        self.handleErr = handleErr
        self.handleErrShady = handleErrShady
        self.handleBar = handleBar
        self.handleStack = handleStack
        self.handleStackNeg = handleStackNeg
        self.handleText = handleText
        self.handleViolin = handleViolin
        self.handleScatter = handleScatter
      
    # return handles to graphics objects
    return handleData, handleErr, handleErrShady, handleBar, handleStack, handleStackNeg, handleText, handleViolin, handleScatter

  def myNanPercentile(self, data, low=2.5, high=97.5):
    # custom percentile function to speed up superslow numpy function
    # credits to:
    # https://krstn.eu/np.nanpercentile()-there-has-to-be-a-faster-way/
    def takeThat(data, indices):
      # helper function to get the right indices
      retv = []
      for number, index in enumerate(indices):
        retv.append(data[index, number])
      return np.array(retv)
      
    # convert to numpy array
    if(type(data) is list):
      data = np.array(data)
    ###data = data.transpose()
    dx, dy = data.shape
    # determine valid observations
    valid = np.sum(np.isfinite(data), axis=0)
    # replace all nan values by max value encountered in array
    max_val = np.nanmax(data)
    data[np.isnan(data)] = max_val
    # sort data array
    data = np.sort(data, axis=0)
    result = []
    # loop over quantiles
    for quantile in [low, high]:
      indices = (valid - 1) * (quantile / 100.)
      floor = np.floor(indices).astype(np.int32)
      ceil = np.ceil(indices).astype(np.int32)
      floor_val = takeThat(data, floor)
      ceil_val = takeThat(data, ceil)
      values = (indices - floor) * floor_val + (ceil - indices) * ceil_val
      values[floor == ceil] = floor_val[floor == ceil]
      result.append(values)

    return result
      
  def plotFunction(self, fitobject=None, x=[], handlePlot=None, handleBoot=None, redraw=True, splitX=False, doAutoZoom=True):
    # was fitobject specified?
    if(fitobject == None):
      fitobject = self.parent.fit[self.parent.activeFit]

    if(splitX):
      # retrieve plot handle
      self.handlePlot_div, self.handleBoot_div = handlePlot, handleBoot
      
      # determine which axes to draw on
      if(fitobject.onSecondAxes):
        useAxes = self.ax2_div
      else:
        useAxes = self.ax_div
    else:
      # retrieve plot handle
      self.handlePlot, self.handleBoot = handlePlot, handleBoot
      
      # determine which axes to draw on
      if(fitobject.onSecondAxes):
        useAxes = self.ax2
      else:
        useAxes = self.ax

    # retrieve restriction information
    restrictMeLow, restrictMeHigh, restrictLow_orig, restrictHigh_orig = fitobject.getRestricted()
    restrictLow, restrictHigh = np.min((restrictLow_orig, restrictHigh_orig)), np.max((restrictLow_orig, restrictHigh_orig))
    
    # was target plot interval specified?
    if(splitX):
      if (len(x)):
        self.x_div = np.array(x)
      else:
        # determine x interval of plot
        xmin, xmax = self.minX_div, self.maxX_div
        if(xmin > xmax):
          restrictMeLow, restrictMeHigh = restrictMeHigh, restrictMeLow
          restrictLow, restrictHigh = restrictHigh, restrictLow
        if(restrictMeLow):
          if(xmin < xmax):
            xmin = np.max((xmin, restrictLow))
          else:
            xmin = np.min((xmin, restrictLow))
        if(restrictMeHigh):
          if(xmin < xmax):
            xmax = np.min((xmax, restrictHigh))
          else:
            xmax = np.max((xmax, restrictHigh))
        if(self.modeX_div == 'linear'):
          self.x_div = np.linspace(xmin, xmax, self.DATAPOINTS_SIMULATION)
        elif(self.modeX_div == 'log'):
          self.x_div = np.linspace(np.log(xmin), np.log(xmax), self.DATAPOINTS_SIMULATION)
          self.x_div = np.exp(self.x_div)
      x = self.x_div

      # calculate function values
      self.x_div, funcY = fitobject.simulateFunc(self.x_div, splitX=True)
      if(fitobject.onSecondAxes):
        self.y_div, self.y2_div = np.array([]), funcY
      else:
        self.y_div, self.y2_div = funcY, np.array([])

      # can do some plotting
      if((self.handlePlot_div != None) and (self.handlePlot_div.axes == useAxes)):
        self.handlePlot_div.set_xdata(self.x_div)
        self.handlePlot_div.set_ydata(funcY)
      else:
        if((self.handlePlot_div != None) and (self.handlePlot_div.axes != useAxes)):
          self.handlePlot_div.remove()
        self.handlePlot_div, = useAxes.plot(self.x_div, funcY, zorder=fitobject.zorder + self.parent.zOffset)

      # do we have a bootstrap interval here
      if(self.handleBoot_div != None):
        self.handleBoot_div.remove()
        self.handleBoot_div = None

      fvlo, fvhi = [], []
      if(fitobject.bootType):
        bootFpar, different = fitobject.getBoot()
        if(len(bootFpar)):
          fvals = []
          for fpar in bootFpar:
            checko = np.any(~np.isfinite(np.array(fpar)))
            if(not checko):
              _, fval = fitobject.evaluateFunc(x=self.x_div, param=fpar)
              fvals.append(fval)
          # ignore nan values when calculating percentiles
          ###fvlo, fvhi = np.nanpercentile(fvals, [2.5, 97.5], axis=0)
          fvlo, fvhi = self.myNanPercentile(fvals, 2.5, 97.5)
      else:
        # retrieve relevant information from fit object
        if(('dof' in fitobject.lastFitResults) and ('stderr' in fitobject.lastFitResults) and ('xdata' in fitobject.lastFitResults)  and (len(fitobject.lastFitResults['xdata']))):
          dof, stderr, xdata = fitobject.lastFitResults['dof'], fitobject.lastFitResults['stderr'], fitobject.lastFitResults['xdata']
          if(dof > 0):
            tvalue = stats.t.ppf(0.975, dof)
            x, fval = fitobject.simulateFunc(x=self.x_div)
            conf = tvalue * stderr * np.sqrt(1 / dof + (x - np.mean(xdata)) ** 2 / np.sum((xdata - np.mean(xdata)) ** 2))
            fvlo, fvhi = fval - conf, fval + conf

      if(len(fvlo)):
        # plot this
        self.handleBoot_div = useAxes.fill_between(self.x_div, fvlo, fvhi, zorder=fitobject.zorder + self.parent.zOffset - .1)
        self.handleBoot_div.set_pickradius(PICK_TOLERANCE)
        #store this for duplicate on split
        fitobject.storeConfidenceBand(self.x_div, fvlo, fvhi, splitX=True)

      handlePlot, handleBoot = self.handlePlot_div, self.handleBoot_div
    else:
      if (len(x)):
        self.x = np.array(x)
      else:
        # determine x interval of plot
        xmin, xmax = self.minX, self.maxX
        if(xmin > xmax):
          restrictMeLow, restrictMeHigh = restrictMeHigh, restrictMeLow
          restrictLow, restrictHigh = restrictHigh, restrictLow
        if(restrictMeLow):
          if(xmin < xmax):
            xmin = np.max((xmin, restrictLow))
          else:
            xmin = np.min((xmin, restrictLow))
        if(restrictMeHigh):
          if(xmin < xmax):
            xmax = np.min((xmax, restrictHigh))
          else:
            xmax = np.max((xmax, restrictHigh))
        if(self.modeX == 'linear'):
          self.x = np.linspace(xmin, xmax, self.DATAPOINTS_SIMULATION)
        elif(self.modeX == 'log'):
          self.x = np.linspace(np.log(xmin), np.log(xmax), self.DATAPOINTS_SIMULATION)
          self.x = np.exp(self.x)
      x = self.x
    
      # calculate function values
      self.x, funcY = fitobject.simulateFunc(self.x)
      if(fitobject.onSecondAxes):
        self.y, self.y2 = np.array([]), funcY
      else:
        self.y, self.y2 = funcY, np.array([])

      # can do some plotting
      if((self.handlePlot != None) and (self.handlePlot.axes == useAxes)):
        self.handlePlot.set_xdata(self.x)
        self.handlePlot.set_ydata(funcY)
      else:
        if((self.handlePlot != None) and (self.handlePlot.axes != useAxes)):
          self.handlePlot.remove()
        self.handlePlot, = useAxes.plot(self.x, funcY, zorder=fitobject.zorder + self.parent.zOffset)
      self.handlePlot.set_pickradius(PICK_TOLERANCE)
  
      # do we have a bootstrap interval here
      if(self.handleBoot != None):
        self.handleBoot.remove()
        self.handleBoot = None

      fvlo, fvhi = [], []
      if(fitobject.bootType):
        bootFpar, different = fitobject.getBoot()
        if(len(bootFpar)):
          fvals = []
          for fpar in bootFpar:
            # check fpar for nan/inf values
            checko = np.any(~np.isfinite(np.array(fpar)))
            if(not checko):
              _, fval = fitobject.evaluateFunc(x=self.x, param=fpar)
              fvals.append(fval)
          # ignore nan values when calculating percentiles
          ###fvlo, fvhi = np.nanpercentile(fvals, [2.5, 97.5], axis=0)
          fvlo, fvhi = self.myNanPercentile(fvals, 2.5, 97.5)
      else:
        # retrieve relevant information from fit object
        if(('dof' in fitobject.lastFitResults) and ('stderr' in fitobject.lastFitResults) and ('xdata' in fitobject.lastFitResults)  and (len(fitobject.lastFitResults['xdata']))):
          dof, stderr, xdata = fitobject.lastFitResults['dof'], fitobject.lastFitResults['stderr'], fitobject.lastFitResults['xdata']
          if(dof > 0):
            tvalue = stats.t.ppf(0.975, dof)
            x, fval = fitobject.simulateFunc(x=self.x)
            conf = tvalue * stderr * np.sqrt(1 / dof + (x - np.mean(xdata)) ** 2 / np.sum((xdata - np.mean(xdata)) ** 2))
            fvlo, fvhi = fval - conf, fval + conf

      if(len(fvlo)):
        # plot this
        self.handleBoot = useAxes.fill_between(self.x, fvlo, fvhi, zorder=fitobject.zorder + self.parent.zOffset - .1)
        self.handleBoot.set_pickradius(PICK_TOLERANCE)
        # store this for duplicate on split
        fitobject.storeConfidenceBand(self.x, fvlo, fvhi, splitX=False)

      handlePlot, handleBoot = self.handlePlot, self.handleBoot

      # determine axis data is plotted on
      if(fitobject.onSecondAxes):
        axis = 'y2'; useMode = self.modeY2; autoCheck = self.autoScaleY2
      else:
        axis = 'y'; useMode = self.modeY; autoCheck = self.autoScaleY

      # adjust y limits
      if(autoCheck and doAutoZoom):
        # check whether currently plotted curve is visible
        if(fitobject.visibility):
          zoomY = funcY
        else:
          # if invisible, don't consider for autoscaling
          zoomY = np.array([])
        # check if data is loaded
        temp_y = zoomY
        if(True):
          for dataset in self.parent.data:
            if((dataset.visibility) and (dataset.onBothAxes or (dataset.onSecondAxes == fitobject.onSecondAxes))):
              values, roles = dataset.getData_n_Fit()
              if('y' in roles):
                datay = list(values[:, roles.index('y')])
                if('yerr' in roles):
                  datayerr = list(values[:, roles.index('yerr')])
                  temp_y = np.hstack((temp_y, [i - j for i, j in zip(datay, datayerr)]))
                  temp_y = np.hstack((temp_y, [i + j for i, j in zip(datay, datayerr)]))
                else:
                  temp_y = np.hstack((temp_y, datay))
          
          temp_y = np.array([i for i in temp_y if ((not np.isnan(i)) and (not np.isinf(i)))])
        temp_y = temp_y[np.isfinite(temp_y)]
        
        # check to account for inf/nan only curves
        if(len(temp_y)):
          temp_ymin = np.min(temp_y)
          temp_ymax = np.max(temp_y)
            
          # check for split y axis
          if(self.splitY):
            if(axis == 'y'):
              temp_ymin = np.max((temp_ymin, self.maxY2, self.minY2))
            else:
              temp_ymax = np.min((temp_ymax, self.maxY, self.minY))
  
          # ensure minimum limit
          if(temp_ymax - temp_ymin < self.EPSILON):
            temp_ymax += self.EPSILON; temp_ymin -= self.EPSILON
          elif(self.data_spacer > 0):
            if(useMode == 'linear'):
              difference = temp_ymax - temp_ymin
              if((not self.splitY) or (axis == 'y')):
                temp_ymax += difference * self.data_spacer
              if((not self.splitY) or (axis == 'y2')):
                temp_ymin -= difference * self.data_spacer
            else:
              # log scale -- isolate positive data
              pos_y = np.array(temp_y)
              pos_y = pos_y[pos_y > 0]
              if(len(pos_y > 1)):
                #difference = np.log(pos_y[-1] / pos_y[0])
                temp_ymin, temp_ymax = np.min(pos_y), np.max(pos_y)
                if(self.splitY and (axis == 'y') and (np.max((self.minY2, self.maxY2)) > 0)):
                  posLimits = [i for i  in [self.minY2, self.maxY2] if (i > 0)]
                  temp_ymin = np.max((temp_ymin, np.max(posLimits)))
                if(self.splitY and (axis == 'y2') and (np.max((self.minY, self.maxY)) > 0)):
                  posLimits = [i for i  in [self.minY, self.maxY] if (i > 0)]
                  temp_ymax = np.min((temp_ymax, np.min(posLimits)))
                difference = np.log(temp_ymax / temp_ymin)
                if((not self.splitY) or (axis == 'y2')):
                  temp_ymin = np.exp(np.log(temp_ymin) - self.data_spacer * difference)
                if((not self.splitY) or (axis == 'y')):
                  temp_ymax = np.exp(np.log(temp_ymax) + self.data_spacer * difference)
        else:
          if(fitobject.onSecondAxes):
            temp_ymin, temp_ymax = self.minY2, self.maxY2
          else:
            temp_ymin, temp_ymax = self.minY, self.maxY
  
        self.setAxisLimits(lower=temp_ymin, upper=temp_ymax, axis=axis, updateLabel=True, redraw=False, updateGrid=True)
        self.updateTickEntryField(axis=axis)
    
    # apply styles
    fitobject.styleMe(handlePlot=handlePlot, handleBoot=handleBoot)
      
    # and finally draw everything
    if(redraw):
      self.dataplotwidget.myRefresh()

    # return handles to graphics
    return handlePlot, handleBoot

class BruteWindow(QtWidgets.QMainWindow):
  def __init__(self, parent=None, title=' '):
    super(BruteWindow, self).__init__()
    self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint)
    self.parent = parent
    self.title = title
    self.setWindowTitle(self.title)
    
    self.centralwidget = QWidgetMac(self)
    self.centralwidget.setMinimumSize(QtCore.QSize(int(250 * SCALEFONT), int(100 * SCALEFONT)))
    self.centralwidget.setMaximumSize(QtCore.QSize(int(250 * SCALEFONT), int(100 * SCALEFONT)))
    self.setCentralWidget(self.centralwidget)
    
    self.vLayout = QtWidgets.QVBoxLayout(self.centralwidget)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    
    self.messageLabel = QtWidgets.QLabel()
    self.vLayout.addWidget(self.messageLabel)

    self.messageLabel2 = QtWidgets.QLabel()
    self.vLayout.addWidget(self.messageLabel2)

    self.messageLabel3 = QtWidgets.QLabel()
    self.vLayout.addWidget(self.messageLabel3)

    self.messageLabel4 = QtWidgets.QLabel()
    self.vLayout.addWidget(self.messageLabel4)

    self.buttonContainer = QtWidgets.QWidget()
    self.hLayout = QtWidgets.QHBoxLayout(self.buttonContainer)
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter|QtCore.Qt.AlignmentFlag.AlignTop)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.addWidget(self.buttonContainer)
    
    self.closeButton = QPushButtonMac()
    self.closeButton.setText('Stop')
    self.closeButton.setToolTip('Terminate random parameter search.')
    self.closeButton.setMinimumHeight(int(BASE_SIZE))
    self.closeButton.setMaximumHeight(int(BASE_SIZE))
    self.closeButton.clicked.connect(self.close)
    self.hLayout.addWidget(self.closeButton)
    
    self.resetButton = QPushButtonMac()
    self.resetButton.setText('Restart')
    self.resetButton.setToolTip('Restart this cycle.')
    self.resetButton.setMinimumHeight(int(BASE_SIZE))
    self.resetButton.setMaximumHeight(int(BASE_SIZE))
    self.resetButton.clicked.connect(self.restartCycle)
    self.hLayout.addWidget(self.resetButton)
    
  def closeEvent(self, event):
    # stop fit procedure in parent window
    self.parent.runFlag = False
    
  def restartCycle(self):
    # restarts the current random search cycle
    self.parent.restartFlag = True

class MinWindow(QtWidgets.QMainWindow):
  def __init__(self, parent=None, title=' '):
    super(MinWindow, self).__init__()
    self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint)
    self.parent = parent
    self.title = title
    self.setWindowTitle(self.title)
    
    self.centralwidget = QWidgetMac(self)
    self.centralwidget.setMinimumSize(QtCore.QSize(int(250 * SCALEFONT), int(90 * SCALEFONT)))
    self.centralwidget.setMaximumSize(QtCore.QSize(int(250 * SCALEFONT), int(90 * SCALEFONT)))
    self.setCentralWidget(self.centralwidget)
    
    self.vLayout = QtWidgets.QVBoxLayout(self.centralwidget)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    
    self.messageLabel = QtWidgets.QLabel(self.title)
    self.vLayout.addWidget(self.messageLabel)

    self.iterLabel = QtWidgets.QLabel()
    self.vLayout.addWidget(self.iterLabel)

    self.minLabel = QtWidgets.QLabel()
    self.vLayout.addWidget(self.minLabel)

    self.closeButton = QPushButtonMac()
    self.closeButton.setText('Stop')
    self.closeButton.setToolTip('Terminate numerical minimization.')
    self.closeButton.setMinimumHeight(int(BASE_SIZE))
    self.closeButton.setMaximumHeight(int(BASE_SIZE))
    self.closeButton.clicked.connect(self.close)
    self.vLayout.addWidget(self.closeButton)
    
  def closeEvent(self,event):
    # stop fit procedure in parent window
    self.parent.runFlag = False
    self.parent.daughterWindow = None

class BootWindow(QtWidgets.QMainWindow):
  def __init__(self, parent=None, title=' '):
    super(BootWindow, self).__init__()
    self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint)
    self.parent = parent
    self.title = title
    self.setWindowTitle(self.title)
    
    self.centralwidget = QWidgetMac(self)
    self.centralwidget.setMinimumSize(QtCore.QSize(int(250 * SCALEFONT), int(90 * SCALEFONT)))
    self.centralwidget.setMaximumSize(QtCore.QSize(int(250 * SCALEFONT), int(90 * SCALEFONT)))
    self.setCentralWidget(self.centralwidget)
    
    self.vLayout = QtWidgets.QVBoxLayout(self.centralwidget)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    
    self.messageLabel = QtWidgets.QLabel(self.title)
    self.vLayout.addWidget(self.messageLabel)

    self.iterLabel = QtWidgets.QLabel()
    self.vLayout.addWidget(self.iterLabel)

    self.closeButton = QPushButtonMac()
    self.closeButton.setText('Stop')
    self.closeButton.setToolTip('Terminate bootstrapping.')
    self.closeButton.setMinimumHeight(int(BASE_SIZE))
    self.closeButton.setMaximumHeight(int(BASE_SIZE))
    self.closeButton.clicked.connect(self.close)
    self.vLayout.addWidget(self.closeButton)
    
  def closeEvent(self,event):
    # stop fit procedure in parent window
    self.parent.runFlag = False
    self.parent.daughterWindow = None

# a custom QTextEdit
class myQTextEditBase(QtWidgets.QTextEdit):
  def __init__(self, parent = None):
    super(myQTextEditBase, self).__init__(parent)
    self.parent = parent
    self.fontSize = float(WIDGET_FONTSIZE)
    
    self.updateStyle()
    self.installEventFilter(self)
    
  # had to overwrite the following functions since setting base widget size via stylesheet
  def wheelEvent(self, event):
    # adjusts zoom level when Ctrl is pressed also
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier):
      if(event.angleDelta().y() > 0):
        self.zoomIn()
      else:
        self.zoomOut()
    else:
      # normal event handling
      QtWidgets.QTextEdit.wheelEvent(self, event)

  def updateStyle(self):
    # called when Fit-o-mat style changes
    ###self.styleBase = 'color: rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_ALTERNATE_BASE_STRING + '; '
    self.styleBase = 'color: rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_BASE_STRING + '; '

    # for some reason need to explicitly set QSTYLE again -- could be due to nested widgets?! unclear
    if(QSTYLE != None):
      self.setStyle(QSTYLE)
    # setProperty() and setStyle() patchy, don't update correctly
    # => go via styleSheet
    self.setStyleSheet('myQTextEditBase {' + self.styleBase + 'font-size:' + str(self.fontSize) + 'pt;}')
    
  def zoomIn(self, step=1):
    self.fontSize += step * SCALEFONT
    self.setStyleSheet('myQTextEditBase {' + self.styleBase + 'font-size:' + str(self.fontSize) + 'pt;}')

  def zoomOut(self, step=1):
    self.fontSize -= step * SCALEFONT
    self.fontSize = max(self.fontSize, 1)
    self.setStyleSheet('myQTextEditBase {' + self.styleBase + 'font-size:' + str(self.fontSize) + 'pt;}')

  def eventFilter(self, source, event):
    # filters events to allow dynamic display of tooltips
    if(self.toolTip()):
      # this button has a tooltip alright
      if((event.type() == QtCore.QEvent.Type.ToolTip) and (not myapp.toolTipsShow)):
        # ignore this one
        return True

    return QtWidgets.QWidget.eventFilter(self, source, event)

class myQTextEdit(myQTextEditBase):
  def __init__(self, parent = None):
    super(myQTextEdit, self).__init__(parent)
    self.parent = parent
    self._originalFocusInEvent = QtWidgets.QTextEdit.focusInEvent
    # stuff for updating line numbers
    self.lineNumbersField = None
    self.textChanged.connect(self.textChangedCallback)
    self.currLineCount = 0
    self.verticalScrollBar().valueChanged.connect(self.scrollCallback)
    self.setAcceptRichText(False)

  def focusInEvent(self, event):
    self._originalFocusInEvent(self, event)
    # don't use palettes as these are incompatible with style sheets
    ###self.styleBase = 'color:rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_ALTERNATE_BASE_STRING + '; border-left-style: none; '
    self.styleBase = 'color:rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_BASE_STRING + '; border-left-style: none; '
    self.setStyleSheet('myQTextEditBase {' + self.styleBase + 'font-size:' + str(self.fontSize) + 'pt;}')
    
  def keyPressEvent(self, event):
    if event.matches(QtGui.QKeySequence.StandardKey.Save):
      if(self.parent != None):
        self.parent.saveFit()
    elif event.matches(QtGui.QKeySequence.StandardKey.Open):
      if(self.parent != None):
        self.parent.openFit()
    elif(event.matches(QtGui.QKeySequence.StandardKey.ZoomIn)):
      self.zoomli(1)
      event.accept()
    elif(event.matches(QtGui.QKeySequence.StandardKey.ZoomOut)):
      self.zoomli(-1)
      event.accept()
    elif(event.matches(QtGui.QKeySequence.StandardKey.HelpContents)\
      or event.matches(QtGui.QKeySequence.StandardKey.Print) or event.matches(QtGui.QKeySequence.StandardKey.Quit) or event.matches(QtGui.QKeySequence.StandardKey.Italic)\
      or event.matches(QtGui.QKeySequence.StandardKey.Find)\
      or event.matches(QtGui.QKeySequence.StandardKey.New) or event.matches(QtGui.QKeySequence.StandardKey.Refresh) or event.matches(QtGui.QKeySequence.StandardKey.FindNext)):
      # pass through event
      event.ignore()
    else:
      QtWidgets.QTextEdit.keyPressEvent(self, event)
      # prevent event from triggering main ui as well
      event.accept()
      
  def textChangedCallback(self):
    # check whether this altered the number of lines
    lineCount = self.document().lineCount()
    if(lineCount != self.currLineCount):
      self.currLineCount = lineCount
      if(self.lineNumbersField != None):
        lineText = '\n'.join([str(i + 1) for i in range(lineCount)])
        self.lineNumbersField.setText(lineText)
        useWidth = self.lineNumbersField.fontMetrics().horizontalAdvance(str(lineCount)) + 8
        self.lineNumbersField.setMinimumWidth(useWidth)
        self.lineNumbersField.setMaximumWidth(useWidth)
        # issue scroll update (required to keep track during editing)
        self.scrollCallback()
        
  def scrollCallback(self):
    # check whether text edit has been scrolled
    value = self.verticalScrollBar().value()
    if(self.lineNumbersField != None):
      self.lineNumbersField.verticalScrollBar().setValue(value)

  def wheelEvent(self, event):
    # adjusts zoom level when Ctrl is pressed also
    modifiers = QtWidgets.QApplication.queryKeyboardModifiers()
    if(modifiers & QtCore.Qt.KeyboardModifier.ControlModifier):
      if(event.angleDelta().y() > 0):
        self.zoomli(1)
      else:
        self.zoomli(-1)
    else:
      # normal event handling
      QtWidgets.QTextEdit.wheelEvent(self, event)
    
  def zoomli(self, direction):
    if(direction == 1):
      self.zoomIn()
      if(self.lineNumbersField != None):
        self.lineNumbersField.zoomIn()
    else:
      self.zoomOut()
      if(self.lineNumbersField != None):
        self.lineNumbersField.zoomOut()

    # adjust width of line numbers field
    if(self.lineNumbersField != None):
      useWidth = self.lineNumbersField.fontMetrics().horizontalAdvance(str(self.document().lineCount())) + 8
      self.lineNumbersField.setMinimumWidth(useWidth)
      self.lineNumbersField.setMaximumWidth(useWidth)

# class for display of line numbers
class myQTextEditLineNumbers(myQTextEditBase):
  def __init__(self, parent = None):
    super(myQTextEditLineNumbers, self).__init__(parent)
    self.parent = parent
    # initialize settings
    self.setText('1')
    self.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
    self.setReadOnly(True)
    self.setEnabled(False)
    self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
    self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
    self.styleBase = 'background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 0, stop: 0 #b0aae8, stop: 0.3 #9288e8, stop: 0.8 #9288e8, stop: 1.0 #8077e8); color: white; font-weight: bold; text-align: right; border-right-style: none; '
    self.setStyleSheet('myQTextEditBase {' + self.styleBase + 'font-size:' + str(self.fontSize) + 'pt;}')

class GlobalArea(QWidgetMac):
  def __init__(self, parent=None):
    super(GlobalArea, self).__init__()
    self.parent = parent
    self.validFloat = MyValidFloat()
    self.globalParamsCollate = {}
    self.globalParam_active = []
    self.globalParamNames = []
    self.globalParamValues = []
    self.globalParamFlag = []
    self.globalConfidence = []
    self.globalConfidence_apriori = []
    self.globalParam_lower, self.globalParam_upper =  [], []
    self.chisquare, self.red_chisquare, self.rsquare = 0, 0, 0
    self.storeGlobalParam = []
    self.fitGlobalResultsHeader = '<h3>Latest global fit results</h3><br/>'
    self.globalOutstring = self.fitGlobalResultsHeader
    self.globalCovar, self.globalCovarParameters = [], []
    
    # use epsilon as minimum error value and for calculating derivatives
    self.EPSILON = 1e-9
    # define amplitude threshold for random variation of parameters
    self.MIN_AMPLITUDE = 0.5
    # flag to determine whether errors are used for fitting
    self.globalErrorSwitch, self.globalErrorModel = True, 0
    self.globalErrorConst, self.globalErrorPercent, self.globalErrorMultiply = 1.0, 5.0, 1.0

    # advanced export settings
    self.advancedMinimization = {'algorithm': 'Nelder-Mead', 'xtol': 1e-8, 'maxfev': 1e5}
    self.advancedRandomSearch = {'cycles': 5, 'escalate': 1.5, 'maxfev': 5e3}

    # locally import numpy again
    import numpy as np
    # import common functions from numpy for ease of access (and for consistency with local fitting)
    from numpy import abs, arccos, arcsin, arctan, exp, cos, cosh, log, log2, log10, power, sin, sinh, sqrt, tan, tanh
    self.mySpace = locals()

    # set up GUI
    self.buildRessource()

  def buildRessource(self):
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)

    self.masterwidget = QtWidgets.QSplitter(QtCore.Qt.Orientation.Vertical)
    self.vLayout.addWidget(self.masterwidget)
    self.masterwidget.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
    self.masterwidget.setChildrenCollapsible(False)

    # specify data sets and associated curves
    self.dataSetContainer = QWidgetMac()
    self.masterwidget.addWidget(self.dataSetContainer)
    self.LayoutDataSetContainer = QtWidgets.QVBoxLayout(self.dataSetContainer)
    self.LayoutDataSetContainer.setContentsMargins(0, 5, 0, 0)

    self.dataSetLabel = QtWidgets.QLabel()
    useFont = self.dataSetLabel.font()
    useFont.setBold(True)
    self.dataSetLabel.setFont(useFont)
    self.dataSetLabel.setText('Data sets and curves')
    self.dataSetLabel
    self.LayoutDataSetContainer.addWidget(self.dataSetLabel)

    self.dataSetTable = QtWidgets.QTableWidget()
    self.dataSetTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.dataSetTable.setEnabled(True)
    self.dataSetTable.setColumnCount(4)
    self.dataSetTable.setRowCount(1)
    item = QTableWidgetItemToolTip('Use?')
    item.setToolTip('This column controls which data objects are used during the global fit or minimization.')
    self.dataSetTable.setHorizontalHeaderItem(0, item)
    item = QTableWidgetItemToolTip('Item')
    item.setToolTip('This column shows the names of the data objects available for the global fit or minimization.')
    self.dataSetTable.setHorizontalHeaderItem(1, item)
    item = QTableWidgetItemToolTip('Curve')
    item.setToolTip('This column allows to associate data objects with curve objects for the global fit or minimization.')
    self.dataSetTable.setHorizontalHeaderItem(2, item)
    item = QTableWidgetItemToolTip('Params')
    item.setToolTip('This column shows which parameters the selected curve objects has.')
    self.dataSetTable.setHorizontalHeaderItem(3, item)
    self.LayoutDataSetContainer.addWidget(self.dataSetTable)
    
    # global parameter table
    self.parameterTableContainer = QWidgetMac()
    self.masterwidget.addWidget(self.parameterTableContainer)
    self.LayoutParameterTableContainer = QtWidgets.QVBoxLayout(self.parameterTableContainer)
    self.LayoutParameterTableContainer.setContentsMargins(0, 0, 0, 0)

    self.parameterLabelContainer = QWidgetMac()
    self.LayoutParameterTableContainer.addWidget(self.parameterLabelContainer)
    self.LayoutParameterLabelContainer = QtWidgets.QHBoxLayout(self.parameterLabelContainer)
    self.LayoutParameterLabelContainer.setContentsMargins(0, 0, 0, 0)

    self.parameterLabel = QtWidgets.QLabel()
    useFont = self.parameterLabel.font()
    useFont.setBold(True)
    self.parameterLabel.setFont(useFont)
    self.parameterLabel.setText('Parameters Local /')
    self.LayoutParameterLabelContainer.addWidget(self.parameterLabel)
    self.parameterLabel2 = QtWidgets.QLabel()
    useFont = self.parameterLabel2.font()
    useFont.setBold(True)
    self.parameterLabel2.setFont(useFont)
    self.parameterLabel2.setText('Global')
    self.parameterLabel2.setObjectName('global')
    self.LayoutParameterLabelContainer.addWidget(self.parameterLabel2)
    self.LayoutParameterLabelContainer.addStretch()

    self.globalParamTable = QtWidgets.QTableWidget()
    self.globalParamTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.globalParamTable.setEnabled(True)
    self.globalParamTable.setColumnCount(5)
    self.globalParamTable.setRowCount(1)
    item = QTableWidgetItemToolTip('Fit')
    item.setToolTip('This column controls whether global fit parameters are varied during the fit or minimization.')
    self.globalParamTable.setHorizontalHeaderItem(0, item)
    item = QTableWidgetItemToolTip('Value')
    item.setToolTip('This column holds the value of the global fit parameters.')
    self.globalParamTable.setHorizontalHeaderItem(1, item)
    item = QTableWidgetItemToolTip('Error')
    item.setToolTip('This column holds the confidence interval of the global fit parameters.\n(Calculated based on estimated data errors.)')
    self.globalParamTable.setHorizontalHeaderItem(2, item)
    item3 = QTableWidgetItemToolTip('a priori')
    item3.setToolTip('This column holds the <i>a priori</i> confidence interval of the global fit parameters.\n(Calculated based on supplied data errors.)')
    useFont = item3.font()
    useFont.setItalic(True)
    item3.setFont(useFont)
    self.globalParamTable.setHorizontalHeaderItem(3, item3)
    item = QTableWidgetItemToolTip('Boundaries')
    item.setToolTip('This column allows to configure boundaries for the global fit parameters during the fit or minimization.')
    self.globalParamTable.setHorizontalHeaderItem(4, item)
    self.LayoutParameterTableContainer.addWidget(self.globalParamTable)

    # set fit controls
    self.ButtonContainer = QWidgetMac()
    self.LayoutParameterTableContainer.addWidget(self.ButtonContainer)
    self.LayoutButtonContainer = QtWidgets.QHBoxLayout(self.ButtonContainer)
    self.LayoutButtonContainer.setContentsMargins(0, 0, 0, 0)

    # set up Fit button
    self.doGlobalFitButton = QPushButtonMac()
    self.doGlobalFitButton.setText(' Global Fit')
    self.doGlobalFitButton.setToolTip('Perform global least-squares fit')
    self.doGlobalFitButton.setMinimumHeight(int(BASE_SIZE))
    self.doGlobalFitButton.setMaximumHeight(int(BASE_SIZE))
    self.doGlobalFitButton.clicked.connect(self.doGlobalFit)
    self.doGlobalFitButton.setIcon(FOM_ICON_GEAR)
    self.LayoutButtonContainer.addWidget(self.doGlobalFitButton)

    # set up minimize button
    self.doGlobalMinButton = QPushButtonMac()
    self.doGlobalMinButton.setText(' Glob. ' + self.advancedMinimization['algorithm'])
    self.doGlobalMinButton.setToolTip('Perform global numerical minimization')
    self.doGlobalMinButton.setMinimumHeight(int(BASE_SIZE))
    self.doGlobalMinButton.setMaximumHeight(int(BASE_SIZE))
    self.doGlobalMinButton.clicked.connect(self.doGlobalMin)
    self.doGlobalMinButton.setIcon(FOM_ICON_GEAR)
    self.LayoutButtonContainer.addWidget(self.doGlobalMinButton)

    # set up random fit button
    self.doGlobalBruteButton = QPushButtonMac()
    self.doGlobalBruteButton.setToolTip('Perform global random search of parameters')
    self.doGlobalBruteButton.setText(' Random')
    self.doGlobalBruteButton.setMinimumHeight(int(BASE_SIZE))
    self.doGlobalBruteButton.setMaximumHeight(int(BASE_SIZE))
    self.doGlobalBruteButton.clicked.connect(self.doBruteGlobalFit)
    self.doGlobalBruteButton.setIcon(FOM_ICON_DICE)
    self.LayoutButtonContainer.addWidget(self.doGlobalBruteButton)

    # set up Reset button
    self.resetGlobalButton = QPushButtonMac()
    self.resetGlobalButton.setText(' Reset')
    self.resetGlobalButton.setToolTip('Reset global function parameters to previous values')
    self.resetGlobalButton.setMinimumHeight(int(BASE_SIZE))
    self.resetGlobalButton.setMaximumHeight(int(BASE_SIZE))
    self.resetGlobalButton.clicked.connect(self.resetGlobalParam)
    self.resetGlobalButton.setIcon(FOM_ICON_RESET)
    self.LayoutButtonContainer.addWidget(self.resetGlobalButton)

    # set fit controls 3
    self.ButtonContainer3 = QWidgetMac()
    self.LayoutParameterTableContainer.addWidget(self.ButtonContainer3)
    self.LayoutButtonContainer3 = QtWidgets.QHBoxLayout(self.ButtonContainer3)
    self.LayoutButtonContainer3.setContentsMargins(0, 0, 20, 0)
 
    self.errorLabel = QPushButtonCheckable()
    self.errorLabel.setChecked(self.globalErrorSwitch)
    self.errorLabel.setText('Error weights?')
    self.errorLabel.setToolTip('Use \u0394y errors as weights in global fit?\nUncheck to perform unweighted global fit.')
    self.errorLabel.setMinimumHeight(int(BASE_SIZE))
    self.errorLabel.setMaximumHeight(int(BASE_SIZE))
    self.errorLabel.clicked.connect(self.toggleErrorSwitch)
    self.LayoutButtonContainer3.addWidget(self.errorLabel, stretch=1)

    self.choiceContainer = QWidgetMac()
    self.LayoutButtonContainer3.addWidget(self.choiceContainer, stretch=3)
    self.LayoutChoiceContainer = QtWidgets.QHBoxLayout(self.choiceContainer)
    self.LayoutChoiceContainer.setContentsMargins(0, 0, 0, 0)
    
    self.errorButtons = []
    self.errorButtons.append(QRadioButtonToolTip())
    self.errorButtons[-1].setText('const')
    self.errorButtons[-1].setChecked(1 == self.globalErrorModel)
    self.errorButtons[-1].setToolTip('Use constant \u0394y error')
    self.errorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 1))
    self.LayoutChoiceContainer.addWidget(self.errorButtons[-1])
    
    self.errorConstEntry = QLineEditClick()
    self.errorConstEntry.setText(str(self.globalErrorConst))
    self.errorConstEntry.setToolTip('Use constant \u0394y error')
    self.errorConstEntry.setMinimumHeight(int(BASE_SIZE))
    self.errorConstEntry.setMaximumHeight(int(BASE_SIZE))
    self.errorConstEntry.setMaximumWidth(int(40 * SCALEFONT))
    self.errorConstEntry.setValidator(self.validFloat)
    self.errorConstEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorConstEntry, 'globalErrorConst'))
    self.errorConstEntry.focusOutEvent = partial(self.lostFocus, self.errorConstEntry, 'globalErrorConst', self.errorConstEntry.focusOutEvent)
    self.errorConstEntry.focusInEvent = partial(self.gainFocus, self.errorButtons[-1], self.errorConstEntry.focusInEvent)
    self.LayoutChoiceContainer.addWidget(self.errorConstEntry)

    self.errorButtons.append(QRadioButtonToolTip())
    self.errorButtons[-1].setText('prop')
    self.errorButtons[-1].setChecked(2 == self.globalErrorModel)
    self.errorButtons[-1].setToolTip('Use \u0394y error proportional to y value')
    self.errorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 2))
    self.LayoutChoiceContainer.addWidget(self.errorButtons[-1])
    
    self.errorPercentEntry = QLineEditClick()
    self.errorPercentEntry.setText(str(self.globalErrorPercent))
    self.errorPercentEntry.setToolTip('Use \u0394y error proportional to y value')
    self.errorPercentEntry.setMinimumHeight(int(BASE_SIZE))
    self.errorPercentEntry.setMaximumHeight(int(BASE_SIZE))
    self.errorPercentEntry.setMaximumWidth(int(40 * SCALEFONT))
    self.errorPercentEntry.setValidator(self.validFloat)
    self.errorPercentEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorPercentEntry, 'globalErrorPercent'))
    self.errorPercentEntry.focusOutEvent = partial(self.lostFocus, self.errorPercentEntry, 'globalErrorPercent', self.errorPercentEntry.focusOutEvent)
    self.errorPercentEntry.focusInEvent = partial(self.gainFocus, self.errorButtons[-1], self.errorPercentEntry.focusInEvent)
    self.LayoutChoiceContainer.addWidget(self.errorPercentEntry)

    self.errorPercentLabel = QtWidgets.QLabel()
    self.errorPercentLabel.setText('%')
    self.LayoutChoiceContainer.addWidget(self.errorPercentLabel)

    self.errorButtons.append(QRadioButtonToolTip())
    self.errorButtons[-1].setText('\u0394y \u00D7')
    self.errorButtons[-1].setChecked(0 == self.globalErrorModel)
    self.errorButtons[-1].setToolTip('Use \u0394y error assigned in dataset')
    self.errorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 0))
    self.LayoutChoiceContainer.addWidget(self.errorButtons[-1])

    self.errorDeltaEntry = QLineEditClick()
    self.errorDeltaEntry.setText(str(self.globalErrorMultiply))
    self.errorDeltaEntry.setToolTip('Multiply assigned \u0394y by factor')
    self.errorDeltaEntry.setValidator(self.validFloat)
    self.errorDeltaEntry.setMinimumHeight(int(BASE_SIZE))
    self.errorDeltaEntry.setMaximumHeight(int(BASE_SIZE))
    self.errorDeltaEntry.setMaximumWidth(int(40 * SCALEFONT))
    self.errorDeltaEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorDeltaEntry, 'globalErrorMultiply'))
    self.errorDeltaEntry.focusOutEvent = partial(self.lostFocus, self.errorDeltaEntry, 'globalErrorMultiply', self.errorDeltaEntry.focusOutEvent)
    self.errorDeltaEntry.focusInEvent = partial(self.gainFocus, self.errorButtons[-1], self.errorDeltaEntry.focusInEvent)
    self.LayoutChoiceContainer.addWidget(self.errorDeltaEntry)
    self.LayoutChoiceContainer.addStretch()

    # global fit results
    self.globalFitResultsContainer = QWidgetMac()
    self.masterwidget.addWidget(self.globalFitResultsContainer)
    self.LayoutGlobalFitResultsContainer = QtWidgets.QVBoxLayout(self.globalFitResultsContainer)
    self.LayoutGlobalFitResultsContainer.setContentsMargins(0, 0, 0, 0)

    # on Windows and Linux add markers for splitter
    if(not (platform == 'darwin')):
      self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='horizontal', number=1, repeat=5)
      self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='horizontal', number=2, repeat=5)
    
    # set up text edit field for displaying fit results
    self.globalFitResults = myQTextEditBase()
    self.globalFitResults.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
    self.globalFitResults.setGeometry(QtCore.QRect(0, 0, int(500 * SCALEFONT), 600))
    self.LayoutGlobalFitResultsContainer.addWidget(self.globalFitResults)
    self.globalFitResults.setReadOnly(True)
    self.globalFitResults.setText(self.fitGlobalResultsHeader)
    self.globalFitResults.setToolTip('Results from previous global least-squares fit')
    
    # set up button for display of covariances
    self.globalCovarButton = QPushButtonMac(self.globalFitResultsContainer)
    self.globalCovarButton.setText('Covariances')
    self.globalCovarButton.setToolTip('Display covariances from previous global least-squares fit')
    self.globalCovarButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.globalCovarButton.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.globalCovarButton.clicked.connect(self.displayGlobalCovar)
    self.globalFitResults.resizeEvent = self.customResize
    self.repositionGlobalCovarButton()
    self.globalCovarButton.hide()

    # set up button for confidences based on fit parameters
    self.globalConfidenceButton = QPushButtonMac(self.globalFitResultsContainer)
    self.globalConfidenceButton.setText('Conf. Bands')
    self.globalConfidenceButton.setToolTip('Determine 95% confidence bands of previous global least-squares fit')
    self.globalConfidenceButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.globalConfidenceButton.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.globalConfidenceButton.clicked.connect(self.globalSigmaConfidence)
    self.repositionGlobalConfidenceButton()
    self.globalConfidenceButton.hide()

    # update
    self.updateDataSetTable()

  def gainFocus(self, toggleOption=None, defaultHandler=None, event=None):
    # entry field gained focus
    # select corresponding option
    if(toggleOption != None):
      toggleOption.setChecked(True)

    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)

  def lostFocus(self, entryobject=None, quantity=None, defaultHandler=None, event=None):
    # entry field lost focus, perform sanity check
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        self.__dict__[quantity] = float(entrytext)
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))
    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)
    
  def validateErrorEntry(self, entryobject=None, quantity=None):
    # validates entryfield
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        newnumber = float(entrytext)
        self.__dict__[quantity] = np.abs(newnumber)
        if(newnumber < 0):
          entryobject.setText(str(np.abs(newnumber)))
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))

  def toggleErrorSwitch(self):
    # use errors?
    value = self.errorLabel.isChecked()
    self.globalErrorSwitch = value

  def toggleErrorModel(self, mode=0):
    # change error model
    self.globalErrorModel = mode
    # check switch button
    self.errorLabel.setChecked(True)
    self.globalErrorSwitch = True

  def addSplitterHandleSymbols(self, parentHandle=None, direction='horizontal', number=1, repeat=5):
    # adds spacer symbols to splitter handle
    if(parentHandle != None):
      if(direction in ['horizontal', 'vertical']):
        parentHandle.setHandleWidth(5)
        handli = parentHandle.handle(number)
        if(handli != None):
          if(direction == 'horizontal'):
            layoutli = QtWidgets.QHBoxLayout(handli)
          else:
            layoutli = QtWidgets.QVBoxLayout(handli)
          layoutli.addStretch()
          for repeat in range(repeat):
            bogus = QtWidgets.QWidget()
            bogus.setStyleSheet('QWidget {background-color: #888888;border-radius: 2px;}')
            bogus.setMinimumSize(4, 4)
            bogus.setMaximumSize(4, 4)
            layoutli.addWidget(bogus)
          layoutli.addStretch()
          layoutli.setSpacing(3)
          layoutli.setContentsMargins(1, 0, 0, 0)

  def reportState(self):
    # reports contents for saveState function
    return self.globalFitResults.toHtml() + '\n'

  def restoreState(self, data):
    # restores contents from loadState function
    if(type(data) == type([])):
      self.globalFitResults.setText('\n'.join(data))

  def customResize(self, event):
    # hijack resize event to allow repositioning of covariance button
    myQTextEditBase.resizeEvent(self.globalFitResults, event)
    if(hasattr(self, 'globalCovarButton')):
      self.repositionGlobalCovarButton()
    if(hasattr(self, 'globalConfidenceButton')):
      self.repositionGlobalConfidenceButton()

  def repositionGlobalCovarButton(self):
    # dynamically adjusts position of covariance button
    topRight = self.globalFitResultsContainer.rect().topRight()
    width = self.globalCovarButton.geometry().width()
    self.globalCovarButton.move(QtCore.QPoint(int(topRight.x() - width - 16), int(topRight.y() + 4)))

  def displayGlobalCovar(self):
    # opens new window for display of covariances
    if(len(self.globalCovar)):
      if(not (hasattr(self.parent, 'covarWindow'))):
        self.parent.covarWindow = CovarWindow()
        self.parent.covarWindow.setWindowTitle('Global Covariances')
      self.parent.covarWindow.covarLabel.setText('Normalized Global Covariances')
      self.parent.covarWindow.move(QtGui.QCursor.pos())

      # populate covariance table
      self.parent.covarWindow.populateCovariances(names=self.globalCovarParameters, covar=self.globalCovar)
      
      # apply styles and show
      if(QSTYLE != None):
        self.parent.covarWindow.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.parent.covarWindow.setStyleSheet(QSTYLESHEET)
      self.parent.covarWindow.show()
      self.parent.covarWindow.activateWindow()
      self.parent.covarWindow.focusNextChild()

  def repositionGlobalConfidenceButton(self):
    # dynamically adjusts position of global covariance button
    topRight = self.globalFitResults.rect().topRight()
    width, height = self.globalConfidenceButton.geometry().width(), self.globalConfidenceButton.geometry().height()
    self.globalConfidenceButton.move(QtCore.QPoint(int(topRight.x() - width - 16), int(topRight.y() + height + 4)))

  def globalSigmaConfidence(self):
    # clear bootstrap data (if existing and switch to calculated confidence bands)
    if(len(self.parent.lastFitGlobalCurves)):
      redraw = False
      for curveIndex in self.parent.lastFitGlobalCurves:
        if(('xdata' in self.parent.fit[curveIndex].lastFitResults) and len(self.parent.fit[curveIndex].lastFitResults['xdata'])):
          self.parent.fit[curveIndex].clearBoot()
          self.parent.fit[curveIndex].bootType = False
          self.parent.fit[curveIndex].drawMe(redraw=False)
          redraw = True
        
      if(redraw):
        # issue plot refresh
        self.parent.plotArea.dataplotwidget.myRefresh()
    else:
      self.parent.statusbar.showMessage('Cannot display confidence bands using this option. Perform global data fit first!', self.parent.STATUS_TIME)

  def updateBruteGlobalParam(self, culledDataSets=[], nop=[], updateResid=False):
    # update global fit parameters once found better resid
    counter = 0
    for index, entry in enumerate(self.globalParam_active):
      if (entry == 1):
        self.globalParamValues[index] = self.globalParam_active_list[counter]
        counter += 1

    # update parameter tables
    # updateGlobalParamTable() too brutal at this point, find a more benign way
    ###self.updateGlobalParamTable(fitted=True)
    self.changeGlobalParamTable()
    # update parameter table for current curve
    self.parent.fitarea.changeParamTable()

    # update individual curves
    selectedDataSets = [index for index, entry in enumerate(self.parent.data) if entry.globalFitCheck]
    selectedCurves = [self.parent.data[index].globalFitCurve for index in selectedDataSets]
    selectedCurves = list(set(selectedCurves))
    for index in selectedCurves:
      # clear bootstrap data
      self.parent.fit[index].clearBoot()
      self.parent.fit[index].clearLastFitResults()
      # update params as needed
      paramUpdateNames, paramUpdateValues = [], []
      for entry in self.parent.fit[index].paramNames:
        if(entry in self.globalParamNames):
          paramUpdateNames.append(entry)
          paramUpdateValues.append(self.globalParamValues[self.globalParamNames.index(entry)])
      self.parent.fit[index].updateParamSelect(paramUpdateNames, paramUpdateValues)

      if(index != self.parent.activeFit):
        self.updateNonActiveCurve(index, redefine=False)
      else:
        self.updateActiveCurve()
      # plot function
      self.parent.fit[index].handlePlot, self.parent.fit[index].handleBoot = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index],\
                     handlePlot=self.parent.fit[index].handlePlot, handleBoot=self.parent.fit[index].handleBoot, redraw=False)
      self.parent.fit[index].handlePlot_div, self.parent.fit[index].handleBoot_div = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index],\
                     handlePlot=self.parent.fit[index].handlePlot_div, handleBoot=self.parent.fit[index].handleBoot_div, redraw=False, splitX=True)
      # copy in case split axes are shown
      curve = self.parent.fit[index]
      if(self.parent.plotArea.splitY and curve.onBothAxes):
        curve.duplicateForSplit()

    # update residuals in data sets
    if(updateResid):
      for index, dataSetNumber in enumerate(culledDataSets):
        if(not index):
          self.parent.data[dataSetNumber].setFval(self.globalFval[:nop[index]])
        elif(index < len(culledDataSets) - 1):
          self.parent.data[dataSetNumber].setFval(self.globalFval[sum(nop[:index]):sum(nop[:index + 1])])
        else:
          self.parent.data[dataSetNumber].setFval(self.globalFval[sum(nop[:index]):])
  
        # do the actual plot
        curveIndex = self.parent.data[index].globalFitCurve
        # generate resid style on the fly
        self.parent.data[index].Residstyle.update(self.parent.data[index].style)
        if(self.parent.data[index].Residstyle['linestyle'] == 'None'):
          # copy style definitions for line from curve object used for fitting
          tempDict = {}
          for item in ['linewidth', 'linestyle', 'dash_capstyle', 'color']:
            if(item in self.parent.fit[curveIndex].style):
              tempDict[item] = self.parent.fit[curveIndex].style[item]
          self.parent.data[index].Residstyle.update(tempDict)
        self.parent.data[index].ResidBarstyle.update(self.parent.data[index].Barstyle)
        self.parent.data[index].ResidStackstyle.update(self.parent.data[index].Stackstyle)
        self.parent.data[index].ResidTextstyle.update(self.parent.data[index].Textstyle)
        self.parent.data[index].ResidScatterstyle.update(self.parent.data[index].Scatterstyle)
        # ensure line is visible to connect dots
        if(self.parent.data[index].Residstyle['linestyle'] == 'None'):
          self.parent.data[index].Residstyle['linestyle'] = 'solid'
        # plot residuals
        self.parent.data[index].handleResid, self.parent.plotArea.handleResidZero,\
          self.parent.data[index].handleResidBar, self.parent.data[index].handleResidStack,\
          self.parent.data[index].handleResidStackNeg, self.parent.data[index].handleResidText,\
          self.parent.data[index].handleResidScatter = self.parent.plotArea.plotResid(\
          dataobject = self.parent.data[index], handleResid = self.parent.data[index].handleResid,\
          handleResidZero = self.parent.plotArea.handleResidZero, handleResidBar = self.parent.data[index].handleResidBar,\
          handleResidStack = self.parent.data[index].handleResidStack, handleResidStackNeg = self.parent.data[index].handleResidStackNeg,\
          handleResidText = self.parent.data[index].handleResidText, handleResidScatter=self.parent.data[index].handleResidScatter, redraw=False)
        self.parent.data[index].handleResid_div, self.parent.plotArea.handleResidZero_div,\
          self.parent.data[index].handleResidBar_div, self.parent.data[index].handleResidStack_div,\
          self.parent.data[index].handleResidStackNeg_div, self.parent.data[index].handleResidText_div,\
          self.parent.data[index].handleResidScatter_div = self.parent.plotArea.plotResid(\
          dataobject = self.parent.data[index], handleResid = self.parent.data[index].handleResid_div,\
          handleResidZero = self.parent.plotArea.handleResidZero_div, handleResidBar = self.parent.data[index].handleResidBar_div,\
          handleResidStack = self.parent.data[index].handleResidStack_div, handleResidStackNeg = self.parent.data[index].handleResidStackNeg_div,\
          handleResidText = self.parent.data[index].handleResidText_div, handleResidScatter=self.parent.data[index].handleResidScatter_div, redraw=False, splitX=True)

      # issue plot updates
      self.parent.plotArea.residplotwidget.myRefresh()

    # issue plot updates
    self.parent.plotArea.dataplotwidget.myRefresh()

  def getGlobalRelativeDerivatives(self):
    # determine derivatives of fit parameters
    # define helper function to calc. square deviations
    def XX_monster_minWrapper(globalParam):#, x, globalY, globalYerr):
      self.globalFval = self.XX__monster_Ffunc_(self.globalX, *globalParam)
      globalResid = (self.globalY - self.globalFval) / self.globalYerr
      globalResid = globalResid ** 2
      self.currGlobalResid = np.sum(globalResid)
      return self.currGlobalResid
        
    # probe sensitivity of parameters
    globalParam = [i for i in self.globalParam_active_list]
    self.globalStartVal = self.XX__monster_Ffunc_(self.globalX, *globalParam)
    self.globalStartResid = XX_monster_minWrapper(globalParam)

    # cycle through parameters and calc. derivatives
    for index, entry in enumerate(self.globalParam_active_list):
      globalWorkParam = [i for i in self.globalParam_active_list]
      globalWorkParam[index] *= (1.0 + self.EPSILON)
      globalPerturbResid = XX_monster_minWrapper(globalWorkParam)
      # calc. change in chi square when varying a certain parameter
      self.globalDerivatives[index] = (globalPerturbResid - self.globalStartResid)

    # now also assign amplitudes for random parameter variation
    self.globalRandomAmplitudes = np.array([1.0/i if i != 0 else 0 for i in self.globalDerivatives])
          
    # calc. relative amplitudes
    divisor = np.max(np.abs(self.globalRandomAmplitudes))
    divisor = np.max((divisor, self.EPSILON))
    self.globalRandomAmplitudes = self.globalRandomAmplitudes / divisor / 2.0

  def doBruteGlobalFit(self):
    if(self.doGlobalHelper(modus='random search')):
      # if selected dataset exists, clear it
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.clearMe(redraw=False)

      # and we are ready to gogog!
      self.globalParam_active_list = [i for i, j in zip(self.globalParamValues, self.globalParam_active) if j]
      self.globalParam_lower_list = [self.globalParam_lower[i] for i, j in enumerate(self.globalParam_active) if j]
      self.globalParam_upper_list = [self.globalParam_upper[i] for i, j in enumerate(self.globalParam_active) if j]
      # ensure that we start within bounds
      self.globalParam_active_list = [min(max(init, lower), upper) for init, lower, upper in \
                                zip(self.globalParam_active_list, self.globalParam_lower_list, self.globalParam_upper_list)]
      self.globalOrigParam = [i for i in self.globalParam_active_list]
      self.daughterWindow = None
      self.runFlag = True
      self.restartFlag = False
      try:
        # define helper function to calc. square deviations
        def XX_monster_minWrapper(globalParam):#, x, globalY, globalYerr):
          self.globalFval = self.XX__monster_Ffunc_(self.globalX, *globalParam)
          globalResid = (self.globalY - self.globalFval) / self.globalYerr
          globalResid = globalResid ** 2
          self.currGlobalResid = np.sum(globalResid)
          return self.currGlobalResid
        
        # display busy pointer
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))

        # open an extra window to interact with procedure
        self.daughterWindow = BruteWindow(self, 'Global Random Search')
        # apply styles to popup window
        if(QSTYLE != None):
          self.daughterWindow.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.daughterWindow.setStyleSheet(QSTYLESHEET)
        self.daughterWindow.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
        self.daughterWindow.show()
  
        # now do the minimization
        # cycle until daughter window closed
        movingCursor = '|,/,-,\\'.split(',')
        cursorCount = 0
        repeatCycleLimit, escalate, lastSuccessLimit = self.advancedRandomSearch['cycles'],\
          self.advancedRandomSearch['escalate'], self.advancedRandomSearch['maxfev']
        escalate = [escalate ** i for i in range(repeatCycleLimit)]
        residstr = ''
        repeatCycleCount = 0
        while repeatCycleCount < repeatCycleLimit:
          # update message label
          self.daughterWindow.messageLabel.setText('Repeat cycle ' + str(repeatCycleCount + 1) + '/' + str(repeatCycleLimit))
          
          # parameter derivatives
          self.globalDerivatives = np.zeros(len(self.globalParam_active_list))
          self.getGlobalRelativeDerivatives()
          self.daughterWindow.messageLabel2.setText('resid ' + self.parent.formatNumber(self.globalStartResid) + residstr)
        
          # need to still process events
          QtCore.QCoreApplication.processEvents()
      
          lastSuccessCount = 0
          while ((lastSuccessCount < lastSuccessLimit) and (self.runFlag) and (not self.restartFlag)):
            # randomly vary parameters
            for trial in range(100):
              if(not self.restartFlag):
                lastSuccessCount += 1
                # randomly vary parameters -- add epsilon to get rid of zero parameters
                globalWorkamplitude = [i if (np.abs(i) > self.MIN_AMPLITUDE) else (np.sign(i + 1.1 * self.EPSILON) * self.MIN_AMPLITUDE) for i in self.globalParam_active_list]
                globalWorkamplitude = escalate[repeatCycleCount] * np.array(globalWorkamplitude)
                globalWorkParam = self.globalParam_active_list  + (globalWorkamplitude * (0.33 - np.random.random(len(self.globalParam_active_list))))
                # ensure that we stay within bounds and reflect if not
                for index, bounds in enumerate(zip(self.globalParam_lower_list, self.globalParam_upper_list)):
                  lower, upper = bounds
                  if(lower == upper):
                    globalWorkParam[index] = lower
                  elif(globalWorkParam[index] < lower):
                    if(np.isfinite(upper)):
                      globalWorkParam[index] = lower + ((lower - globalWorkParam[index]) % (upper - lower))
                    else:
                      globalWorkParam[index] = 2 * lower - globalWorkParam[index]
                  elif(globalWorkParam[index] > upper):
                    if(np.isfinite(lower)):
                      globalWorkParam[index] = upper - ((globalWorkParam[index] - upper) % (upper - lower))
                    else:
                      globalWorkParam[index] = 2 * upper - globalWorkParam[index]
                globalPerturbResid = XX_monster_minWrapper(globalWorkParam)
                
                if(globalPerturbResid < self.globalStartResid):
                  # improvement, yeah!
                  # update params
                  self.globalParam_active_list = [i for i in globalWorkParam]
                  self.updateBruteGlobalParam(self.culledDataSets, self.nop, updateResid=False)
                  # prepare new round of random search
                  self.getGlobalRelativeDerivatives()
                  self.daughterWindow.messageLabel2.setText('resid ' + self.parent.formatNumber(self.globalStartResid) + residstr)
                  lastSuccessCount = 0
                  self.daughterWindow.messageLabel3.setText('Func eval: ' + str(int(np.min((lastSuccessCount,\
                    lastSuccessLimit)))) + '/' + str(int(lastSuccessLimit)))
            
            # periodically update label in daughter window
            cursorCount += 1
            if(cursorCount >= len(movingCursor)):
              cursorCount = 0
            self.daughterWindow.messageLabel3.setText('Func eval: ' + str(int(np.min((lastSuccessCount,\
              lastSuccessLimit)))) + '/' + str(int(lastSuccessLimit)))
            self.daughterWindow.messageLabel4.setText('Working ' + movingCursor[cursorCount])
            # need to still process events
            QtCore.QCoreApplication.processEvents()

          if((residstr == '') or (self.globalStartResid < self.globalBestResid)):
            # first cycle or improvement
            self.globalBestParam = [i for i in self.globalParam_active_list]
            self.globalBestVal = 1.0 * self.globalStartVal
            self.globalBestResid = 1.0 * self.globalStartResid
            residstr = '; best ' + self.parent.formatNumber(self.globalBestResid)

          # reset original parameters for next cycle
          self.globalParam_active_list = [i for i in self.globalOrigParam]

          # advance cycle counter
          if(self.restartFlag):
            self.restartFlag = False
          else:
            repeatCycleCount += 1

        # finished
        success = True
      except:
        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()
        self.parent.statusbar.showMessage('Encountered some problem when conducting random search!', self.parent.STATUS_TIME)
        if(self.daughterWindow != None):
          self.daughterWindow.close()
          self.daughterWindow = None
        success = False
        return False

      # restore pointer
      QtWidgets.QApplication.restoreOverrideCursor()

      if(success):
        # finished all repeat cycles
        self.daughterWindow.close()
        self.daughterWindow = None
        
        # restore best results from previous cycle
        if(repeatCycleCount > 0):
          self.globalParam_active_list = [i for i in self.globalBestParam]
        
        # calc. residuals even if no improvement
        globalPerturbResid = XX_monster_minWrapper(self.globalBestParam)
        # after procedure, set new parameters and update curve
        self.updateBruteGlobalParam(self.culledDataSets, self.nop, updateResid=True)
        # success
        return True

  def doGlobalHelper(self, modus=''):
    # combines preparatory calls needed by both global minimization and random search
    # determine number of selected data sets
    selectedDataSets = [index for index, entry in enumerate(self.parent.data) if entry.globalFitCheck]
    if(len(selectedDataSets) < 2):
      self.parent.statusbar.showMessage('Select at least two datasets for global ' + modus + '!', self.parent.STATUS_TIME)
    elif(sum([1 if i else 0 for i in self.globalParam_active]) == 0):
      self.parent.statusbar.showMessage('Select at least one parameter for global ' + modus + '!', self.parent.STATUS_TIME)
    else:
      # check for no. of global parameters and display warning if equals zero
      noGlobalParams = True
      for index, entry in enumerate(self.globalParamNames):
        if(len(self.globalParamsCollate[entry]) > 1):
          noGlobalParams = False
      if(noGlobalParams):
        self.parent.statusbar.showMessage('No global parameters found! Will minimize but there is no gain by global minimization.', self.parent.STATUS_TIME, color='blue')

      # collate and organize data
      self.culledDataSets = []
      checkYerr = False
      self.globalX, self.globalY, self.globalYerr, self.nop = [], [], [], []
      globalYSecond = []
      for index in selectedDataSets:
        # read and organize data
        indivData, indivRoles = self.parent.data[index].getData_n_Fit()
        currDataOnSecond = self.parent.data[index].onSecondAxes
        if(('x' in indivRoles) and ('y' in indivRoles)):
          self.culledDataSets.append(index)
          colX, colY = indivRoles.index('x'), indivRoles.index('y')
          # get y errors
          if('yerr' in indivRoles):
            colYerr = indivRoles.index('yerr')
          else:
            colYerr = -1
            checkYerr = True
          for line in indivData:
            self.globalX.append(line[colX])
            self.globalY.append(line[colY])
            if(colYerr + 1):
              self.globalYerr.append(line[colYerr])
            else:
              self.globalYerr.append(1)
            globalYSecond.append(currDataOnSecond)
          # count data points
          self.nop.append(len(indivData))

      if(checkYerr):
        if(self.globalErrorSwitch and (not self.globalErrorModel)):
          self.parent.statusbar.showMessage('Warning -- y error missing in certain data sets => set to 1', self.parent.STATUS_TIME, color='blue')

      # prepare yerr according to chosen error model
      if(self.globalErrorSwitch):
        if(self.globalErrorModel == 0):
          # use assigned deltaY
          try:
            self.globalYerr = [i * self.globalErrorMultiply for i in self.globalYerr]
          except:
            pass
        elif(self.globalErrorModel == 1):
          # use constant error
          try:
            self.globalYerr = [self.globalErrorConst for i in self.globalX]
          except:
            pass
        elif(self.globalErrorModel == 2):
          # use proportional error
          try:
            self.globalYerr = [i * self.globalErrorPercent for i in self.globalY]
          except:
            pass
      else:
        # remove errors if present
        try:
          self.globalYerr = [1 for i in self.globalX]
        except:
          pass
      
      # replace zero entries by self.EPSILON
      self.globalX, self.globalY, self.globalYerr = np.array(self.globalX), np.array(self.globalY), np.array(self.globalYerr)
      zerosigma = self.globalYerr[self.globalYerr <= 0]
      if(len(zerosigma) > 0):
        self.parent.statusbar.showMessage('Encountered zero/negative sigma values => set to ' + str(self.EPSILON), self.parent.STATUS_TIME, color='blue')
        self.globalYerr = np.array([i if(i>0) else self.EPSILON for i in self.globalYerr])
      
      # determine curves to use
      selectedCurves = [self.parent.data[index].globalFitCurve for index in self.culledDataSets]
      selectedCurves = list(set(selectedCurves))
      
      # cycle over all and define the individual fits in the current scope
      noProblem = self.functionInScope(selectedCurves)
      
      # only proceed if success with setting individual fit functions
      if(noProblem):
        # prepare the monster fit function
        globalFitFunc = 'def XX__monster_Ffunc_(self, x, '
        freeParam = [i for i, j in zip(self.globalParamNames, self.globalParam_active) if j]
        globalFitFunc += ', '.join(freeParam) + '):\n'
        globalFitFunc += '\t# fixed parameters\n'
        for index, entry in enumerate(self.globalParamNames):
          if(not(self.globalParam_active[index])):
            globalFitFunc += '\t' + entry + ' = ' + str(self.globalParamValues[index]) + '\n'
  
        # now call individual fit functions
        globalFitFunc += '\n\t# now call functions for individual curves'
        globalFitFunc += '\n\t__globalFval_ = np.array([])\n'
        for index, dataSetNumber in enumerate(self.culledDataSets):
          # localX
          if(not index):
            globalFitFunc += '\n\t__localX_' + str(dataSetNumber) + ' = x[:' + str(self.nop[index]) + ']'
          elif(index < len(self.culledDataSets) - 1):
            globalFitFunc += '\n\t__localX_' + str(dataSetNumber) + ' = x[' + str(sum(self.nop[:index])) + ':' + str(sum(self.nop[:index + 1])) + ']'
          else:
            globalFitFunc += '\n\t__localX_' + str(dataSetNumber) + ' = x[' + str(sum(self.nop[:index])) + ':]'
          # call function w/ localX
          useCurve = self.parent.data[dataSetNumber].globalFitCurve
          globalFitFunc += '\n\t__localFval_' + str(dataSetNumber) + ' = self.__globFfunc_' + str(useCurve) + '(__localX_' + str(dataSetNumber) + ', '
          globalFitFunc += ', '.join(self.parent.fit[useCurve].paramNames) + ')'
          # and np-stack results
          globalFitFunc += '\n\t__globalFval_ = np.hstack((__globalFval_, __localFval_' + str(dataSetNumber) + '))\n'
          
        # and wrap up the monster fit function
        globalFitFunc += '\n\treturn __globalFval_\n'
  
        # now test the monster fit function
        try:
          # generate ffunc in global namespace (this is needed for Python3 vs. Python2, bummer)
          namespace = self.mySpace
          exec(globalFitFunc, namespace)
          # we need to do some initial test to see whether the function can be called
          initpa = [i for i, j in zip(self.globalParamValues, self.globalParam_active) if j]
          retv = namespace['XX__monster_Ffunc_'](self, self.globalX, *initpa)
          # check for dimension mismatch
          if(retv.shape != self.globalX.shape):
            # we found some mismatch b/w x and y -- raise error to prevent program crash
            raise ValueError
        except:
          self.parent.statusbar.showMessage('Error collating global fit -- please check individual fit functions separately!', self.parent.STATUS_TIME)
        else:
          # now define the new function in the object scope
          setattr(GlobalArea, 'XX__monster_Ffunc_', namespace['XX__monster_Ffunc_'])
          return True
        # some kind of error
        return False
          
  def doGlobalMin(self):
    if(self.doGlobalHelper(modus='minimization')):
      # if selected dataset exists, clear it
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.clearMe(redraw=False)

      # and we are ready to gogog!
      initpa = [i for i, j in zip(self.globalParamValues, self.globalParam_active) if j]
      self.fitpa = [i for i in initpa]
      xtol, maxfev = self.advancedMinimization['xtol'], self.advancedMinimization['maxfev']
      self.fev, self.currGlobalResid = 0, 0
      self.globalFval = 0 * self.globalX
      success = False
      self.daughterWindow = None
      self.runFlag = True
      try:
        # define callback and wrapper functions
        def minimizeCallback(args=0):
          self.fev += 1
          self.fitpa = args
          if((self.fev > maxfev) or (not self.runFlag)):
            # have to raise a warning to terminate
            raise ValueError('exceeded')
          # process events once in a while
          if(not ((self.fev - 1) % 100)):
            if(self.daughterWindow != None):
              self.daughterWindow.iterLabel.setText('iter ' + str(int(self.fev)) + ' / ' + str(int(maxfev)))
              self.daughterWindow.minLabel.setText('resid ' + self.formatNumber(self.currGlobalResid))
            QtCore.QCoreApplication.processEvents()
      
        # generate wrapper function to calculate square deviation
        def XX_monster_minWrapper(globalParam):#, x, globalY, globalYerr):
          self.globalFval = self.XX__monster_Ffunc_(self.globalX, *globalParam)
          globalResid = (self.globalY - self.globalFval) / self.globalYerr
          globalResid = globalResid ** 2
          self.currGlobalResid = np.sum(globalResid)
          return self.currGlobalResid

        # deal w/ boundaries
        usedAlgorithm = self.advancedMinimization['algorithm']
        boundaryLow = np.array([i for i, j in zip(self.globalParam_lower, self.globalParam_active) if j])
        boundaryHigh = np.array([i for i, j in zip(self.globalParam_upper, self.globalParam_active) if j])
        boundaryLowCheck = len(boundaryLow[boundaryLow != -np.inf])
        boundaryHighCheck = len(boundaryHigh[boundaryHigh != np.inf])
        boundaryCheck = boundaryLowCheck or boundaryHighCheck
        if(boundaryCheck):
          # adjust algorithm to use?
          if(usedAlgorithm in ['Powell', 'BFGS']):
            # algorithms that support bounds
            if(usedAlgorithm == 'BFGS'):
              usedAlgorithm = 'L-BFGS-B'
          else:
            self.parent.statusbar.showMessage('As at least some parameters were bounded, global minimization was done by L-BFGS-B.', self.parent.STATUS_TIME, color='blue')
            usedAlgorithm = 'L-BFGS-B'
      
          # prepare boundaries
          boundsLow = [None if(i == -np.inf) else i for i in boundaryLow]
          boundsHigh = [None if(i == np.inf) else i for i in boundaryHigh]
          bounds = [(i, j) for i, j in zip(boundsLow, boundsHigh)]

        # display busy pointer
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))

        # open an extra window to interact with procedure
        self.daughterWindow = MinWindow(self, 'Global ' + self.advancedMinimization['algorithm'] + ' Minimization')
        # apply styles to popup window
        if(QSTYLE != None):
          self.daughterWindow.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.daughterWindow.setStyleSheet(QSTYLESHEET)
        self.daughterWindow.show()
  
        # now do the minimization
        options = {'disp': False}
        if(usedAlgorithm in ['Nelder-Mead', 'Powell']):
          options.update({'xtol': xtol})
        if(boundaryCheck):
          res = optim.minimize(XX_monster_minWrapper, initpa, method=usedAlgorithm, options=options, callback=minimizeCallback, bounds=bounds)
        else:
          res = optim.minimize(XX_monster_minWrapper, initpa, method=usedAlgorithm, options=options, callback=minimizeCallback)

        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()
        success = True
      except ValueError as err:
        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()
        if((len(err.args)) and (err.args[0] == 'exceeded')):
          success = True
        else:
          if(self.daughterWindow != None):
            self.daughterWindow.close()
            self.daughterWindow = None
          success = False
          return False
      except:
        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()
        self.parent.statusbar.showMessage('Encountered some problem when searching minimum!', self.parent.STATUS_TIME)
        if(self.daughterWindow != None):
          self.daughterWindow.close()
          self.daughterWindow = None
        success = False
        return False
    
      if(success):
        # finish, close window if still visible
        if(self.daughterWindow != None):
          self.daughterWindow.close()
          self.daughterWindow = None
        # now update plot and parameter table
        self.globalParam_active_list = [i for i in self.fitpa]
        # after procedure, set new parameters and update curve
        self.updateBruteGlobalParam(self.culledDataSets, self.nop, updateResid=True)
        # success
        return True

  def doGlobalFit(self):
    # determine number of selected data sets
    selectedDataSets = [index for index, entry in enumerate(self.parent.data) if entry.globalFitCheck]
    if(len(selectedDataSets) < 2):
      self.parent.statusbar.showMessage('Select at least two datasets for global fit!', self.parent.STATUS_TIME)
    elif(sum([1 if i else 0 for i in self.globalParam_active]) == 0):
      self.parent.statusbar.showMessage('Select at least one parameter for global fit!', self.parent.STATUS_TIME)
    else:
      # if selected dataset exists, clear it
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.clearMe(redraw=False)

      # check for no. of global parameters and display warning if equals zero
      noGlobalParams = True
      for index, entry in enumerate(self.globalParamNames):
        if(len(self.globalParamsCollate[entry]) > 1):
          noGlobalParams = False
      if(noGlobalParams):
        self.parent.statusbar.showMessage('No global parameters found! Will fit but there is no gain by global fitting.', self.parent.STATUS_TIME, color='blue')
      
      # collate and organize data
      culledDataSets = []
      checkXerr, checkYerr = False, False
      flagXerr, flagYerr = False, False
      globalX, globalY, globalYerr, globalXerr, globalXerrOriginal, nop = [], [], [], [], [], []
      globalYSecond = []
      for index in selectedDataSets:
        # read and organize data
        indivData, indivRoles = self.parent.data[index].getData_n_Fit()
        currDataOnSecond = self.parent.data[index].onSecondAxes
        if(('x' in indivRoles) and ('y' in indivRoles)):
          culledDataSets.append(index)
          colX, colY = indivRoles.index('x'), indivRoles.index('y')
          # get y errors
          if('yerr' in indivRoles):
            colYerr = indivRoles.index('yerr')
            flagYerr = True
          else:
            colYerr = -1
            checkYerr = True
          # get x errors
          if('xerr' in indivRoles):
            colXerr = indivRoles.index('xerr')
            flagXerr = True
          else:
            colXerr = -1
            checkXerr = True
          for line in indivData:
            globalX.append(line[colX])
            globalY.append(line[colY])
            if(colYerr + 1):
              globalYerr.append(line[colYerr])
            else:
              globalYerr.append(1)
            if(colXerr + 1):
              globalXerr.append(line[colXerr])
              globalXerrOriginal.append(line[colXerr])
            else:
              globalXerr.append(1)
              globalXerrOriginal.append(0)
            globalYSecond.append(currDataOnSecond)
          # count data points
          nop.append(len(indivData))
      
      if(checkYerr and flagYerr):
        if(self.globalErrorSwitch and (not self.globalErrorModel)):
          self.parent.statusbar.showMessage('Warning -- y error missing in certain data sets => set to 1', self.parent.STATUS_TIME, color='blue')
      if(checkXerr and flagXerr):
        self.parent.statusbar.showMessage('Warning -- x error missing in certain data sets => set to 1', self.parent.STATUS_TIME, color='blue')

      # prepare yerr according to chosen error model
      if(self.globalErrorSwitch):
        if(self.globalErrorModel == 0):
          # use assigned deltaY
          try:
            globalYerr = [i * self.globalErrorMultiply for i in globalYerr]
            # don't set flag to allow for no deltay included in data sets
            ###flagYerr = True
          except:
            pass
        elif(self.globalErrorModel == 1):
          # use constant error
          try:
            globalYerr = [self.globalErrorConst for i in globalX]
            flagYerr = True
          except:
            pass
        elif(self.globalErrorModel == 2):
          # use proportional error
          try:
            globalYerr = [i * self.globalErrorPercent for i in globalY]
            flagYerr = True
          except:
            pass
      else:
        # remove errors if present
        try:
          globalYerr = [1 for i in globalX]
          flagYerr = False
        except:
          pass

      # replace zero entries by self.EPSILON
      globalX, globalY, globalYerr = np.array(globalX), np.array(globalY), np.array(globalYerr)
      zerosigma = globalYerr[globalYerr <= 0]
      if(len(zerosigma) > 0):
        self.parent.statusbar.showMessage('Encountered zero/negative sigma values => set to ' + str(self.EPSILON), self.parent.STATUS_TIME, color='blue')
        globalYerr = np.array([i if(i>0) else self.EPSILON for i in globalYerr])
      # deal with x errors?
      if(flagXerr):
        globalXerr = np.array(globalXerr)
        globalXerrOriginal = np.array(globalXerrOriginal)
        zerosigmaX = globalXerr[globalXerr <= 0]
        if(len(zerosigmaX) > 0):
          self.parent.statusbar.showMessage('Encountered zero/negative sigmaX values => set to ' + str(self.EPSILON), self.parent.STATUS_TIME, color='blue')
          globalXerr = np.array([i if(i>0) else self.EPSILON for i in globalXerr])
          globalXerrOriginal = np.array([i if(i>0) else 0 for i in globalXerrOriginal])
      
      # determine curves to use
      selectedCurves = [self.parent.data[index].globalFitCurve for index in culledDataSets]
      selectedCurves = list(set(selectedCurves))
      selectedCurvesSecond = [self.parent.fit[index].onSecondAxes for index in selectedCurves]
      
      # cycle over all and define the individual fits in the current scope
      noProblem = self.functionInScope(selectedCurves)
      
      # only proceed if success with setting individual fit functions
      if(noProblem):
        # prepare the monster fit function
        globalFitFunc = 'def XX__monster_Ffunc_(self, x, '
        freeParam = [i for i, j in zip(self.globalParamNames, self.globalParam_active) if j]
        globalFitFunc += ', '.join(freeParam) + '):\n'
        globalFitFunc += '\t# fixed parameters\n'
        for index, entry in enumerate(self.globalParamNames):
          if(not(self.globalParam_active[index])):
            globalFitFunc += '\t' + entry + ' = ' + str(self.globalParamValues[index]) + '\n'
  
        # now call individual fit functions
        globalFitFunc += '\n\t# now call functions for individual curves'
        globalFitFunc += '\n\t__globalFval_ = np.array([])\n'
        for index, dataSetNumber in enumerate(culledDataSets):
          # localX
          if(not index):
            globalFitFunc += '\n\t__localX_' + str(dataSetNumber) + ' = x[:' + str(nop[index]) + ']'
          elif(index < len(culledDataSets) - 1):
            globalFitFunc += '\n\t__localX_' + str(dataSetNumber) + ' = x[' + str(sum(nop[:index])) + ':' + str(sum(nop[:index + 1])) + ']'
          else:
            globalFitFunc += '\n\t__localX_' + str(dataSetNumber) + ' = x[' + str(sum(nop[:index])) + ':]'
          # call function w/ localX
          useCurve = self.parent.data[dataSetNumber].globalFitCurve
          globalFitFunc += '\n\t__localFval_' + str(dataSetNumber) + ' = self.__globFfunc_' + str(useCurve) + '(__localX_' + str(dataSetNumber) + ', '
          globalFitFunc += ', '.join(self.parent.fit[useCurve].paramNames) + ')'
          # and np-stack results
          globalFitFunc += '\n\t__globalFval_ = np.hstack((__globalFval_, __localFval_' + str(dataSetNumber) + '))\n'
          
        # and wrap up the monster fit function
        globalFitFunc += '\n\treturn __globalFval_\n'
  
        # now test the monster fit function
        try:
          # generate ffunc in global namespace (this is needed for Python3 vs. Python2, bummer)
          namespace = self.mySpace
          exec(globalFitFunc, namespace)
          # we need to do some initial test to see whether the function can be called
          initpa = [i for i, j in zip(self.globalParamValues, self.globalParam_active) if j]
          retv = namespace['XX__monster_Ffunc_'](self, globalX, *initpa)
          # check for dimension mismatch
          if(retv.shape != globalX.shape):
            # we found some mismatch b/w x and y -- raise error to prevent program crash
            raise ValueError
        except:
          self.parent.statusbar.showMessage('Error collating global fit -- please check individual fit functions separately!', self.parent.STATUS_TIME)
        else:
          # display busy pointer
          QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))

          # now define the new function in the object scope
          setattr(GlobalArea, 'XX__monster_Ffunc_', namespace['XX__monster_Ffunc_'])
          
          # prepare boundary values and determine algorithm to use
          boundaryLow = np.array([i for i, j in zip(self.globalParam_lower, self.globalParam_active) if j])
          boundaryHigh = np.array([i for i, j in zip(self.globalParam_upper, self.globalParam_active) if j])
          
          # and we are ready to gogog!
          initpa = [i for i, j in zip(self.globalParamValues, self.globalParam_active) if j]
          sigma = globalYerr
          maxfev = 100000
          success = False
          if(flagXerr):
            usedAlgorithm = 'odr'
            try:
              # generate wrapper function to refactor x and param vectors
              def XX_monster_odrWrapper(globalParam, x):
                return self.XX__monster_Ffunc_(x, *globalParam)
              # populate x errors
              sigmaX = globalXerr
              sigmaX = [i if(i > 0) else self.EPSILON for i in sigmaX]
              # call odr
              odrData = odr.RealData(globalX, globalY, sigmaX, sigma)
              odrModel = odr.Model(XX_monster_odrWrapper)
              odrODR = odr.ODR(odrData, odrModel, beta0=initpa)
              odrODR.set_job(fit_type=2)
              odrOut = odrODR.run()
              fitpa, covar = odrOut.beta, odrOut.cov_beta
              success = True
            except:
              # catch all kind of fit problems
              fitpa = initpa
              covar = np.zeros((len(fitpa), len(fitpa)))
          else:
            # determine whether we have boundaries
            usedAlgorithm = 'lm'
            if(len(boundaryLow) or len(boundaryHigh)):
              if(len(boundaryLow[boundaryLow != -np.inf]) or len(boundaryHigh[boundaryHigh != np.inf])):
                # careful -- we need to check whether trf is supported
                try:
                  # fake fit call
                  optim.curve_fit(lambda y, p: y + 3 + p, [1, 2, 3], [0, -1, 2], [2], method='trf', bounds=(-10, 10))
                  usedAlgorithm = 'trf'
                except:
                  self.parent.statusbar.showMessage('Trf fitting not supported on your platform, will switch to lm!', self.parent.STATUS_TIME)

            # do appropriate fit
            if(usedAlgorithm == 'trf'):
              # we do have boundaries here
              # ensure that we start within bounds
              initpa = [min(max(init, lower), upper) for init, lower, upper in zip(initpa, boundaryLow, boundaryHigh)]
              try:
                fitpa, covar = optim.curve_fit(self.XX__monster_Ffunc_, globalX, globalY, initpa, sigma, maxfev=maxfev,\
                                               method=usedAlgorithm, bounds=(boundaryLow, boundaryHigh))
                success = True
              except:
                # catch all kind of fit problems
                fitpa = initpa
                covar = np.zeros((len(fitpa), len(fitpa)))
            else:
              # no boundaries, use Levenberg Marquardt
              try:
                # careful -- we need to check whether method argument is supported
                try:
                  optim.curve_fit(lambda y, p: y + 3 + p, [1, 2, 3], [0, -1, 2], [2], method='lm')
                  fitpa, covar = optim.curve_fit(self.XX__monster_Ffunc_, globalX, globalY, initpa, sigma, maxfev=maxfev, method=usedAlgorithm)
                  success = True
                except:
                  fitpa, covar = optim.curve_fit(self.XX__monster_Ffunc_, globalX, globalY, initpa, sigma, maxfev=maxfev)
                  success = True
              except:
                # catch all kind of fit problems
                fitpa = initpa
                covar = np.zeros((len(fitpa), len(fitpa)))

          # continue if fit successful
          if(success):
            # fit success! -- this is awesome
            # calculate variances etc.
            # evaluate fitted function at x values and store in data object
            globalFval = self.XX__monster_Ffunc_(globalX, *fitpa)

            # calculate confidences
            normcovar = deepcopy(covar)
            try:
              confidence = np.power(covar.diagonal(), 0.5)
              nanList = confidence[np.isnan(confidence)]
              if(nanList.size):
                normcovar = np.ones((len(confidence), len(confidence)))
              else:
                try:
                  for index, value in enumerate(confidence):
                    if(value != 0):
                      normcovar[:, index] /= value
                      normcovar[index, :] /= value
                except:
                  normcovar = np.ones((len(confidence), len(confidence)))
            except:
              # takes care of NaN and similar errors
              confidence = ['--'] * len(fitpa)
              normcovar = np.ones((len(confidence), len(confidence)))
            
            # check for 'nan' in confidence
            if(type(confidence) == type(np.array([]))):
              nanCheck = np.isnan(confidence)
              nanList = confidence[nanCheck]
              if(nanList.size):
                confidence = ['--'] * len(fitpa)

            # check for 'nan' in covar
            if(type(normcovar) == type(np.array([]))):
              nanCheck = np.isnan(normcovar)
              nanList = normcovar[nanCheck]
              if(nanList.size):
                normcovar = np.ones((len(confidence), len(confidence)))

            # calculate chi_square and reduced chi_square
            resid = globalY - globalFval
            self.chisquare = (globalY - globalFval) ** 2 / globalYerr ** 2
            self.chisquare = np.sum(self.chisquare)
            dof = len(globalX) - sum([1 if i else 0 for i in self.globalParam_active])
            if(dof > 0):
              self.red_chisquare = self.chisquare / dof
            else:
              self.red_chisquare = 'inf'
            # and now calculate a priori errors
            if((self.red_chisquare != 'inf') and flagYerr):
              covar_apriori = covar / self.red_chisquare
              confidence_apriori = np.power(covar_apriori.diagonal(), 0.5)
            else:
              confidence_apriori = ['--'] * len(fitpa)
              
            # provide R-square statistics?
            # => better not, as even for a single non-linear curve, R statistics on shaky ground
  
            # did we use x errors or boundaries?
            if(usedAlgorithm == 'odr'):
              self.parent.statusbar.showMessage('As data contained x errors, global fit was done by orthogonal distance regression (odr).', self.parent.STATUS_TIME, color='blue')
            elif(usedAlgorithm == 'trf'):
              self.parent.statusbar.showMessage('As at least some parameters were bounded, global fit was done by trust region reflective (trf).', self.parent.STATUS_TIME, color='blue')
          
            # update parameters in list and table
            counter = 0
            for index, entry in enumerate(self.globalParam_active):
              if (entry):
                self.globalParamValues[index] = fitpa[counter]
                self.globalConfidence[index] = confidence[counter]
                self.globalConfidence_apriori[index] = confidence_apriori[counter]
                counter += 1
              else:
                self.globalConfidence[index] = '--'
                self.globalConfidence_apriori[index] = '--'
  
            # adjust x-axes limits according to all data sets and curves
            if(self.parent.plotArea.autoScaleX):
              if(len(globalXerrOriginal)):
                temp_xmin = np.min([i - j for i, j in zip(globalX, globalXerrOriginal)])
                temp_xmax = np.max([i + j for i, j in zip(globalX, globalXerrOriginal)])
              else:
                temp_xmin, temp_xmax = np.min(globalX), np.max(globalX)
              # check for split x axis
              if(self.parent.plotArea.splitShow):
                temp_xmax = np.min((temp_xmax, self.parent.plotArea.maxX_div, self.parent.plotArea.minX_div))
              # ensure minimum limit
              if (temp_xmax - temp_xmin < self.EPSILON):
                temp_xmax += self.EPSILON; temp_xmin -= self.EPSILON
              elif(self.parent.plotArea.data_spacer > 0):
                data_spacer = self.parent.plotArea.data_spacer
                if(self.parent.plotArea.modeX == 'linear'):
                  difference = temp_xmax - temp_xmin
                  if(not self.parent.plotArea.splitShow):
                    temp_xmax += difference * data_spacer
                  temp_xmin -= difference * data_spacer
                else:
                  # log scale -- isolate positive data
                  pos_x = np.array(globalX)
                  pos_x = pos_x[pos_x > 0]
                  # recalc. xmin to address error when restoring state
                  if(len(pos_x > 1)):
                    temp_xmin, temp_xmax = np.min(pos_x), np.max(pos_x)
                    if(self.parent.plotArea.splitShow and (np.max((self.parent.plotArea.minX_div, self.parent.plotArea.maxX_div)) > 0)):
                      posLimits = [i for i  in [self.parent.plotArea.minX_div, self.parent.plotArea.maxX_div] if (i > 0)]
                      temp_xmax = np.min((temp_xmax, np.min(posLimits)))
                    difference = np.log(temp_xmax / temp_xmin)
                    if(not self.parent.plotArea.splitShow):
                      temp_xmin = np.exp(np.log(temp_xmin) - data_spacer * difference)
                    temp_xmax = np.exp(np.log(temp_xmax) + data_spacer * difference)

              self.parent.plotArea.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x', updateLabel=True, target='plot', redraw=False, updateGrid=True)
              self.parent.plotArea.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x', updateLabel=True, target='resid', redraw=False, updateGrid=True)
              self.parent.plotArea.updateTickEntryField(axis='x')

            if(self.parent.plotArea.autoScaleX_div):
              if(len(globalXerrOriginal)):
                temp_xmin = np.min([i - j for i, j in zip(globalX, globalXerrOriginal)])
                temp_xmax = np.max([i + j for i, j in zip(globalX, globalXerrOriginal)])
              else:
                temp_xmin, temp_xmax = np.min(globalX), np.max(globalX)
              # check for split x axis
              if(self.parent.plotArea.splitShow):
                temp_xmin = np.max((temp_xmin, self.parent.plotArea.maxX, self.parent.plotArea.minX))
              # ensure minimum limit
              if (temp_xmax - temp_xmin < self.EPSILON):
                temp_xmax += self.EPSILON; temp_xmin -= self.EPSILON
              elif(self.parent.plotArea.data_spacer > 0):
                data_spacer = self.parent.plotArea.data_spacer
                if(self.parent.plotArea.modeX == 'linear'):
                  difference = temp_xmax - temp_xmin
                  temp_xmax += difference * data_spacer
                  if(not self.parent.plotArea.splitShow):
                    temp_xmin -= difference * data_spacer
                else:
                  # log scale -- isolate positive data
                  pos_x = np.array(globalX)
                  pos_x = pos_x[pos_x > 0]
                  # recalc. xmin to address error when restoring state
                  if(len(pos_x > 1)):
                    temp_xmin, temp_xmax = np.min(pos_x), np.max(pos_x)
                    if(self.parent.plotArea.splitShow and (np.max((self.parent.plotArea.minX, self.parent.plotArea.maxX)) > 0)):
                      posLimits = [i for i  in [self.parent.plotArea.minX, self.parent.plotArea.maxX] if (i > 0)]
                      temp_xmin = np.max((temp_xmin, np.max(posLimits)))
                    difference = np.log(temp_xmax / temp_xmin)
                    temp_xmin = np.exp(np.log(temp_xmin) - data_spacer * difference)
                    if(not self.parent.plotArea.splitShow):
                      temp_xmax = np.exp(np.log(temp_xmax) + data_spacer * difference)

              self.parent.plotArea.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x2', updateLabel=True, target='plot', redraw=False, updateGrid=True)
              self.parent.plotArea.setAxisLimits(lower=temp_xmin, upper=temp_xmax, axis='x2', updateLabel=True, target='resid', redraw=False, updateGrid=True)
              self.parent.plotArea.updateTickEntryField(axis='x2')
 
            # cycle through individual curves and plot them
            # determine x interval over which to plot
            for index in selectedCurves:
              # clear bootstrap data
              self.parent.fit[index].clearBoot()
              self.parent.fit[index].clearLastFitResults()
              # update parameters
              self.parent.fit[index].updateParamSelect(names=self.globalParamNames, values=self.globalParamValues)
              # for non-active functions, calculate xy values => do that in this scope as we already defined the functions here
              if(index != self.parent.activeFit):
                self.updateNonActiveCurve(index, redefine=False)
              else:
                self.updateActiveCurve()
              # plot function
              self.parent.fit[index].handlePlot, self.parent.fit[index].handleBoot = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index],\
                             handlePlot=self.parent.fit[index].handlePlot, handleBoot=self.parent.fit[index].handleBoot, redraw=False)
              self.parent.fit[index].handlePlot_div, self.parent.fit[index].handleBoot_div = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index],\
                             handlePlot=self.parent.fit[index].handlePlot_div, handleBoot=self.parent.fit[index].handleBoot_div, redraw=False, splitX=True)
              # copy in case split axes are shown
              curve = self.parent.fit[index]
              if(self.parent.plotArea.splitY and curve.onBothAxes):
                curve.duplicateForSplit()
  
            # update parameter table for current curve
            self.parent.fitarea.changeParamTable()
  
            # in case of 'odr', check whether we had boundaries that would need to be expanded to new value
            if(usedAlgorithm == 'odr'):
              self.changeGlobalParamTableBoundaries()
            
            # update residuals in data sets
            for index, dataSetNumber in enumerate(culledDataSets):
              if(not index):
                self.parent.data[dataSetNumber].setFval(globalFval[:nop[index]])
              elif(index < len(culledDataSets) - 1):
                self.parent.data[dataSetNumber].setFval(globalFval[sum(nop[:index]):sum(nop[:index + 1])])
              else:
                self.parent.data[dataSetNumber].setFval(globalFval[sum(nop[:index]):])
              
            # and we should update the results table
            self.parent.resultsarea.setDataSet(currIndex=self.parent.activeData)
                
            # cycle through all data sets and plot residuals
            for index in culledDataSets:
              curveIndex = self.parent.data[index].globalFitCurve
              # generate resid style on the fly
              self.parent.data[index].Residstyle.update(self.parent.data[index].style)
              if(self.parent.data[index].Residstyle['linestyle'] == 'None'):
                # copy style definitions for line from curve object used for fitting
                tempDict = {}
                for item in ['linewidth', 'linestyle', 'dash_capstyle', 'color']:
                  if(item in self.parent.fit[curveIndex].style):
                    tempDict[item] = self.parent.fit[curveIndex].style[item]
                self.parent.data[index].Residstyle.update(tempDict)
              self.parent.data[index].ResidBarstyle.update(self.parent.data[index].Barstyle)
              self.parent.data[index].ResidStackstyle.update(self.parent.data[index].Stackstyle)
              self.parent.data[index].ResidTextstyle.update(self.parent.data[index].Textstyle)
              self.parent.data[index].ResidScatterstyle.update(self.parent.data[index].Scatterstyle)
              for item in ['linewidth', 'linestyle', 'color']:
                self.parent.data[index].ResidLinestyle[item] = deepcopy(self.parent.fit[curveIndex].style[item])
              # ensure line is visible to connect dots
              if(self.parent.data[index].Residstyle['linestyle'] == 'None'):
                self.parent.data[index].Residstyle['linestyle'] = 'solid'
              # plot residuals
              self.parent.data[index].handleResid, self.parent.plotArea.handleResidZero,\
                self.parent.data[index].handleResidBar, self.parent.data[index].handleResidStack,\
                self.parent.data[index].handleResidStackNeg, self.parent.data[index].handleResidText,\
                self.parent.data[index].handleResidScatter = self.parent.plotArea.plotResid(\
                dataobject = self.parent.data[index], handleResid = self.parent.data[index].handleResid,\
                handleResidZero = self.parent.plotArea.handleResidZero, handleResidBar = self.parent.data[index].handleResidBar,\
                handleResidStack = self.parent.data[index].handleResidStack, handleResidStackNeg = self.parent.data[index].handleResidStackNeg,\
                handleResidText = self.parent.data[index].handleResidText, handleResidScatter=self.parent.data[index].handleResidScatter, redraw=False)
              self.parent.data[index].handleResid_div, self.parent.plotArea.handleResidZero_div,\
                self.parent.data[index].handleResidBar_div, self.parent.data[index].handleResidStack_div,\
                self.parent.data[index].handleResidStackNeg_div, self.parent.data[index].handleResidText_div,\
                self.parent.data[index].handleResidScatter_div = self.parent.plotArea.plotResid(\
                dataobject = self.parent.data[index], handleResid = self.parent.data[index].handleResid_div,\
                handleResidZero = self.parent.plotArea.handleResidZero_div, handleResidBar = self.parent.data[index].handleResidBar_div,\
                handleResidStack = self.parent.data[index].handleResidStack_div, handleResidStackNeg = self.parent.data[index].handleResidStackNeg_div,\
                handleResidText = self.parent.data[index].handleResidText_div, handleResidScatter=self.parent.data[index].handleResidScatter_div, redraw=False, splitX=True)
                
            # adjust y-axes limits according to all data sets and curves
            axes = [i for i, j in zip(['y', 'y2'], [self.parent.plotArea.autoScaleY, self.parent.plotArea.autoScaleY2]) if j]
            condition = {'y': False, 'y2': True}
            mode = {}; mode['y'] = self.parent.plotArea.modeY; mode['y2'] = self.parent.plotArea.modeY2
            for axis in axes:
              # filter for data that is on current y axis
              filter_y = np.array([i for i, j in zip(globalY, globalYSecond) if (j == condition[axis])])
              if(not checkYerr):
                filter_yerr = np.array([i for i, j in zip(globalYerr, globalYSecond) if (j == condition[axis])])
              # prepare temp_y -- data
              if(not checkYerr):
                temp_y = np.array([i - j for i, j in zip(filter_y, filter_yerr)])
                temp_y = np.hstack((temp_y, np.array([i + j for i, j in zip(filter_y, filter_yerr)])))
              else:
                temp_y = filter_y
              # prepare temp_y -- fit
              temp_fval = []
              for index, currFitOnSecond in zip(selectedCurves, selectedCurvesSecond):
                if(currFitOnSecond == condition[axis]):
                  dump_x, curr_y = self.parent.fit[index].simulateFunc()
                  temp_fval = np.hstack((temp_fval, curr_y))
              temp_y = np.hstack((temp_fval, temp_y))
              temp_y = np.array([i for i in temp_y if ((not np.isnan(i)) and (not np.isinf(i)))])
              # if any points available do the auto fit
              if(temp_y.size):
                temp_ymin = np.min(temp_y)
                temp_ymax = np.max(temp_y)
                # check for split y axis
                if(self.parent.plotArea.splitY):
                  if(axis == 'y'):
                    temp_ymin = np.max((temp_ymin, self.parent.plotArea.maxY2, self.parent.plotArea.minY2))
                  else:
                    temp_ymax = np.min((temp_ymax, self.parent.plotArea.maxY, self.parent.plotArea.minY))
                # ensure minimum limit
                if (temp_ymax - temp_ymin < self.EPSILON):
                  temp_ymax += self.EPSILON; temp_ymin -= self.EPSILON
                elif(self.parent.plotArea.data_spacer > 0):
                  data_spacer = self.parent.plotArea.data_spacer
                  if(mode[axis] == 'linear'):
                    difference = temp_ymax - temp_ymin
                    if((not self.parent.plotArea.splitY) or (axis == 'y')):
                      temp_ymax += difference * data_spacer
                    if((not self.parent.plotArea.splitY) or (axis == 'y2')):
                      temp_ymin -= difference * data_spacer
                  else:
                    # log scale -- isolate positive data
                    pos_y = np.array(temp_y)
                    pos_y = pos_y[pos_y > 0]
                    if(len(pos_y > 1)):
                      # recalc. xmin to address error when restoring state
                      temp_ymin, temp_ymax = np.min(pos_y), np.max(pos_y)
                      if(self.parent.plotArea.splitY and (axis == 'y') and (np.max((self.parent.plotArea.minY2, self.parent.plotArea.maxY2)) > 0)):
                        posLimits = [i for i  in [self.parent.plotArea.minY2, self.parent.plotArea.maxY2] if (i > 0)]
                        temp_ymin = np.max((temp_ymin, np.max(posLimits)))
                      if(self.parent.plotArea.splitY and (axis == 'y2') and (np.max((self.parent.plotArea.minY, self.parent.plotArea.maxY)) > 0)):
                        posLimits = [i for i  in [self.parent.plotArea.minY, self.parent.plotArea.maxY] if (i > 0)]
                        temp_ymax = np.min((temp_ymax, np.min(posLimits)))
                      difference = np.log(temp_ymax / temp_ymin)
                      if((not self.parent.plotArea.splitY) or (axis == 'y2')):
                        temp_ymin = np.exp(np.log(temp_ymin) - data_spacer * difference)
                      if((not self.parent.plotArea.splitY) or (axis == 'y')):
                        temp_ymax = np.exp(np.log(temp_ymax) + data_spacer * difference)
                
                self.parent.plotArea.setAxisLimits(lower=temp_ymin, upper=temp_ymax, axis=axis, updateLabel=True, target='plot', redraw=False, updateGrid=True)
                self.parent.plotArea.updateTickEntryField(axis=axis)

            # adjust zoom of residuals plot to encompass all global datasets
            procval = [i for i in resid if ((not np.isnan(i)) and (not np.isinf(i)))]
            if(len(procval)):
              temp_ylimit = np.max([np.abs(i) for i in procval])
              # ensure minimum limit
              if (temp_ylimit == 0):
                maxResidY, minResidY = self.EPSILON, -self.EPSILON
              else:
                maxResidY, minResidY = 1.2 * temp_ylimit, -1.2 * temp_ylimit
              self.parent.plotArea.setAxisLimits(lower = minResidY, upper = maxResidY, axis = 'y', updateLabel = True, target='resid', redraw=False, updateGrid=True)
              self.parent.plotArea.updateTickEntryField(axis='resid')

            # and we should update the fit information
            freeparameters = []; fixedparameters = []
            for index, entry in enumerate(self.globalParam_active):
              if(entry):
                if(self.globalConfidence_apriori[index] != '--'):
                  freeparameters.append(self.globalParamNames[index] + ' = ' + self.parent.formatNumber(self.globalParamValues[index])\
                    + ' &plusmn; ' + self.parent.formatNumber(self.globalConfidence[index]) + ' (' + self.parent.formatNumber(self.globalConfidence_apriori[index]) + ')')
                else:
                  freeparameters.append(self.globalParamNames[index] + ' = ' + self.parent.formatNumber(self.globalParamValues[index]) + ' &plusmn; ' + self.parent.formatNumber(self.globalConfidence[index]))
              else:
                fixedparameters.append(self.globalParamNames[index] + ' = ' + self.parent.formatNumber(self.globalParamValues[index]))
            freestring = '  ' + '\n  '.join(freeparameters)
            fixedstring = '  ' + '\n  '.join(fixedparameters)
            self.globalOutstring = self.fitGlobalResultsHeader
            self.globalOutstring += 'algorithm: ' + usedAlgorithm + '\n'
            self.globalOutstring += 'degrees of freedom: ' + str(dof) + '\n'
            self.globalOutstring += u'<i>\N{GREEK CAPITAL LETTER CHI}</i><sup>2</sup> ' + self.parent.formatNumber(self.chisquare) + '\n'
            self.globalOutstring += u'red. <i>\N{GREEK CAPITAL LETTER CHI}</i><sup>2</sup> ' + self.parent.formatNumber(self.red_chisquare) + '\n\n'

            self.globalOutstring += '<u>used datasets / functions</u>\n<span style="white-space: pre;">'
            for index in culledDataSets:
              curveIndex = self.parent.data[index].globalFitCurve
              self.globalOutstring += '  ' + self.parent.data[index].name + ' / ' + self.parent.fit[curveIndex].name + '\n'
            self.globalOutstring += '\n</span>\n'

            self.globalOutstring += '<u>free parameters</u>\n<span style="white-space: pre;">' + freestring + '\n\n</span>\n\n'

            if(usedAlgorithm == 'trf'):
              self.globalOutstring += '<u>parameter bounds</u>\n<span style="white-space: pre;">'
              for index, entry in enumerate(self.globalParam_active):
                if(entry and ((self.globalParam_lower[index] != -np.inf) or (self.globalParam_upper[index] != np.inf))):
                  self.globalOutstring += '  ' + self.parent.formatNumber(self.globalParam_lower[index]) + ' &le; ' + self.parent.formatNumber(self.globalParamNames[index])
                  self.globalOutstring += ' &le; ' + self.parent.formatNumber(self.globalParam_upper[index]) + '\n'
              self.globalOutstring += '\n</span>\n'

            if(len(fixedparameters)):
              self.globalOutstring += '<u>fixed parameters</u>\n<span style="white-space: pre;">' + fixedstring + '\n</span>\n'
            self.globalOutstring = self.globalOutstring.replace('\n', '<br/>')
            self.globalFitResults.setText(self.globalOutstring)

            # remember that last fit was global
            self.parent.lastFitType = 'global'
            self.parent.lastFitGlobalDataSets = culledDataSets
            self.parent.lastFitGlobalCurves = selectedCurves

            # store information for confidence interval plotting
            # first determine for each curve which datasets feature
            dataByCurve = {}
            for index in culledDataSets:
              curveIndex = self.parent.data[index].globalFitCurve
              if(curveIndex in dataByCurve):
                dataByCurve[curveIndex].append(index)
              else:
                dataByCurve[curveIndex] = [index]
            # then loop over all curves and update information
            for curveIndex in dataByCurve:
              currX, currY, currFval = np.array([]), np.array([]), np.array([])
              for index in dataByCurve[curveIndex]:
                if(not index):
                  currX = np.hstack((currX, globalX[:nop[index]]))
                  currY = np.hstack((currY, globalY[:nop[index]]))
                  currFval = np.hstack((currFval, globalFval[:nop[index]]))
                elif(index < len(culledDataSets) - 1):
                  currX = np.hstack((currX, globalX[sum(nop[:index]):sum(nop[:index + 1])]))
                  currY = np.hstack((currY, globalY[sum(nop[:index]):sum(nop[:index + 1])]))
                  currFval = np.hstack((currFval, globalFval[sum(nop[:index]):sum(nop[:index + 1])]))
                else:
                  currX = np.hstack((currX, globalX[sum(nop[:index]):]))
                  currY = np.hstack((currY, globalY[sum(nop[:index]):]))
                  currFval = np.hstack((currFval, globalFval[sum(nop[:index]):]))
              if(len(currX) and dof):
                stderr = np.sqrt(np.sum((currY - currFval) ** 2) / dof)
                self.parent.fit[curveIndex].lastFitResults = {'dof': dof, 'stderr': stderr, 'xdata': [i for i in currX]}

            # issue plot updates
            self.parent.plotArea.dataplotwidget.myRefresh()
            self.parent.plotArea.residplotwidget.myRefresh()
                
            # update parameter tables
            self.updateGlobalParamTable(fitted=True)

            # remember covariances and parameter names
            self.globalCovar = normcovar
            self.globalCovarParameters = [i for index, i in enumerate(self.globalParamNames) if self.globalParam_active[index]]
            self.globalCovarButton.show()
            self.globalConfidenceButton.show()
          else:
            # some kind of fit problem
            self.parent.statusbar.showMessage('Error doing global fit -- please check individual fit functions separately!', self.parent.STATUS_TIME)

          # restore pointer
          QtWidgets.QApplication.restoreOverrideCursor()

  def functionInScope(self, selectedCurves=[]):
    # defines a list of functions in current object scope
    # => needed for plotting and fitting
    noProblem = True
    for index in selectedCurves:
      indivFitFunc = 'def __globFfunc_' + str(index) + '(self, x, ' + ', '.join(self.parent.fit[index].paramNames) + '):\n\t'
      indivFitFunc += '\n\t'.join(self.parent.fit[index].ffuncstr_base.split('\n'))
      indivFitFunc += '\n\treturn y'
      try:
        namespace = self.mySpace
        # remove previous definition
        if(hasattr(GlobalArea, '__globFfunc_' + str(index))):
          delattr(GlobalArea, '__globFfunc_' + str(index))
        exec(indivFitFunc, namespace)
        setattr(GlobalArea, '__globFfunc_' + str(index), namespace['__globFfunc_' + str(index)])
      except:
        self.parent.statusbar.showMessage('Error setting fit function no. ' + str(index) + '!', self.parent.STATUS_TIME)
        noProblem = False
        
    # successful?
    return noProblem

  def updateActiveCurve(self):
    # here we update the active curve parameters
    # udpate fit function
    fitpa = [i for i, j in zip(self.parent.fit[self.parent.activeFit].paramAll, self.parent.fit[self.parent.activeFit].active) if j]
    self.parent.fit[self.parent.activeFit].updateParam(fitpa)
    self.parent.fitarea.param = [i for i in self.parent.fit[self.parent.activeFit].paramAll]
    self.parent.fitarea.setFfunc(redraw=False, keepBoot=True)

    # update fit area
    self.parent.fitarea.changeParamTable()

  def updateNonActiveCurve(self, index=0, redefine=True):
    # helper function to update curves that are currently not active
    # check whether we should redefine the function (this is to prevent errors when function changed)
    success = True
    if(redefine):
      success = self.functionInScope([index])
    if(success and (hasattr(self, '__globFfunc_' + str(index)))):
      # get plot limiations
      restrictMeLow, restrictMeHigh, restrictLow, restrictHigh = self.parent.fit[index].getRestricted()
      restrictLow, restrictHigh = np.min((restrictLow, restrictHigh)), np.max((restrictLow, restrictHigh))
      # plot over interval
      for splitX in [False, True]:
        if(splitX):
          xmin, xmax, useModeX = self.parent.plotArea.minX_div, self.parent.plotArea.maxX_div, self.parent.plotArea.modeX_div
        else:
          xmin, xmax, useModeX = self.parent.plotArea.minX, self.parent.plotArea.maxX, self.parent.plotArea.modeX
        # heed plot limitations
        if(xmin > xmax):
          restrictMeLow, restrictMeHigh = restrictMeHigh, restrictMeLow
        if(restrictMeLow):
          if(xmin < xmax):
            xmin = np.max((xmin, restrictLow))
          else:
            xmin = np.min((xmin, restrictHigh))
        if(restrictMeHigh):
          if(xmin < xmax):
            xmax = np.min((xmax, restrictHigh))
          else:
            xmax = np.max((xmax, restrictLow))
        # calculate new x vector
        if(useModeX == 'linear'):
          plotX = np.linspace(xmin, xmax, self.parent.plotArea.DATAPOINTS_SIMULATION)
        else:
          plotX = np.linspace(np.log(xmin), np.log(xmax), self.parent.plotArea.DATAPOINTS_SIMULATION)
          plotX = np.exp(plotX)
    
        usepa = []
        for entry in self.parent.fit[index].paramNames:
          index2 = self.globalParamNames.index(entry)
          usepa.append(self.globalParamValues[index2])
        method2call = getattr(self, '__globFfunc_' + str(index))
        indivFval = method2call(plotX, *usepa)
        self.parent.fit[index].updateXY(plotX, indivFval, splitX=splitX)
  
  def reportGlobalParam(self):
    # returns current parameters
    return self.globalParamNames, self.globalParam_active, self.globalParamValues, self.globalConfidence, self.globalConfidence_apriori, self.chisquare, self.red_chisquare, self.globalParamFlag

  def resetGlobalParam(self):
    # restores global params
    flag = False
    for index, entry in enumerate(self.storeGlobalParam):
      if(entry != self.globalParamValues[index]):
        flag = True
        
    if(flag):
      # update global values and table
      self.globalParamValues = [i for i in self.storeGlobalParam]
      self.globalConfidence = ['--'] * len(self.globalParamValues)
      self.globalConfidence_apriori = ['--'] * len(self.globalParamValues)
      # deal w/ parameter boundaries
      self.globalParam_lower = [min(i, j) for i, j in zip(self.globalParam_lower, self.globalParamValues)]
      self.globalParam_upper = [max(i, j) for i, j in zip(self.globalParam_upper, self.globalParamValues)]

      self.updateGlobalParamTable(fitted=True)
      
      # update individual curves
      selectedDataSets = [index for index, entry in enumerate(self.parent.data) if entry.globalFitCheck]
      selectedCurves = [self.parent.data[index].globalFitCurve for index in selectedDataSets]
      selectedCurves = list(set(selectedCurves))
      for index in selectedCurves:
        # clear bootstrap data
        self.parent.fit[index].clearBoot()
        self.parent.fit[index].clearLastFitResults()
        # update params as needed
        paramUpdateNames, paramUpdateValues = [], []
        for entry in self.parent.fit[index].paramNames:
          if(entry in self.globalParamNames):
            paramUpdateNames.append(entry)
            paramUpdateValues.append(self.globalParamValues[self.globalParamNames.index(entry)])
        self.parent.fit[index].updateParamSelect(paramUpdateNames, paramUpdateValues)

        if(index != self.parent.activeFit):
          self.updateNonActiveCurve(index, redefine=False)
        else:
          self.updateActiveCurve()
        # plot function
        self.parent.fit[index].handlePlot, self.parent.fit[index].handleBoot = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index],\
                       handlePlot=self.parent.fit[index].handlePlot, handleBoot=self.parent.fit[index].handleBoot, redraw=False)
        self.parent.fit[index].handlePlot_div, self.parent.fit[index].handleBoot_div = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index],\
                       handlePlot=self.parent.fit[index].handlePlot_div, handleBoot=self.parent.fit[index].handleBoot_div, redraw=False, splitX=True)
        # copy in case split axes are shown
        curve = self.parent.fit[index]
        if(self.parent.plotArea.splitY and curve.onBothAxes):
          curve.duplicateForSplit()
  
      # update parameter table for current curve
      self.parent.fitarea.changeParamTable()

      # issue plot update
      self.parent.plotArea.dataplotwidget.myRefresh()

  def changeGlobalParamTable(self):
    # fills in values into the parameter table
    for index, entry in enumerate(self.globalParamNames):
      self.globalParamTable.cellWidget(index, 1).setText(self.parent.formatNumber(self.globalParamValues[index]))

  def updateGlobalParamTable(self, fitted=False):
    # updates global param table
    if(not fitted):
      # first collate all parameters from all entries
      self.globalParamsCollate = {}
      self.globalParamNames = []
      self.globalParamValues = []
      self.globalParam_active = []
      for index, entry in enumerate(self.parent.data):
        if(self.dataSetTable.cellWidget(index, 0).isChecked()):
          # sanity check for globalFitCurve
          self.parent.data[index].globalFitCurve = min(self.parent.data[index].globalFitCurve, len(self.parent.fit) - 1)
          indexCurve = self.parent.data[index].globalFitCurve
          indivParam, indivParamNames, indivParamActive = self.parent.fit[indexCurve].paramAll, self.parent.fit[indexCurve].paramNames, self.parent.fit[indexCurve].active
          
          for param, paramName, paramActive in zip(indivParam, indivParamNames, indivParamActive):
            if(paramName in self.globalParamsCollate):
              self.globalParamsCollate[paramName].append(param)
            else:
              self.globalParamsCollate[paramName] = [param]
              self.globalParam_active.append(paramActive)
              self.globalParamNames.append(paramName)
              self.globalParamValues.append(param)
      
      # reset confidences and active parameters        
      self.globalConfidence = ['--'] * len(self.globalParamsCollate)
      self.globalConfidence_apriori = ['--'] * len(self.globalParamsCollate)
      
      # store parameters for later resetting
      self.storeGlobalParam = [i for i in self.globalParamValues]
      
      # update global param boundaries
      self.globalParam_lower = [-np.inf] * len(self.globalParamsCollate)
      self.globalParam_upper = [np.inf] * len(self.globalParamsCollate)

    # prepare table
    self.globalParamTable.setRowCount(len(self.globalParamsCollate))
    
    # set row height and fix
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.globalParamTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    hheader = self.globalParamTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    
    # initialize boundary fields
    self.boundaryLowCheck, self.boundaryLowEntry, self.boundaryHighCheck, self.boundaryHighEntry = [], [], [], []

    # set up new param entries
    self.globalParamFlag = []
    for index, entry in enumerate(self.globalParamNames):
      item = QtWidgets.QTableWidgetItem(entry)
      if(len(self.globalParamsCollate[entry]) > 1):
        if(DARK_MODE):
          item.setForeground(QtGui.QColor(180, 180, 255, 255))
        else:
          item.setForeground(QtCore.Qt.GlobalColor.blue)
        useFont = item.font()
        useFont.setBold(True)
        item.setFont(useFont)
        self.globalParamFlag.append(True)
      else:
        self.globalParamFlag.append(False)
      self.globalParamTable.setVerticalHeaderItem(index, item)

      qchkbox_item = QPushButtonCheckable()
      if(self.globalParam_active[index]):
        qchkbox_item.setChecked(True)
      else:
        qchkbox_item.setChecked(False)
      qchkbox_item.setText('')
      qchkbox_item.setPadMe(2)
      qchkbox_item.setToolTip('Vary parameter ' + str(index + 1) + ' during global fit/minimization')
      qchkbox_item.setCheckMe(True)
      qchkbox_item.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      qchkbox_item.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      qchkbox_item.clicked.connect(partial(self.clickGlobalParam, index))
      self.globalParamTable.setCellWidget(index, 0, qchkbox_item)

      qline_item = QLineEditClick(self.parent.formatNumber(self.globalParamValues[index]))
      qline_item.setToolTip('Value of global parameter ' + str(index + 1))
      qline_item.setValidator(self.validFloat)
      qline_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      qline_item.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qline_item.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qline_item.editingFinished.connect(partial(self.editGlobalParam, index, False))
      self.globalParamTable.setCellWidget(index, 1, qline_item)

      qlabel_item = QLabelToolTip(self.parent.formatNumber(self.globalConfidence[index]))
      qlabel_item.setToolTip('A posteriori error of global parameter ' + str(index + 1))
      qlabel_item.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
      self.globalParamTable.setCellWidget(index, 2, qlabel_item)
      
      qlabel_item = QLabelToolTip(self.parent.formatNumber(self.globalConfidence_apriori[index]))
      qlabel_item.setToolTip('A priori error of global parameter ' + str(index + 1))
      qlabel_item.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
      self.globalParamTable.setCellWidget(index, 3, qlabel_item)

      # set up boundary area
      qitem = QtWidgets.QWidget()
      qitem.setMinimumWidth(int(132 * SCALEFONT))
      qitem.setMaximumWidth(int(132 * SCALEFONT))
      self.globalParamTable.setCellWidget(index, 4, qitem)
      qitem_Layout = QtWidgets.QGridLayout(qitem)
      qitem_Layout.setContentsMargins(0, 0, 0, 0)
      qitem_Layout.setSpacing(1)
      
      self.boundaryLowCheck.append(QPushButtonCheckable())
      self.boundaryLowCheck[-1].setText('lower')
      self.boundaryLowCheck[-1].setToolTip('Activate lower fit boundary for global parameter ' + str(index + 1))
      self.boundaryLowCheck[-1].setChecked(bool(self.globalParam_lower[index] != -np.inf))
      self.boundaryLowCheck[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowCheck[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowCheck[-1].clicked.connect(partial(self.clickBoundary, index, True))
      qitem_Layout.addWidget(self.boundaryLowCheck[-1], 0, 0, 1, 1)
      
      self.boundaryLowEntry.append(QLineEditClick())
      self.boundaryLowEntry[-1].setToolTip('Lower fit boundary for global parameter ' + str(index + 1))
      self.boundaryLowEntry[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowEntry[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowEntry[-1].editingFinished.connect(partial(self.editBoundary, index, True))
      self.boundaryLowEntry[-1].setValidator(self.validFloat)
      qitem_Layout.addWidget(self.boundaryLowEntry[-1], 1, 0, 1, 1)
      if(self.boundaryLowCheck[-1].isChecked()):
        self.boundaryLowEntry[-1].setText(self.parent.formatNumber(self.globalParam_lower[index]))
        # explicitly set row height as PyQt layout does not update correctly
        self.globalParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.boundaryLowEntry[-1].hide()
      
      self.boundaryHighCheck.append(QPushButtonCheckable())
      self.boundaryHighCheck[-1].setText('upper')
      self.boundaryHighCheck[-1].setToolTip('Activate upper fit boundary for global parameter ' + str(index + 1))
      self.boundaryHighCheck[-1].setChecked(bool(self.globalParam_upper[index] != np.inf))
      self.boundaryHighCheck[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighCheck[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighCheck[-1].clicked.connect(partial(self.clickBoundary, index, False))
      qitem_Layout.addWidget(self.boundaryHighCheck[-1], 0, 1, 1, 1)
      
      self.boundaryHighEntry.append(QLineEditClick())
      self.boundaryHighEntry[-1].setToolTip('Upper fit boundary for global parameter ' + str(index + 1))
      self.boundaryHighEntry[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighEntry[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighEntry[-1].editingFinished.connect(partial(self.editBoundary, index, False))
      self.boundaryHighEntry[-1].setValidator(self.validFloat)
      qitem_Layout.addWidget(self.boundaryHighEntry[-1], 1, 1, 1, 1)
      if(self.boundaryHighCheck[-1].isChecked()):
        self.boundaryHighEntry[-1].setText(self.parent.formatNumber(self.globalParam_upper[index]))
        # explicitly set row height as PyQt layout does not update correctly
        self.globalParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.boundaryHighEntry[-1].hide()
      
    # set automatic column size
    self.globalParamTable.resizeColumnsToContents()

  def clickBoundary(self, index, lower=True):
    # toggle lower/upper boundary fields
    if(lower):
      value = self.boundaryLowCheck[index].isChecked()
      if(value):
        if(self.globalParam_lower[index] == -np.inf):
          self.globalParam_lower[index] = min(0, 10 * self.globalParamValues[index])
        else:
          self.globalParam_lower[index] = min(self.globalParam_lower[index], 0, 10 * self.globalParamValues[index])
        self.boundaryLowEntry[index].show()
        self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.globalParam_lower[index]))
        self.globalParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.globalParam_lower[index] = -np.inf
        self.boundaryLowEntry[index].setText('')
        self.boundaryLowEntry[index].hide()
        if(self.boundaryHighCheck[index].isChecked()):
          self.globalParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
        else:
          self.globalParamTable.setRowHeight(index, self.rowHeight)
    else:
      value = self.boundaryHighCheck[index].isChecked()
      if(value):
        if(self.globalParam_upper[index] == np.inf):
          self.globalParam_upper[index] = max(0, 10 * self.globalParamValues[index])
        else:
          self.globalParam_upper[index] = max(self.globalParam_upper[index], 0, 10 * self.globalParamValues[index])
        self.boundaryHighEntry[index].show()
        self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.globalParam_upper[index]))
        self.globalParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.globalParam_upper[index] = np.inf
        self.boundaryHighEntry[index].setText('')
        self.boundaryHighEntry[index].hide()
        if(self.boundaryLowCheck[index].isChecked()):
          self.globalParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
        else:
          self.globalParamTable.setRowHeight(index, self.rowHeight)
    
  def editBoundary(self, index, lower=True):
    # edit boundary value
    if(lower):
      try:
        self.globalParam_lower[index] = float(self.boundaryLowEntry[index].text())
      except:
        self.globalParam_lower[index] = 0
    else:
      try:
        self.globalParam_upper[index] = float(self.boundaryHighEntry[index].text())
      except:
        self.globalParam_upper[index] = 0

    # check other boundary and parameter value
    flag = False
    if(self.globalParam_lower[index] > self.globalParam_upper[index]):
      self.globalParam_lower[index], self.globalParam_upper[index] = self.globalParam_upper[index], self.globalParam_lower[index]
      self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.globalParam_lower[index]))
      self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.globalParam_upper[index]))
    if(self.globalParamValues[index] < self.globalParam_lower[index]):
      self.globalParamValues[index] = self.globalParam_lower[index]
      flag = True
    if(self.globalParamValues[index] > self.globalParam_upper[index]):
      self.globalParamValues[index] = self.globalParam_upper[index]
      flag = True
    if(flag):
      # update fit function
      self.editGlobalParam(index, force=True)

  def changeGlobalParamTableBoundaries(self):
    # function called by odr optimization to ensure that afterwards lower and upper limits are adjusted if needed
    for index, entry in enumerate(self.globalParamNames):
      if(self.boundaryLowCheck[index].isChecked()):
        if(self.globalParamValues[index] < self.globalParam_lower[index]):
          self.globalParam_lower[index] = self.globalParamValues[index]
          self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.globalParam_lower[index]))
      if(self.boundaryHighCheck[index].isChecked()):
        if(self.globalParamValues[index] > self.globalParam_upper[index]):
          self.globalParam_upper[index] = self.globalParamValues[index]
          self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.globalParam_upper[index]))

  def updateDataSetTable(self):
    # prepare table
    self.dataSetTable.setRowCount(len(self.parent.data))
    
    # set row height and fix
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.dataSetTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    hheader = self.dataSetTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Stretch)
    # fix first field
    hheader.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeMode.Fixed)
    
    # populate table
    for index, entry in enumerate(self.parent.data):
      self.dataSetTable.setVerticalHeaderItem(index, QtWidgets.QTableWidgetItem(str(index + 1)))

      qchkbox_item = QPushButtonCheckable()
      qchkbox_item.setChecked(entry.globalFitCheck)
      qchkbox_item.setText('')
      qchkbox_item.setPadMe(2)
      qchkbox_item.setToolTip('Include data set ' + str(index + 1) + ' in global fit/minimization')
      qchkbox_item.setCheckMe(True)
      qchkbox_item.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      qchkbox_item.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      qchkbox_item.clicked.connect(partial(self.clickDataSet, index))
      self.dataSetTable.setCellWidget(index, 0, qchkbox_item)

      qlabel_item = QLabelToolTip(entry.name)
      qlabel_item.setToolTip('Name of data set ' + str(index + 1))
      qlabel_item.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      ###qlabel_item.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignVCenter)
      self.dataSetTable.setCellWidget(index, 1, qlabel_item)
      
      qcombobox_item = QComboBoxMac()
      qcombobox_item.setToolTip('Associate data set ' + str(index + 1) + ' with fit function')
      qcombobox_item.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      ###qcombobox_item.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      for index2, entry2 in enumerate(self.parent.fit):
        qcombobox_item.addItem(str(index2 + 1) + ' - ' + entry2.name)
      if(self.parent.data[index].globalFitCurve < len(self.parent.fit)):
        qcombobox_item.setCurrentIndex(self.parent.data[index].globalFitCurve)
      else:
        qcombobox_item.setCurrentIndex(0)
      qcombobox_item.activated.connect(partial(self.selectGlobalFfunc, index))
      self.dataSetTable.setCellWidget(index, 2, qcombobox_item)
      
      qlabel_item = QLineEditClick()
      qlabel_item.setReadOnly(True)
      indexCurve = min(self.parent.data[index].globalFitCurve, (len(self.parent.fit) - 1))
      curr_param = ', '.join(self.parent.fit[indexCurve].paramNames)
      qlabel_item.setText(curr_param)
      qlabel_item.setToolTip('Parameters featuring in selected function')
      qlabel_item.setMinimumSize(QtCore.QSize(int(220 * SCALEFONT), int(BASE_SIZE)))
      ###qlabel_item.setMaximumSize(QtCore.QSize(int(220 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignVCenter)
      self.dataSetTable.setCellWidget(index, 3, qlabel_item)
      
    # set automatic column size
    self.dataSetTable.resizeColumnsToContents()
    
    # trigger update of global param table
    self.updateGlobalParamTable()

  def selectGlobalFfunc(self, index=0):
    # need to update data object
    indexCurve = self.dataSetTable.cellWidget(index, 2).currentIndex()
    self.parent.data[index].globalFitCurve = indexCurve
    
    # update parameters
    curr_param = ', '.join(self.parent.fit[indexCurve].paramNames)
    self.dataSetTable.cellWidget(index, 3).setText(curr_param)

    # trigger update of global param table
    self.updateGlobalParamTable()

  def clickDataSet(self, index=0):
    # processes click on data set
    state = self.dataSetTable.cellWidget(index, 0).isChecked()
    if(index < len(self.parent.data)):
      self.parent.data[index].globalFitCheck = state

    # trigger update of global param table
    self.updateGlobalParamTable()

  def clickGlobalParam(self, index=0):
    # check/uncheck global param
    state = self.globalParamTable.cellWidget(index, 0).isChecked()
    self.globalParam_active[index] = state

  def editGlobalParam(self, index=0, force=False):
    # edits global param
    try:
      value = float(self.globalParamTable.cellWidget(index, 1).text())
    except:
      value = 0
    
    if((self.globalParamValues[index] != value) or force):
      if(force):
        # force equals True means called b/c of boundary change
        self.globalParamTable.cellWidget(index, 1).setText(self.parent.formatNumber(self.globalParamValues[index]))
      else:
        self.globalParamValues[index] = value
      paramName = self.globalParamNames[index]
      # update parameters in curves
      redraw = False
      selectedDataSets = [index2 for index2, entry in enumerate(self.parent.data) if entry.globalFitCheck]
      selectedCurves = [self.parent.data[index2].globalFitCurve for index2 in selectedDataSets]
      selectedCurves = list(set(selectedCurves))
      for index2 in selectedCurves:
        if(paramName in self.parent.fit[index2].paramNames):
          # clear bootstrap data
          self.parent.fit[index2].clearBoot()
          self.parent.fit[index2].clearLastFitResults()
          if(index2 == self.parent.activeFit):
            # update parameter in active curve (go via fitarea to update parameters there as well)
            if(paramName in self.parent.fitarea.paramList):
              index3 = self.parent.fitarea.paramList.index(paramName)
              self.parent.fitarea.param[index3] = value
            fitpa = [i for i, j in zip(self.parent.fit[index2].paramAll, self.parent.fit[index2].active) if j]
            self.parent.fit[index2].updateParam(fitpa)
            self.parent.fitarea.setFfunc(redraw=False, keepBoot=True)
            # update parameter table for current curve
            self.parent.fitarea.changeParamTable()
          else:
            self.updateNonActiveCurve(index2)        
            
          self.parent.fit[index2].updateParamSelect([paramName], [self.globalParamValues[index]])
          # plot function
          self.parent.fit[index2].handlePlot, self.parent.fit[index2].handleBoot = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index2],\
                         handlePlot=self.parent.fit[index2].handlePlot, handleBoot=self.parent.fit[index2].handleBoot, redraw=False)
          self.parent.fit[index2].handlePlot_div, self.parent.fit[index2].handleBoot_div = self.parent.plotArea.plotFunction(fitobject=self.parent.fit[index2],\
                         handlePlot=self.parent.fit[index2].handlePlot_div, handleBoot=self.parent.fit[index2].handleBoot_div, redraw=False, splitX=True)
          # copy in case split axes are shown
          curve = self.parent.fit[index2]
          if(self.parent.plotArea.splitY and curve.onBothAxes):
            curve.duplicateForSplit()
          if(self.parent.fit[index2].visibility):
            redraw = True

      # update parameter boundaries if required
      if(self.globalParamValues[index] < self.globalParam_lower[index]):
        self.globalParam_lower[index] = self.globalParamValues[index]
        self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.globalParam_lower[index]))
      if(self.globalParamValues[index] > self.globalParam_upper[index]):
        self.globalParam_upper[index] = self.globalParamValues[index]
        self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.globalParam_upper[index]))
  
      # issue plot updates
      if(redraw):
        self.parent.plotArea.dataplotwidget.myRefresh()

  def setAdvancedRandomSearch(self, key=None, value=None):
    # updates advanced random search settings
    if(key in self.advancedRandomSearch):
      self.advancedRandomSearch[key] = value

  def setAdvancedMinimization(self, key=None, value=None):
    # updates advanced minmization settings
    if(key in self.advancedMinimization):
      self.advancedMinimization[key] = value
      
    if(key == 'algorithm'):
      self.doGlobalMinButton.setText(' Glob. ' + self.advancedMinimization['algorithm'])

  def formatNumber(self, number):
    # formats number for output
    NUMBER_SWITCH = 1e3
    FORMAT_DECIMAL = '{:.3f}'
    FORMAT_SCIENTIFIC = '{:.3e}'
    # determine return string
    if((self.isNumber(number)) and (np.isfinite(float(number)))):
      if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0 / NUMBER_SWITCH)):
        numberstr = FORMAT_SCIENTIFIC.format(number)
        zerostr = FORMAT_SCIENTIFIC.format(0.0)
      else:
        numberstr = FORMAT_DECIMAL.format(number)
        zerostr = FORMAT_DECIMAL.format(0.0)
      if(numberstr == zerostr):
        numberstr = '0'
    else:
      numberstr = str(number)
    
    return numberstr

  def isNumber(self, s):
    # checks whether string is a number
    try:
      float(s)
      return True
    except ValueError:
      pass
   
    try:
      import unicodedata
      unicodedata.numeric(s)
      return True
    except (TypeError, ValueError):
      pass
    
    return False

class CovarWindow(QtWidgets.QMainWindow):
  def __init__(self, *args, **kwargs):
    super(CovarWindow, self).__init__(*args, **kwargs)
    self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowType.WindowStaysOnTopHint)
    self.covar = []
    self.names = []
    self.setMinimumSize(QtCore.QSize(int(240 * SCALEFONT), int(100 * SCALEFONT)))

    # set up GUI
    self.buildRessource()

  def buildRessource(self):
    self.centralwidget = QWidgetMac(self)
    self.centralwidget.setMinimumSize(QtCore.QSize(int(320 * SCALEFONT), int(240 * SCALEFONT)))
    self.setCentralWidget(self.centralwidget)
    
    self.vLayout = QtWidgets.QVBoxLayout(self.centralwidget)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setSpacing(2)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    
    self.covarLabel = QtWidgets.QLabel()
    useFont = self.covarLabel.font()
    useFont.setBold(True)
    useFont.setPointSize(int(useFont.pointSize() * 1.25))
    self.covarLabel.setFont(useFont)
    self.covarLabel.setText('Normalized Covariances')
    self.vLayout.addWidget(self.covarLabel)

    # initialize data table
    self.tableView = QtWidgets.QTableView()
    self.vLayout.addWidget(self.tableView)
    self.tableView.setSelectionMode(self.tableView.SelectionMode.ContiguousSelection)
    self.tableView.setEditTriggers(self.tableView.EditTrigger.NoEditTriggers)
    # set delegate
    self.tableView.setItemDelegate(CovarDelegate())

    # close button
    self.closeButton = QPushButtonMac()
    self.vLayout.addWidget(self.closeButton)
    self.closeButton.setText('Close')
    self.closeButton.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.closeButton.clicked.connect(self.close)
    self.vLayout.addStretch()
    
    # copy button
    # actually don't put in for now as clipboard copying appears buggy due to Qt bug, see below

  def populateCovariances(self, names=[], covar=[]):
    # populates covariance table
    if(len(covar) and (type(covar) == type(np.array([])))):
      self.names, self.covar = names, covar
      
      # prepare table model
      shape = self.covar.shape
      self.model = QtGui.QStandardItemModel(shape[0], shape[1])
      
      # poulate model
      for row in range(shape[0]):
        for col in range(shape[1]):
          item = QtGui.QStandardItem('%4.3f' % self.covar[row, col])
          item.setTextAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
          self.model.setItem(row, col, item)
      self.model.setHorizontalHeaderLabels(self.names)
      self.model.setVerticalHeaderLabels(self.names)
      self.model.layoutChanged.emit()

      # assign to table
      self.tableView.setModel(self.model)
      [self.tableView.verticalHeader().setSectionResizeMode(vheader, QtWidgets.QHeaderView.ResizeMode.Fixed) for vheader in range(shape[0])]
      [self.tableView.horizontalHeader().setSectionResizeMode(hheader, QtWidgets.QHeaderView.ResizeMode.Fixed) for hheader in range(shape[1])]
      # argh, resizeColumnsToContents fails miserably:(
      ###self.tableView.resizeColumnsToContents()
      # hard-code expected size, thank you Qt
      [self.tableView.horizontalHeader().resizeSection(hheader, int(44 * SCALEFONT)) for hheader in range(shape[1])]

      # now correctly size the QWindow -- seriously effing bugger this
      width, height = self.tableView.viewportSizeHint().width(), self.tableView.viewportSizeHint().height()
      # need to add some extra pixels b/c on repeat calls viewportSizeHint() screws up :( 
      # => what an ugly fudge
      self.tableView.setFixedSize(QtCore.QSize(width + int(4 * SCALEFONT), height + int(4 * SCALEFONT)))
      self.centralwidget.adjustSize()
      self.centralwidget.update()
      # explicitly update windows size but check for viewport size
      requiredSize = self.vLayout.sizeHint()
      screenSize = QtGui.QGuiApplication.primaryScreen().availableGeometry()
      useWidth = min(requiredSize.width(), screenSize.width())
      useWidth = int(max(useWidth, 240))
      useHeight = int(min(requiredSize.height(), screenSize.height()))
      self.setMaximumSize(QtCore.QSize(useWidth, useHeight))
      self.resize(useWidth, useHeight)
    
  def keyPressEvent(self, event):
    # catches escape key
    if(event.key() == QtCore.Qt.Key.Key_Escape):
      self.close()
    elif(event.matches(QtGui.QKeySequence.StandardKey.Copy)):
      # prepare output
      selind = self.tableView.selectionModel().selectedIndexes()
      selrow = list(set([index.row() for index in selind]))
      selcol = list(set([index.column() for index in selind]))
      output = [' \t' + '\t'.join([self.names[col] for col in selcol])]
      for row in selrow:
        output.append(self.names[row] + '\t' + '\t'.join(['%4.3f' % self.covar[row, col] for col in selcol]))
      output = '\n'.join(output)
      # for reasons beyond me, clipboard copying fails -- seems to be a Windows Qt bug
      # neither setText() nor setMimeData() appear to do the job :(
      data = QtCore.QMimeData()
      data.setText(output)
      QtWidgets.QApplication.clipboard().setMimeData(data)
    else:
      # activate normal event handling
      QtWidgets.QMainWindow.keyPressEvent(self, event)

# define item delegate for coloring of specific cells
class CovarDelegate(QtWidgets.QStyledItemDelegate):
  def __init__(self):
    super(CovarDelegate, self).__init__()

  def paint(self, painter, option, index):
    if(index.row() == index.column()):
      # regular draw event
      QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)
      if(DARK_MODE):
        brushColor = QtGui.QColor('#555555')
      else:
        brushColor = QtGui.QColor('#aaaaaa')
      painter.save()
      painter.setBrush(QtGui.QBrush(brushColor))
      painter.setOpacity(0.5)
      painter.setPen(QtCore.Qt.PenStyle.NoPen)
      r = QtCore.QRect(option.rect)
      painter.drawRect(r)
      painter.restore()
    else:
      try:
        # highlight covariances larger than 0.9
        if(abs(float(index.data())) > 0.9):
          if(DARK_MODE):
            brushColor = QtGui.QColor('#dd5566')
          else:
            brushColor = QtGui.QColor('#ffaabb')
          painter.save()
          painter.setBrush(QtGui.QBrush(brushColor))
          painter.setOpacity(1.0)
          painter.setPen(QtCore.Qt.PenStyle.NoPen)
          r = QtCore.QRect(option.rect)
          painter.drawRect(r)
          painter.restore()
      except:
        pass
      # regular draw event
      QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)

# a custom tree view for dynamic resizing
class Baum(QtWidgets.QTreeView):
  def __init__(self, parent=None):
    super(Baum, self).__init__()
    self.parent = parent

    self.setHeaderHidden(True)
    self.setAlternatingRowColors(True)
    self.setUniformRowHeights(True)
    self.setEditTriggers(self.EditTrigger.NoEditTriggers)
    self.setSelectionMode(self.SelectionMode.NoSelection)

    # connect signals such that we can dynamically resize view
    # (I got the distinct feeling this should be easier)
    self.expanded.connect(self.resizeMe)
    self.collapsed.connect(self.resizeMe)

  def resizeMe(self, modelIndex):
    ###QtCore.QCoreApplication.processEvents()
    self.parent.resizeMe()

  def countAllVisible(self, anchor=None, parent=None):
    # recursively count all visible rows, not only on top
    # phew, getting this right has been a nightmare, thanks to PyQt
    # super confusing to switch back and forth between tree and model
    if(anchor == None):
      anchor = self.model()
      
    # collect all indices at this level
    count, checkIndices = 0, []
    if(parent == None):
      for i in range(anchor.rowCount()):
        checkIndices.append(anchor.index(i, 0))
    else:
      for i in range(anchor.rowCount(parent)):
        checkIndices.append(anchor.index(i, 0, parent))
    
    # cycle over all indices
    for index in checkIndices:
      if(anchor.hasChildren(index) and self.isExpanded(index)):
        count += self.countAllVisible(anchor, index)
      count += 1

    return count

# the function menu
class FunctionMenu(KuhMenu):
  def __init__(self, parent=None):
    super(FunctionMenu, self).__init__()
    self.parent = parent
    self.maxHeight, self.useWidth = 600, 300

    # set up GUI
    self.buildRessource()

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # and the nice tree view
    self.tree = Baum(parent=self)
    self.tree.clicked.connect(self.selectorCallback)
    self.vLayout.addWidget(self.tree)
    
    # define tree model
    self.treeModel = QtGui.QStandardItemModel()
    self.tree.setModel(self.treeModel)
    self.rootNode = self.treeModel.invisibleRootItem()
    self.populateSelector()
    
  def selectorCallback(self, item):
    # processes click events in function selector
    data = item.data(QtCore.Qt.ItemDataRole.UserRole)
    if(data == None):
      if(self.tree.isExpanded(item)):
        self.tree.collapse(item)
      else:
        self.tree.expand(item)
    else:
      # here we can select a new function
      self.parent.selectFfunc(data)
      # and we close the QMenu
      self.close()

  def populateSelector(self):
    # initializes tree model
    # glob all available functions
    functionsDir = Path(WORKINGDIR + PATH_SEPARATOR + 'functions' + PATH_SEPARATOR)
    functionsDir = functionsDir.absolute().__str__()
    if(not functionsDir.endswith(PATH_SEPARATOR)):
      functionsDir += PATH_SEPARATOR
    # generate node list -- go via absolute paths to take care of relative path shenanigans under Linux
    self.ffuncList = [path.absolute().__str__() for path in Path(functionsDir).rglob('*.ffunc')]
    self.nodeList = {}
    for entry in self.ffuncList:
      # process ffunctions
      if(functionsDir in entry):
        current = entry.split(functionsDir)[-1]
      else:
        current = entry.split(PATH_SEPARATOR)[-1]
      self.recursiveNodeList(self.nodeList, current, functionsDir)
    
    # now populate selector with these nodes
    self.recursiveNodeMaker(self.rootNode, self.nodeList)

  def recursiveNodeMaker(self, node, item):
    # recursively creates nodes
    if(type(item) == dict):
      # dissect into directories and files
      subdirs = sorted([i for i in item.keys() if type(item[i]) == dict])
      individuals = sorted([i for i in item.keys() if not i in subdirs])
      # first recursively deal with the directories
      for entry in subdirs:
        nu_node = QtGui.QStandardItem(entry)
        nu_node.setEditable(False)
        nu_node.setData(None, role=QtCore.Qt.ItemDataRole.UserRole)
        useFont = nu_node.font()
        useFont.setItalic(True)
        nu_node.setFont(useFont)
        nu_node.setIcon(FOM_ICON_DIR)
        node.appendRow(nu_node)
        self.recursiveNodeMaker(nu_node, item[entry])
      # then deal with the individuals
      for entry in individuals:
        label = entry.split('.ffunc')[0]
        item_node = QtGui.QStandardItem(label)
        item_node.setEditable(False)
        item_node.setIcon(FOM_ICON_FUNCTION)
        node.appendRow(item_node)
        item_node.setData(item[entry], role=QtCore.Qt.ItemDataRole.UserRole)
 
  def recursiveNodeList(self, nodeList, item, currDir):
    # recursively adds entries to node list
    if(PATH_SEPARATOR in item):
      dissectPath = item.split(PATH_SEPARATOR)
      first, rest = dissectPath[0], PATH_SEPARATOR.join(dissectPath[1:])
      if(not first in nodeList):
        nodeList[first] = {}
      self.recursiveNodeList(nodeList[first], rest, currDir + first + PATH_SEPARATOR)
    else:
      # store full path here
      nodeList[item] = currDir + item
      
  def resizeMe(self, width=None, maxHeight=None):
    # work-around failing autosizing (add 1 to row number to allow spacing)
    if(width != None):
      self.useWidth = width
    if(maxHeight != None):
      self.maxHeight = maxHeight
    #verticalSize = (self.treeModel.rowCount() + 1) * self.tree.sizeHintForRow(0)
    verticalSize = int((self.tree.countAllVisible() + .5) * self.tree.sizeHintForRow(0))
    # ensure minimum size
    verticalSize = max(100, verticalSize)
    # don't exceed maxHeight
    verticalSize = min(self.maxHeight, verticalSize)
    # resize now
    self.resize(self.useWidth, verticalSize)
    
class FitArea(QWidgetMac):
  def __init__(self, parent=None):
    super(FitArea, self).__init__()
    self.parent = parent
    self.validFloat = MyValidFloat()
    self.param = []
    self.storeParam = []
    self.confidence = []
    self.confidence_apriori = []
    self.param_active = []
    self.param_lower, self.param_upper = [], []
    self.chisquare, self.red_chisquare, self.rsquare = 0, 0, 0
    self.ffuncList = self.initFfunc(searchPath=WORKINGDIR + PATH_SEPARATOR + 'functions' + PATH_SEPARATOR)
    self.fitResultsHeader = '<h3>Latest fit results</h3><br/>'
    self.outstring = self.fitResultsHeader
    self.bootString = ''
    self.covar, self.covarParameters = [], []
    # use epsilon as minimum error value and for calculating derivatives
    self.EPSILON = 1e-9
    # define amplitude threshold for random variation of parameters
    self.MIN_AMPLITUDE = 0.5
    # flag to determine whether errors are used for fitting
    self.errorSwitch, self.errorModel = True, 0
    self.errorConst, self.errorPercent, self.errorMultiply = 1.0, 5.0, 1.0
    
    # advanced export settings
    self.advancedMinimization = {'algorithm': 'Nelder-Mead', 'xtol': 1e-8, 'maxfev': 1e5}
    self.advancedRandomSearch = {'cycles': 5, 'escalate': 1.5, 'maxfev': 1e4}
    self.advancedBootstrap = {'trials': 500}

    # set up GUI
    self.buildRessource()

  def buildRessource(self):
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setSpacing(2)

    self.masterwidget = QtWidgets.QSplitter(QtCore.Qt.Orientation.Vertical)
    self.vLayout.addWidget(self.masterwidget)
    self.masterwidget.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
    self.masterwidget.setChildrenCollapsible(False)
    
    # set up container for fit formula
    self.fitFunctionContainer = QWidgetMac()
    self.masterwidget.addWidget(self.fitFunctionContainer)
    self.LayoutFitFunctionContainer = QtWidgets.QVBoxLayout(self.fitFunctionContainer)
    self.LayoutFitFunctionContainer.setContentsMargins(0, 0, 0, 0)

    self.functionBox = QWidgetMac()
    self.hLayout00 = QtWidgets.QHBoxLayout(self.functionBox)
    self.hLayout00.setContentsMargins(0, 0, 0, 0)
    self.LayoutFitFunctionContainer.addWidget(self.functionBox)
    self.functionLabel = QtWidgets.QLabel()
    useFont = self.functionLabel.font()
    useFont.setBold(True)
    self.functionLabel.setFont(useFont)
    self.functionLabel.setText('Function')
    self.functionLabel.setMinimumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.functionLabel.setMaximumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout00.addWidget(self.functionLabel)

    # set up selector for fit functions
    self.functionSelector = QPushButtonMac()
    self.functionSelector.setText('blubbi')
    self.functionSelector.setMinimumHeight(int(BASE_SIZE))
    self.functionSelector.setMaximumHeight(int(BASE_SIZE))
    self.hLayout00.addWidget(self.functionSelector)
    self.functionSelector.clicked.connect(self.functionSelectorCallback)
    self.functionSelector.setToolTip('Choose fit formula')
    self.functionSelector.setObjectName('leftalign')

    # set up field that declares parameters
    self.declareParamBox = QWidgetMac()
    self.hLayout = QtWidgets.QHBoxLayout(self.declareParamBox)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.declareParamLabel = QtWidgets.QLabel()
    useFont = self.declareParamLabel.font()
    useFont.setBold(True)
    self.declareParamLabel.setFont(useFont)
    self.declareParamLabel.setText('Parameters')
    self.declareParamLabel.setMinimumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.declareParamLabel.setMaximumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.declareParamLabel)
    self.declareParamEntry = QLineEditClick()
    self.declareParamEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.declareParamEntry.setMinimumHeight(int(BASE_SIZE))
    self.declareParamEntry.setMaximumHeight(int(BASE_SIZE))
    self.declareParamEntry.setToolTip('Declare parameters as comma-separated list')
    self.hLayout.addWidget(self.declareParamEntry)
    
    self.LayoutFitFunctionContainer.addWidget(self.declareParamBox)
    
    # set up edit field for fit function
    self.fitFormulaBox = QWidgetMac()
    self.LayoutFitFormulaBox = QtWidgets.QHBoxLayout(self.fitFormulaBox)
    self.LayoutFitFormulaBox.setContentsMargins(0, 0, 0, 0)
    self.LayoutFitFormulaBox.setSpacing(0)
    self.LayoutFitFunctionContainer.addWidget(self.fitFormulaBox)

    self.fitFormulaLineNumbers = myQTextEditLineNumbers(self)
    self.LayoutFitFormulaBox.addWidget(self.fitFormulaLineNumbers, stretch=0)

    self.fitFormula = myQTextEdit(self)
    self.fitFormula.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
    self.fitFormula.setGeometry(QtCore.QRect(0, 0, 500, 600))
    self.LayoutFitFormulaBox.addWidget(self.fitFormula, stretch=1)
    # tool tip kind of disturbs when editing the formula
    ###self.fitFormula.setToolTip('Edit the fit formula')
    self.fitFormula.lineNumbersField = self.fitFormulaLineNumbers

    # read all available fit functions
    if (len(self.ffuncList)):
      defaultFunction = 'binding_isotherm.ffunc'
      functionNo = 0
      for index, entry in enumerate(self.ffuncList):
        if(entry.endswith(defaultFunction)):
          functionNo = index
      success, parameters, formula, values, active = self.loadFfunc(self.ffuncList[functionNo])
      self.currentFunction = self.ffuncList[functionNo]
      label = Path(self.currentFunction)
      label = label.name.split('.ffunc')[0]
      self.functionSelector.setText('\u25be ' + label)
      self.currentFunction = self.ffuncList[functionNo]
      self.paramList = parameters
      parameters = ', '.join(parameters)
    else:
      self.currentFunction = ''
      parameters = 'A0, A1, KD'
      self.paramList = ['A0', 'A1', 'KD']
      formula = '# a binding isotherm\ny = A0 + A1 * x / (x + KD)'
      values = [1, 1, 1]
      active = [True] * 3
      self.functionSelector.setText('\u25be binding isotherm')

    self.declareParamEntry.setText(parameters)
    self.fitFormula.setText(formula)
    self.param = values
    self.storeParam = [i for i in self.param]
    self.confidence = ['--'] * len(values)
    self.confidence_apriori = ['--'] * len(values)
    self.param_active = [1 if i else 0 for i in active]
    
    # set up buttons
    self.buttonContainer = QWidgetMac()
    self.buttonContainer.setContentsMargins(0, 0, 0, 0)
    self.LayoutFitFunctionContainer.addWidget(self.buttonContainer)
    self.LayoutButtonContainer = QtWidgets.QHBoxLayout(self.buttonContainer)
    self.LayoutButtonContainer.setContentsMargins(0, 0, 0, 0)
    
    self.loadFitButton = QPushButtonMac()
    self.loadFitButton.setText(' Open Fit Function')
    self.loadFitButton.setToolTip('Load formula from file')
    self.loadFitButton.setMinimumHeight(int(BASE_SIZE))
    self.loadFitButton.setMaximumHeight(int(BASE_SIZE))
    self.loadFitButton.clicked.connect(self.openFit)
    self.loadFitButton.setIcon(FOM_ICON_OPEN)
    self.LayoutButtonContainer.addWidget(self.loadFitButton)
    self.saveFitButton = QPushButtonMac()
    self.saveFitButton.setText(' Save Fit Function')
    self.saveFitButton.setToolTip('Save formula to file')
    self.saveFitButton.setMinimumHeight(int(BASE_SIZE))
    self.saveFitButton.setMaximumHeight(int(BASE_SIZE))
    self.saveFitButton.clicked.connect(self.saveFit)
    self.saveFitButton.setIcon(FOM_ICON_SAVE)
    self.LayoutButtonContainer.addWidget(self.saveFitButton)
    self.useFitButton = QPushButtonMac()
    self.useFitButton.setText(' Apply Fit Function')
    self.useFitButton.setToolTip('Update formula in curve')
    self.useFitButton.setMinimumHeight(int(BASE_SIZE))
    self.useFitButton.setMaximumHeight(int(BASE_SIZE))
    self.useFitButton.clicked.connect(partial(self.useFit, True))
    self.useFitButton.setIcon(FOM_ICON_MEDIA_PLAY)
    self.LayoutButtonContainer.addWidget(self.useFitButton)

    # set up container for parameter table
    self.parameterTableContainer = QWidgetMac()
    self.masterwidget.addWidget(self.parameterTableContainer)
    self.LayoutParameterTableContainer = QtWidgets.QVBoxLayout(self.parameterTableContainer)
    self.LayoutParameterTableContainer.setContentsMargins(0, 0, 0, 0)

    # set up parameter table
    self.ParamTable = QtWidgets.QTableWidget()
    self.ParamTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.ParamTable.setEnabled(True)
    self.ParamTable.setColumnCount(5)
    self.ParamTable.setRowCount(1)
    item = QTableWidgetItemToolTip('Fit')
    item.setToolTip('This column controls whether fit parameters are varied during the fit or minimization.')
    self.ParamTable.setHorizontalHeaderItem(0, item)
    item = QTableWidgetItemToolTip('Value')
    item.setToolTip('This column holds the value of the fit parameters.')
    self.ParamTable.setHorizontalHeaderItem(1, item)
    item = QTableWidgetItemToolTip('Error')
    item.setToolTip('This column holds the confidence interval of the fit parameters.\n(Calculated based on estimated data errors.)')
    self.ParamTable.setHorizontalHeaderItem(2, item)
    item3 = QtWidgets.QTableWidgetItem('a priori')
    item3.setToolTip('This column holds the <i>a priori</i> confidence interval of the fit parameters.\n(Calculated based on supplied data errors.)')
    useFont = item3.font()
    useFont.setItalic(True)
    item3.setFont(useFont)
    self.ParamTable.setHorizontalHeaderItem(3, item3)
    item = QTableWidgetItemToolTip('Boundaries')
    item.setToolTip('This column allows to configure boundaries for the fit parameters during the fit or minimization.')
    self.ParamTable.setHorizontalHeaderItem(4, item)
    self.LayoutParameterTableContainer.addWidget(self.ParamTable)
    self.updateParamTable()
    self.useFit(redraw=False, keepBoot=False)
   
    # set fit controls 2
    self.ButtonContainer2 = QWidgetMac()
    self.LayoutParameterTableContainer.addWidget(self.ButtonContainer2)
    self.LayoutButtonContainer2 = QtWidgets.QHBoxLayout(self.ButtonContainer2)
    self.LayoutButtonContainer2.setContentsMargins(0, 0, 0, 0)

    # set up Fit button
    self.doFitButton = QPushButtonMac()
    self.doFitButton.setText(' Fit Data')
    self.doFitButton.setToolTip('Perform least-squares fit\n(Ctrl-F)')
    self.doFitButton.setMinimumHeight(int(BASE_SIZE))
    self.doFitButton.setMaximumHeight(int(BASE_SIZE))
    self.doFitButton.clicked.connect(self.doFit)
    self.doFitButton.setIcon(FOM_ICON_GEAR)
    self.LayoutButtonContainer2.addWidget(self.doFitButton)

    # set up minimize button
    self.doMinButton = QPushButtonMac()
    self.doMinButton.setText(' ' + self.advancedMinimization['algorithm'])
    self.doMinButton.setToolTip('Perform numerical minimization\n(Ctrl-N)')
    self.doMinButton.setMinimumHeight(int(BASE_SIZE))
    self.doMinButton.setMaximumHeight(int(BASE_SIZE))
    self.doMinButton.clicked.connect(self.doMin)
    self.doMinButton.setIcon(FOM_ICON_GEAR)
    self.LayoutButtonContainer2.addWidget(self.doMinButton)

    # set up random fit button
    self.doBruteButton = QPushButtonMac()
    self.doBruteButton.setText(' Random')
    self.doBruteButton.setToolTip('Perform random search of parameters')
    self.doBruteButton.setMinimumHeight(int(BASE_SIZE))
    self.doBruteButton.setMaximumHeight(int(BASE_SIZE))
    self.doBruteButton.clicked.connect(self.doBruteFit)
    self.doBruteButton.setIcon(FOM_ICON_DICE)
    self.LayoutButtonContainer2.addWidget(self.doBruteButton)

    # set up Reset button
    self.resetButton = QPushButtonMac()
    self.resetButton.setText(' Reset')
    self.resetButton.setToolTip('Reset function parameters to previous values')
    self.resetButton.setMinimumHeight(int(BASE_SIZE))
    self.resetButton.setMaximumHeight(int(BASE_SIZE))
    self.resetButton.clicked.connect(self.resetParam)
    self.resetButton.setIcon(FOM_ICON_RESET)
    self.LayoutButtonContainer2.addWidget(self.resetButton)

    # set fit controls 3
    self.ButtonContainer3 = QWidgetMac()
    self.LayoutParameterTableContainer.addWidget(self.ButtonContainer3)
    self.LayoutButtonContainer3 = QtWidgets.QHBoxLayout(self.ButtonContainer3)
    self.LayoutButtonContainer3.setContentsMargins(0, 0, 20, 0)
 
    self.errorLabel = QPushButtonCheckable()
    self.errorLabel.setChecked(self.errorSwitch)
    self.errorLabel.setText('Error weights?')
    self.errorLabel.setToolTip('Use \u0394y errors as weights in fit?\nUncheck to perform unweighted fit.')
    self.errorLabel.setMinimumHeight(int(BASE_SIZE))
    self.errorLabel.setMaximumHeight(int(BASE_SIZE))
    self.errorLabel.clicked.connect(self.toggleErrorSwitch)
    self.LayoutButtonContainer3.addWidget(self.errorLabel, stretch=1)

    self.choiceContainer = QWidgetMac()
    self.LayoutButtonContainer3.addWidget(self.choiceContainer, stretch=3)
    self.LayoutChoiceContainer = QtWidgets.QHBoxLayout(self.choiceContainer)
    self.LayoutChoiceContainer.setContentsMargins(0, 0, 0, 0)
    
    self.errorButtons = []
    self.errorButtons.append(QRadioButtonToolTip())
    self.errorButtons[-1].setText('const')
    self.errorButtons[-1].setChecked(1 == self.errorModel)
    self.errorButtons[-1].setToolTip('Use constant \u0394y error')
    self.errorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 1))
    self.LayoutChoiceContainer.addWidget(self.errorButtons[-1])
    
    self.errorConstEntry = QLineEditClick()
    self.errorConstEntry.setText(str(self.errorConst))
    self.errorConstEntry.setToolTip('Use constant \u0394y error')
    self.errorConstEntry.setMinimumHeight(int(BASE_SIZE))
    self.errorConstEntry.setMaximumHeight(int(BASE_SIZE))
    self.errorConstEntry.setMaximumWidth(int(40 * SCALEFONT))
    self.errorConstEntry.setValidator(self.validFloat)
    self.errorConstEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorConstEntry, 'errorConst'))
    self.errorConstEntry.focusOutEvent = partial(self.lostFocus, self.errorConstEntry, 'errorConst', self.errorConstEntry.focusOutEvent)
    self.errorConstEntry.focusInEvent = partial(self.gainFocus, self.errorButtons[-1], self.errorConstEntry.focusInEvent)
    self.LayoutChoiceContainer.addWidget(self.errorConstEntry)

    self.errorButtons.append(QRadioButtonToolTip())
    self.errorButtons[-1].setText('prop')
    self.errorButtons[-1].setChecked(2 == self.errorModel)
    self.errorButtons[-1].setToolTip('Use \u0394y error proportional to y value')
    self.errorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 2))
    self.LayoutChoiceContainer.addWidget(self.errorButtons[-1])
    
    self.errorPercentEntry = QLineEditClick()
    self.errorPercentEntry.setText(str(self.errorPercent))
    self.errorPercentEntry.setToolTip('Use \u0394y error proportional to y value')
    self.errorPercentEntry.setMinimumHeight(int(BASE_SIZE))
    self.errorPercentEntry.setMaximumHeight(int(BASE_SIZE))
    self.errorPercentEntry.setMaximumWidth(int(40 * SCALEFONT))
    self.errorPercentEntry.setValidator(self.validFloat)
    self.errorPercentEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorPercentEntry, 'errorPercent'))
    self.errorPercentEntry.focusOutEvent = partial(self.lostFocus, self.errorPercentEntry, 'errorPercent', self.errorPercentEntry.focusOutEvent)
    self.errorPercentEntry.focusInEvent = partial(self.gainFocus, self.errorButtons[-1], self.errorPercentEntry.focusInEvent)
    self.LayoutChoiceContainer.addWidget(self.errorPercentEntry)

    self.errorPercentLabel = QtWidgets.QLabel()
    self.errorPercentLabel.setText('%')
    self.LayoutChoiceContainer.addWidget(self.errorPercentLabel)

    self.errorButtons.append(QRadioButtonToolTip())
    self.errorButtons[-1].setText('\u0394y \u00D7')
    self.errorButtons[-1].setChecked(0 == self.errorModel)
    self.errorButtons[-1].setToolTip('Use \u0394y error assigned in dataset')
    self.errorButtons[-1].toggled.connect(partial(self.toggleErrorModel, 0))
    self.LayoutChoiceContainer.addWidget(self.errorButtons[-1])

    self.errorDeltaEntry = QLineEditClick()
    self.errorDeltaEntry.setText(str(self.errorMultiply))
    self.errorDeltaEntry.setToolTip('Multiply assigned \u0394y by factor')
    self.errorDeltaEntry.setValidator(self.validFloat)
    self.errorDeltaEntry.setMinimumHeight(int(BASE_SIZE))
    self.errorDeltaEntry.setMaximumHeight(int(BASE_SIZE))
    self.errorDeltaEntry.setMaximumWidth(int(40 * SCALEFONT))
    self.errorDeltaEntry.editingFinished.connect(partial(self.validateErrorEntry, self.errorDeltaEntry, 'errorMultiply'))
    self.errorDeltaEntry.focusOutEvent = partial(self.lostFocus, self.errorDeltaEntry, 'errorMultiply', self.errorDeltaEntry.focusOutEvent)
    self.errorDeltaEntry.focusInEvent = partial(self.gainFocus, self.errorButtons[-1], self.errorDeltaEntry.focusInEvent)
    self.LayoutChoiceContainer.addWidget(self.errorDeltaEntry)
    self.LayoutChoiceContainer.addStretch()

    # set up container for fit results
    self.fitResultsContainer = QWidgetMac()
    self.masterwidget.addWidget(self.fitResultsContainer)
    self.LayoutFitResultsContainer = QtWidgets.QVBoxLayout(self.fitResultsContainer)
    self.LayoutFitResultsContainer.setContentsMargins(0, 0, 0, 0)
    
    # on Windows and Linux add markers for splitter
    if(not (platform == 'darwin')):
      self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='horizontal', number=1, repeat=5)
      self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='horizontal', number=2, repeat=5)

    # set up text edit field for displaying fit results
    self.fitResults = myQTextEditBase()
    self.fitResults.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
    self.fitResults.setGeometry(QtCore.QRect(0, 0, 500, 600))
    self.LayoutFitResultsContainer.addWidget(self.fitResults)
    self.fitResults.setReadOnly(True)
    self.fitResults.setText(self.fitResultsHeader)
    self.fitResults.setToolTip('Results from previous least-squares fit')
    self.parent.fit[self.parent.activeFit].fitresults = self.fitResultsHeader
    
    # set up button for display of covariances
    self.covarButton = QPushButtonMac(self.fitResultsContainer)
    self.covarButton.setText('Covariances')
    self.covarButton.setToolTip('Display covariances from previous least-squares fit')
    self.covarButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.covarButton.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.covarButton.clicked.connect(self.displayCovar)
    self.repositionCovarButton()
    self.covarButton.hide()
    
    # set up button for bootstrapping
    self.bootButton = QPushButtonMac(self.fitResultsContainer)
    self.bootButton.setText('Bootstrap')
    self.bootButton.setToolTip('Determine 95% confidence bands via bootstrapping')
    self.bootButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.bootButton.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.bootButton.clicked.connect(self.bootstrap)
    self.repositionBootButton()
    self.bootButton.hide()

    # set up button for confidences based on fit parameters
    self.confidenceButton = QPushButtonMac(self.fitResultsContainer)
    self.confidenceButton.setText('Conf. Bands')
    self.confidenceButton.setToolTip('Determine 95% confidence bands')
    self.confidenceButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.confidenceButton.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.confidenceButton.clicked.connect(self.sigmaConfidence)
    self.repositionConfidenceButton()
    self.confidenceButton.hide()

    self.fitResults.resizeEvent = self.customResize

  def functionSelectorCallback(self):
    # opens menu with QTreeView for function selection
    self.menu = FunctionMenu(parent=self)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    try:
      qPoint = self.functionSelector.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      menuY += self.functionSelector.geometry().height()
    except:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    menuX, menuY = max(menuX, 0), max(menuY, 0)
    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)
    # PyQt fails miserably when determining size requirements for QMenu() => hard code this :(
    ###self.menu.resize(self.functionSelector.geometry().width(), 500)
    self.menu.resizeMe(width=self.functionSelector.geometry().width(), maxHeight=600)

  def gainFocus(self, toggleOption=None, defaultHandler=None, event=None):
    # entry field gained focus
    # select corresponding option
    if(toggleOption != None):
      toggleOption.setChecked(True)

    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)

  def lostFocus(self, entryobject=None, quantity=None, defaultHandler=None, event=None):
    # entry field lost focus, perform sanity check
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        self.__dict__[quantity] = float(entrytext)
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))
    # pass signal to original handler
    if(defaultHandler != None):
      defaultHandler(event)
    
  def validateErrorEntry(self, entryobject=None, quantity=None):
    # validates entryfield
    if(entryobject != None):
      entrytext = entryobject.text()
      if(self.parent.isNumber(entrytext)):
        newnumber = float(entrytext)
        self.__dict__[quantity] = np.abs(newnumber)
        if(newnumber < 0):
          entryobject.setText(str(np.abs(newnumber)))
      else:
        # restore previous value
        entryobject.setText(str(self.__dict__[quantity]))

  def toggleErrorSwitch(self):
    # use errors?
    value = self.errorLabel.isChecked()
    self.errorSwitch = value

  def toggleErrorModel(self, mode=0):
    # change error model
    self.errorModel = mode
    # check switch button
    self.errorLabel.setChecked(True)
    self.errorSwitch = True

  def addSplitterHandleSymbols(self, parentHandle=None, direction='horizontal', number=1, repeat=5):
    # adds spacer symbols to splitter handle
    if(parentHandle != None):
      if(direction in ['horizontal', 'vertical']):
        parentHandle.setHandleWidth(5)
        handli = parentHandle.handle(number)
        if(handli != None):
          if(direction == 'horizontal'):
            layoutli = QtWidgets.QHBoxLayout(handli)
          else:
            layoutli = QtWidgets.QVBoxLayout(handli)
          layoutli.addStretch()
          for repeat in range(repeat):
            bogus = QtWidgets.QWidget()
            bogus.setStyleSheet('QWidget {background-color: #888888;border-radius: 2px;}')
            bogus.setMinimumSize(4, 4)
            bogus.setMaximumSize(4, 4)
            layoutli.addWidget(bogus)
          layoutli.addStretch()
          layoutli.setSpacing(3)
          layoutli.setContentsMargins(1, 0, 0, 0)

  def customResize(self, event):
    # hijack resize event to allow repositioning of covariance button
    myQTextEditBase.resizeEvent(self.fitResults, event)
    if(hasattr(self, 'covarButton')):
      self.repositionCovarButton()
    if(hasattr(self, 'bootButton')):
      self.repositionBootButton()
    if(hasattr(self, 'confidenceButton')):
      self.repositionConfidenceButton()

  def repositionCovarButton(self):
    # dynamically adjusts position of covariance button
    topRight = self.fitResults.rect().topRight()
    width, height = self.covarButton.geometry().width(), self.covarButton.geometry().height()
    self.covarButton.move(QtCore.QPoint(int(topRight.x() - width - 16), int(topRight.y() + 0 * height + 4)))

  def displayCovar(self):
    # opens new window for display of covariances
    if(len(self.covar)):
      if(not (hasattr(self.parent, 'covarWindow'))):
        self.parent.covarWindow = CovarWindow()
        self.parent.covarWindow.setWindowTitle('Covariances')
      self.parent.covarWindow.covarLabel.setText('Normalized Covariances')
      self.parent.covarWindow.move(QtGui.QCursor.pos())

      # populate covariance table
      self.parent.covarWindow.populateCovariances(names=self.covarParameters, covar=self.covar)
      
      # apply styles and show
      if(QSTYLE != None):
        self.parent.covarWindow.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.parent.covarWindow.setStyleSheet(QSTYLESHEET)
      self.parent.covarWindow.show()
      self.parent.covarWindow.activateWindow()
      self.parent.covarWindow.focusNextChild()

  def repositionConfidenceButton(self):
    # dynamically adjusts position of covariance button
    topRight = self.fitResults.rect().topRight()
    width, height = self.confidenceButton.geometry().width(), self.confidenceButton.geometry().height()
    self.confidenceButton.move(QtCore.QPoint(int(topRight.x() - width - 16), int(topRight.y() + height + 4)))

  def sigmaConfidence(self):
    # clear bootstrap data (if existing and switch to calculated confidence bands)
    if(('xdata' in self.parent.fit[self.parent.activeFit].lastFitResults) and len(self.parent.fit[self.parent.activeFit].lastFitResults['xdata'])):
      self.parent.fit[self.parent.activeFit].clearBoot()
      self.parent.fit[self.parent.activeFit].bootType = False
      
      # issue plot refresh
      self.parent.fit[self.parent.activeFit].drawMe(redraw=True)
    else:
      self.parent.statusbar.showMessage('Cannot display confidence bands. Perform data fit first!', self.parent.STATUS_TIME)
    
  # the following routine disabled and replaced by sigmaConfidence() above
  '''
  def sigmaConfidence_XXX(self):
    # calculate fit values for all combinations of +/- sigma
    # general approach:
    # 1. retrieve all parameters
    # 2. determine which ones have confidences associated
    # 3. cycle over all parameter combinations plus/minus sigma
    # 4. store curve and parameters

    # the folllowing adapted from doFit()
    # check whether there is at least one floating parameter from last fit
    if(not len(self.param_active)):
      self.parent.statusbar.showMessage('At least one parameter has to be active for calculating confidence bands!', self.parent.STATUS_TIME)
      return False
    else:
      # display busy pointer
      QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
      
      # retrieve fit parameters
      origParam = [i for i in self.param]
      confidence = [0 if (self.confidence[i] == '--') else self.confidence[i] for i in range(len(self.param_active))]
      ###print(origParam, confidence, self.param_active)
      
      # initialize arrays
      storeFval, storeFitpa = [], []
      storeFitpa.append([origParam[i] for i in range(len(origParam)) if (self.param_active[i])])
      _, fval = self.parent.fit[self.parent.activeFit].evaluateFunc(x=self.parent.fit[self.parent.activeFit].x, param=origParam)
      storeFval.append(fval)
      
      # cycle over all parameter combinations
      for iteration in range(2 ** sum(self.param_active)):
        # this list comprehension prepares all combinations of plus/minus sigma for all active parameters
        factor = 1.96 ### this corresponds to 95% confidence bands
        factor = 1. ### this corresponds to 68.2% confidence bands
        currParam = [origParam[i] if (not self.param_active[i]) else (origParam[i] + factor * confidence[i]) if (iteration & 2 ** sum(self.param_active[:i])) else (origParam[i] - factor * confidence[i]) for i in range(len(origParam))]
        ###print(iteration + 1, iteration & 1, iteration & 2, iteration & 4, currParam)
        try:
          _, fval = self.parent.fit[self.parent.activeFit].evaluateFunc(x=self.parent.fit[self.parent.activeFit].x, param=currParam)
          storeFitpa.append([currParam[i] for i in range(len(currParam)) if (self.param_active[i])])
          storeFval.append(fval)
        except:
          pass
        
      # now store this in fit object
      self.parent.fit[self.parent.activeFit].storeBoot(fparam=storeFitpa, bootType=False)
      
      # restore pointer
      QtWidgets.QApplication.restoreOverrideCursor()
      
      # issue plot refresh
      self.parent.fit[self.parent.activeFit].drawMe(redraw=True)
  '''

  def repositionBootButton(self):
    # dynamically adjusts position of bootstrapping button
    topRight = self.fitResults.rect().topRight()
    width, height = self.bootButton.geometry().width(), self.bootButton.geometry().height()
    self.bootButton.move(QtCore.QPoint(int(topRight.x() - width - 16), int(topRight.y() + 2 * height + 4)))

  def bootstrap(self):
    # conduct bootstrapping
    # general approach:
    # 1. randomly choose data points with replacement
    # 2. NLLS fit
    # 3. store curve and parameters
    # 4. repeat xx times
    # 5. central limit theorem says we can expect normal distribution
    #    => extract boundaries for curves at 1 sigma (68%) or 2 sigma (95%)
    # 6. plot envelope and prepare bootstrapped report
    #
    # credits to:
    # https://erikbern.com/2018/10/08/the-hackers-guide-to-uncertainty-estimates.html

    # the folllowing adapted from doFit()
    # check whether there is at least one floating parameter
    if(np.sum(self.param_active) == 0):
      self.parent.statusbar.showMessage('At least one parameter has to be free for bootstrapping!', self.parent.STATUS_TIME)
      return False
    else:
      # get data from data object and start the fit procedure
      # check whether we have a data selection
      if(self.parent.selectedData.isLive):
        fullData = self.parent.selectedData.value()
      else:
        fullData = self.parent.data[self.parent.activeData].value()
      # check number of fit parameters vs. data points
      if((not 'x' in fullData) or (len(fullData['x']) < np.sum(self.param_active))):
        try:
          nop = len(fullData['x'])
        except:
          nop = 0
        self.parent.statusbar.showMessage('No. of data points (' + str(nop) + ') should at least equal no. of free parameters (' + str(np.sum(self.param_active)) + ')!', self.parent.STATUS_TIME)
        return False
      else:
        # display busy pointer
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
        self.runFlag = True
        
        # open an extra window to interact with procedure
        self.daughterWindow = BootWindow(self, 'Bootstrapping in Progress')
        # apply styles to popup window
        if(QSTYLE != None):
          self.daughterWindow.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.daughterWindow.setStyleSheet(QSTYLESHEET)
        self.daughterWindow.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
        self.daughterWindow.show()
        
        # remember current parameters
        origParam = [i for i in self.parent.fit[self.parent.activeFit].param]
        
        iteration, nboot, selrange = 0, self.advancedBootstrap['trials'], len(fullData['x'])
        storeFval, storeFitpa = [], []
        while(self.runFlag and (iteration < nboot)):
          # prepare useData
          useIndices = np.random.choice(range(selrange), size=selrange, replace=True)
          useData = {key: value[useIndices] for key, value in fullData.items()}
        
          # fit w/ bootstrapped data
          boundaryLow = [i for i, j in zip(self.param_lower, self.param_active) if j]
          boundaryHigh = [i for i, j in zip(self.param_upper, self.param_active) if j]
          # pass original parameters (from previous fit) as initial parameters for fresh slate
          success, fitpa, covar, usedAlgorithm = self.parent.fit[self.parent.activeFit].fitFunc(useData, initpa=origParam, boundaryLow=boundaryLow, boundaryHigh=boundaryHigh)
          ###success, fitpa, covar, usedAlgorithm = self.parent.fit[self.parent.activeFit].fitFunc(useData, boundaryLow=boundaryLow, boundaryHigh=boundaryHigh)
          if (success):
            # store fitted parameters and fit values
            storeFitpa.append(fitpa)
            _, fval = self.parent.fit[self.parent.activeFit].simulateFunc(x=self.parent.fit[self.parent.activeFit].x)
            storeFval.append(fval)
            
          # update label?
          if(not (iteration % 10)):
            self.daughterWindow.iterLabel.setText('trial ' + str(iteration) + ' / ' + str(int(nboot)))
            QtCore.QCoreApplication.processEvents()
          iteration += 1
        
        # need to restore original params to curve object
        self.parent.fit[self.parent.activeFit].param = [i for i in origParam]
        # don't need any plot or table updates as we should still be at old values
        
        # now store this in fit object
        self.parent.fit[self.parent.activeFit].storeBoot(fparam=storeFitpa, bootType=True)
        
        # close window
        if(hasattr(self.daughterWindow, 'close')):
          self.daughterWindow.close()
        self.daughterWindow = None
        
        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()
        
        # issue plot refresh
        ####self.parent.plotArea.dataplotwidget.myRefresh()
        ###self.parent.fit[self.parent.activeFit].duplicateForSplit()
        self.parent.fit[self.parent.activeFit].drawMe(redraw=True)

        # provide some output on bootstrapped parameter intervals
        bootOutstring = '<u>Bootstrap 95% confidence</u>\n<span style="white-space: pre;">'
        bootFpar, different = self.parent.fit[self.parent.activeFit].getBoot()
        bootFpar = np.array(bootFpar)
        dy, dx = bootFpar.shape
        if(dx):
          print('Bootstrap detailed output\n-------------------------\n')
        for i in range(dx):
          hist, edges = np.histogram(bootFpar[:, i], bins=20)
          for ind, val in enumerate(hist):
            print(self.parent.formatNumber((edges[ind] + edges[ind + 1]) / 2.) + '\t%d' % val)
          print('\n')
          
        # and now get the 95% confidence levels
        confi = self.parent.plotArea.myNanPercentile(bootFpar, 2.5, 97.5)
        
        # conduct test for normality
        kolmogorov = []
        for i in range(dx):
          kolmi = stats.kstest(bootFpar[:, i], stats.norm.cdf)
          kolmogorov.append(kolmi.pvalue)

        # compile output string
        count = 0
        for index, active in enumerate(self.param_active):
          if(active):
            bootOutstring += '  ' + self.parent.formatNumber(confi[0][count]) + ' &lt; '
            bootOutstring += self.paramList[index] + ' &lt; ' + self.parent.formatNumber(confi[1][count])
            bootOutstring += ' (normality ' + self.parent.formatNumber(kolmogorov[count]) + ')\n'
            count += 1
          
        bootOutstring += '</style>\n'
        bootOutstring = bootOutstring.replace('\n', '<br/>')
        self.bootString = bootOutstring
        self.fitResults.setText(self.outstring + bootOutstring)

  def openFit(self):
    # loads a fit function from file (bypassing the function selector)
    # open file dialog
    filename, fitler_ = QtWidgets.QFileDialog.getOpenFileName(self, filter='Fit function (*.ffunc)', directory=self.currentFunction, caption='Open Fit Function')
    filename = str(filename)
    success, parameters, formula, values, active = self.loadFfunc(filename)
    if(success):
      self.currentFunction = filename
      label = Path(self.currentFunction)
      label = label.name.split('.ffunc')[0]
      self.functionSelector.setText('\u25be ' + label)
      
      self.paramList = parameters
      parameters = ', '.join(parameters)
      self.declareParamEntry.setText(parameters)
      self.fitFormula.setText(formula)
      self.param = values
      self.storeParam = [i for i in self.param]
      self.confidence = ['--'] * len(values)
      self.confidence_apriori = ['--'] * len(values)
      self.param_active = [1 if i else 0 for i in active]
      self.param_lower, self.param_upper = [-np.inf] * len(values), [np.inf] * len(values)
      self.updateParamTable()
      self.useFit(keepBoot=False)
    else:
      self.parent.statusbar.showMessage('Cannot load function ' + filename + '!', self.parent.STATUS_TIME)

  def saveFit(self):
    # saves fit function
    # open save dialog
    saveDialog = QtWidgets.QFileDialog()
    saveDialog.selectFile(self.currentFunction)   # strangely enough, this call is not heeded
    filename, fitler_ = saveDialog.getSaveFileName(self, filter='Fit function (*.ffunc)', directory=self.currentFunction, caption='Save Fit Function')
    filename = str(filename)
    try:
      with open(filename, 'w', encoding='utf-8') as savehandle:
        # write parameters
        savehandle.write('<PARAMETERS>\n')
        for index, entry in enumerate(self.paramList):
          red = entry + ', ' + self.parent.formatNumber(self.param[index])
          red += ', ' + str(self.param_active[index]) + '\n'
          savehandle.write(red)
        
        # write formula
        savehandle.write('<FORMULA>\n')
        red = str(self.fitFormula.toPlainText())
        savehandle.write(red)
      
      # update information
      self.currentFunction = filename
      label = Path(filename)
      label = label.name.split('.ffunc')[0]
      self.functionSelector.setText('\u25be ' + label)
      
      # add function to ffunclist and update selector
      self.ffuncList = self.initFfunc(searchPath=WORKINGDIR + PATH_SEPARATOR + 'functions' + PATH_SEPARATOR)
        
      # update storeParam
      self.storeParam = [i for i in self.param]
    except:
      self.parent.statusbar.showMessage('Cannot write function file ' + filename, self.parent.STATUS_TIME)

  def getRelativeDerivatives(self, xval, yval, yerr):
    # determine derivatives of fit parameters
    x, self.startVal = self.parent.fit[self.parent.activeFit].evaluateFunc(x=xval, param=self.param_active_list)
    self.startResid = np.sum((yval - self.startVal) ** 2 / yerr ** 2)

    # cycle through parameters and calc. derivatives
    for index, entry in enumerate(self.param_active_list):
      workparam = [i for i in self.param_active_list]
      workparam[index] = self.param[index] * (1.0 + self.EPSILON)
      x, perturbVal = self.parent.fit[self.parent.activeFit].evaluateFunc(x=xval, param=workparam)
      perturbResid = np.sum((yval - perturbVal) ** 2 / yerr ** 2)
      # calc. change in chi square when varying a certain parameter
      self.derivatives[index] = (perturbResid - self.startResid)# / self.EPSILON #/ self.EPSILON #startResid

    # now also assign amplitudes for random parameter variation
    self.randomAmplitudes = np.array([1.0 / i if i != 0 else 0 for i in self.derivatives])
          
    # calc. relative amplitudes
    divisor = np.max(np.abs(self.randomAmplitudes))
    divisor = np.max((divisor, self.EPSILON))
    self.randomAmplitudes = self.randomAmplitudes / divisor / 2.0

  def doBruteFit(self):
    # check whether there is at least one floating parameter
    if(np.sum(self.param_active) == 0):
      self.parent.statusbar.showMessage('At least one parameter has to be free for fitting!', self.parent.STATUS_TIME)
    else:
      # get data from data object and start the fit procedure
      # check whether we have a data selection
      if(self.parent.selectedData.isLive):
        data, indices = self.parent.selectedData.value(), self.parent.selectedData.getIndices()
      else:
        data = self.parent.data[self.parent.activeData].value()
      if((not 'x' in data) or (len(data['x']) < np.sum(self.param_active))):
        try:
          nop = len(data['x'])
        except:
          nop = 0
        self.parent.statusbar.showMessage('No. of data points (' + str(nop) + ') should at least equal no. of free parameters (' + str(np.sum(self.param_active)) + ')!', self.parent.STATUS_TIME)
      elif(('x' in data) and ('y' in data) and (len(data['x']) > 0)):
        # display busy pointer
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
        # assign values
        xval = np.array(data['x']); yval = np.array(data['y'])
        
        # prepare yerr according to chosen error model
        if(self.errorSwitch):
          if(self.errorModel == 0):
            # use assigned deltaY
            if('yerr' in data):
              try:
                data['yerr'] = [i * self.errorMultiply for i in data['yerr']]
              except:
                pass
          elif(self.errorModel == 1):
            # use constant error
            try:
              data['yerr'] = [self.errorConst for i in data['x']]
            except:
              pass
          elif(self.errorModel == 2):
            # use proportional error
            if('y' in data):
              try:
                data['yerr'] = [i * self.errorPercent for i in data['y']]
              except:
                pass
        else:
          # remove errors if present
          if('yerr' in data):
            data.pop('yerr')

        # assign and check y error
        if('yerr' in data):
          # weed out zero values
          yerr = np.array([i if(i>0) else self.EPSILON for i in data['yerr']])
        else:
          yerr = np.ones(len(data['x']))
          
        # open an extra window to interact with procedure
        self.runFlag = True
        self.restartFlag = False
        self.daughterWindow = BruteWindow(self, 'Random Search')
        # apply styles to popup window
        if(QSTYLE != None):
          self.daughterWindow.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          self.daughterWindow.setStyleSheet(QSTYLESHEET)
        self.daughterWindow.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
        self.daughterWindow.show()
        
        # create list of active parameters
        self.param_active_list = [self.param[i] for i, x in enumerate(self.param_active) if (x == 1)]
        self.param_lower_list = [self.param_lower[i] for i, j in enumerate(self.param_active) if j]
        self.param_upper_list = [self.param_upper[i] for i, j in enumerate(self.param_active) if j]
        # ensure that we start within bounds
        self.param_active_list = [min(max(init, lower), upper) for init, lower, upper in \
                                  zip(self.param_active_list, self.param_lower_list, self.param_upper_list)]
        self.origParam = [i for i in self.param_active_list]
        
        # cycle until daughter window closed
        movingCursor = '|,/,-,\\'.split(',')
        cursorCount = 0
        repeatCycleLimit, escalate, lastSuccessLimit = self.advancedRandomSearch['cycles'],\
          self.advancedRandomSearch['escalate'], self.advancedRandomSearch['maxfev']
        escalate = [escalate ** i for i in range(repeatCycleLimit)]
        residstr = ''
        repeatCycleCount = 0
        improved = False
        while repeatCycleCount < repeatCycleLimit:
          # update message label
          self.daughterWindow.messageLabel.setText('Repeat cycle ' + str(repeatCycleCount + 1) + '/' + str(repeatCycleLimit))
          
          # parameter derivatives
          self.derivatives = np.zeros(len(self.param_active_list))
          self.getRelativeDerivatives(xval, yval, yerr)
          self.daughterWindow.messageLabel2.setText('resid ' + self.parent.formatNumber(self.startResid) + residstr)

          # need to still process events
          QtCore.QCoreApplication.processEvents()
      
          lastSuccessCount = 0
          while ((lastSuccessCount < lastSuccessLimit) and (self.runFlag) and (not self.restartFlag)):
            # randomly vary parameters
            for trial in range(100):
              if(not self.restartFlag):
                lastSuccessCount += 1
                # randomly vary parameters -- add epsilon to get rid of zero parameters
                workamplitude = [i if (np.abs(i) > self.MIN_AMPLITUDE) else (np.sign(i + 1.1 * self.EPSILON) * self.MIN_AMPLITUDE) for i in self.param_active_list]
                workamplitude = escalate[repeatCycleCount] * np.array(workamplitude)
                workparam = self.param_active_list  + (workamplitude * (0.33 - np.random.random(len(self.param_active_list))))
                # ensure that we stay within bounds and reflect if not
                for index, bounds in enumerate(zip(self.param_lower_list, self.param_upper_list)):
                  lower, upper = bounds
                  if(lower == upper):
                    workparam[index] = lower
                  elif(workparam[index] < lower):
                    if(np.isfinite(upper)):
                      workparam[index] = lower + ((lower - workparam[index]) % (upper - lower))
                    else:
                      workparam[index] = 2 * lower - workparam[index]
                  elif(workparam[index] > upper):
                    if(np.isfinite(lower)):
                      workparam[index] = upper - ((workparam[index] - upper) % (upper - lower))
                    else:
                      workparam[index] = 2 * upper - workparam[index]
                x, perturbVal = self.parent.fit[self.parent.activeFit].evaluateFunc(x=xval, param=workparam)
                perturbResid = np.sum((yval - perturbVal)**2 / yerr**2)
                
                if(perturbResid < self.startResid):
                  # improvement, yeah!
                  improved = True
                  # update params
                  self.param_active_list = [i for i in workparam]
                  self.updateBruteParam(False)
                  # calc. residuals
                  if(self.parent.selectedData.isLive):
                    self.parent.data[self.parent.activeData].setFval(perturbVal, indices=indices)
                  else:
                    self.parent.data[self.parent.activeData].setFval(perturbVal)
                  # prepare new round of random search
                  self.getRelativeDerivatives(xval, yval, yerr)
                  self.daughterWindow.messageLabel2.setText('resid ' + self.parent.formatNumber(self.startResid) + residstr)
                  lastSuccessCount = 0
                  self.daughterWindow.messageLabel3.setText('Func eval: ' + str(int(np.min((lastSuccessCount,\
                    lastSuccessLimit)))) + '/' + str(int(lastSuccessLimit)))
            
            # periodically update label in daughter window
            cursorCount += 1
            if(cursorCount >= len(movingCursor)):
              cursorCount = 0
            self.daughterWindow.messageLabel3.setText('Func eval: ' + str(int(np.min((lastSuccessCount,\
              lastSuccessLimit)))) + '/' + str(int(lastSuccessLimit)))
            self.daughterWindow.messageLabel4.setText('Working ' + movingCursor[cursorCount])
            # need to still process events
            QtCore.QCoreApplication.processEvents()

          if((residstr == '') or (self.startResid < self.bestResid)):
            # first cycle or improvement
            self.bestParam = [i for i in self.param_active_list]
            self.bestVal = 1.0 * self.startVal
            self.bestResid = 1.0 * self.startResid
            residstr = '; best ' + self.parent.formatNumber(self.bestResid)

          # reset original parameters for next cycle
          self.param_active_list = [i for i in self.origParam]
          
          # advance cycle counter
          if(self.restartFlag):
            self.restartFlag = False
          else:
            repeatCycleCount += 1
                      
        # finished all repeat cycles
        self.daughterWindow.close()
        self.daughterWindow = None
        
        # restore best results from previous cycle
        if(repeatCycleCount > 0):
          self.param_active_list = [i for i in self.bestParam]
          self.startVal = 1.0 * self.bestVal
        
        # calc. residuals even if no improvement
        # check whether we have a data selection
        if(self.parent.selectedData.isLive):
          x, calcVal = self.parent.fit[self.parent.activeFit].evaluateFunc(x=self.parent.data[self.parent.activeData].x, param=self.bestParam)
          self.parent.data[self.parent.activeData].setFval(calcVal)
        else:
          self.parent.data[self.parent.activeData].setFval(self.startVal)
        # reset confidences in fit object
        if(improved):
          self.confidence, self.confidence_apriori = self.parent.fit[self.parent.activeFit].clearConfidence()
        # after procedure, set new parameters and update curve
        self.updateBruteParam(True)

        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()

  def updateBruteParam(self, updateResid=False):
    # update fit parameters once found better resid
    counter = 0
    for index, entry in enumerate(self.param_active):
      if (entry == 1):
        self.param[index] = self.param_active_list[counter]
        counter += 1
    self.parent.fit[self.parent.activeFit].updateParam(self.param_active_list)
    # clear bootstrap data
    self.parent.fit[self.parent.activeFit].clearBoot()
    self.parent.fit[self.parent.activeFit].clearLastFitResults()
    # plot function
    self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
      fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
    self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
      fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
    # copy in case split axes are shown
    curve = self.parent.fit[self.parent.activeFit]
    if(self.parent.plotArea.splitY and curve.onBothAxes):
      curve.duplicateForSplit()
    self.parent.plotArea.dataplotwidget.myRefresh()
    # update param table
    self.changeParamTable()
    # also update residuals?
    if(updateResid):
      # generate resid style on the fly
      self.parent.data[self.parent.activeData].Residstyle = deepcopy(self.parent.data[self.parent.activeData].style)
      # copy style definitions for line from curve object used for fitting
      if(self.parent.data[self.parent.activeData].Residstyle['linestyle'] == 'None'):
        tempDict = {}
        for item in ['linewidth', 'linestyle', 'dash_capstyle', 'color']:
          if(item in self.parent.fit[self.parent.activeFit].style):
            tempDict[item] = self.parent.fit[self.parent.activeFit].style[item]
        self.parent.data[self.parent.activeData].Residstyle.update(tempDict)
      self.parent.data[self.parent.activeData].ResidBarstyle = deepcopy(self.parent.data[self.parent.activeData].Barstyle)
      self.parent.data[self.parent.activeData].ResidStackstyle = deepcopy(self.parent.data[self.parent.activeData].Stackstyle)
      self.parent.data[self.parent.activeData].ResidLinestyle = deepcopy(self.parent.fit[self.parent.activeFit].style)
      self.parent.data[self.parent.activeData].ResidScatterstyle = deepcopy(self.parent.data[self.parent.activeData].Scatterstyle)
      # ensure line is visible to connect dots
      if(self.parent.data[self.parent.activeData].Residstyle['linestyle'] == 'None'):
        self.parent.data[self.parent.activeData].Residstyle['linestyle'] = 'solid'
      # plot residuals
      # plot residuals on selected data object
      if(self.parent.selectedData.isLive):
        self.parent.selectedData.drawMeResid(self.parent.plotArea.ax_resid, self.parent.plotArea.ax_resid_div, redraw=False)
      self.parent.data[self.parent.activeData].handleResid, self.parent.plotArea.handleResidZero,\
        self.parent.data[self.parent.activeData].handleResidBar, self.parent.data[self.parent.activeData].handleResidStack,\
        self.parent.data[self.parent.activeData].handleResidStackNeg, self.parent.data[self.parent.activeData].handleResidText,\
        self.parent.data[self.parent.activeData].handleResidScatter = self.parent.plotArea.plotResid(\
        dataobject = self.parent.data[self.parent.activeData], handleResid = self.parent.data[self.parent.activeData].handleResid,\
        handleResidZero = self.parent.plotArea.handleResidZero, handleResidBar = self.parent.data[self.parent.activeData].handleResidBar,\
        handleResidStack = self.parent.data[self.parent.activeData].handleResidStack, handleResidStackNeg = self.parent.data[self.parent.activeData].handleResidStackNeg,\
        handleResidText = self.parent.data[self.parent.activeData].handleResidText, handleResidScatter=self.parent.data[self.parent.activeData].handleResidScatter, redraw=False)
      self.parent.data[self.parent.activeData].handleResid_div, self.parent.plotArea.handleResidZero_div,\
        self.parent.data[self.parent.activeData].handleResidBar_div, self.parent.data[self.parent.activeData].handleResidStack_div,\
        self.parent.data[self.parent.activeData].handleResidStackNeg_div, self.parent.data[self.parent.activeData].handleResidText_div,\
        self.parent.data[self.parent.activeData].handleResidScatter_div = self.parent.plotArea.plotResid(\
        dataobject = self.parent.data[self.parent.activeData], handleResid = self.parent.data[self.parent.activeData].handleResid_div,\
        handleResidZero = self.parent.plotArea.handleResidZero_div, handleResidBar = self.parent.data[self.parent.activeData].handleResidBar_div,\
        handleResidStack = self.parent.data[self.parent.activeData].handleResidStack_div, handleResidStackNeg = self.parent.data[self.parent.activeData].handleResidStackNeg_div,\
        handleResidText = self.parent.data[self.parent.activeData].handleResidText_div, handleResidScatter=self.parent.data[self.parent.activeData].handleResidScatter_div, splitX=True)
      
  def reportParam(self):
    # returns current parameters (ensure that a copy is returned here)
    return [i for i in self.paramList], [i for i in self.param_active], [i for i in self.param], [i for i in self.confidence],\
      [i for i in self.confidence_apriori], deepcopy(self.chisquare), deepcopy(self.red_chisquare, deepcopy(self.rsquare))

  def resetParam(self):
    # restores parameters to original (or, saved) values
    # first check whether we need to restore parameters at all
    flag = False
    for index, entry in enumerate(self.storeParam):
      if(entry != self.param[index]):
        flag = True
        
    if(flag):
      self.param = [i for i in self.storeParam]
      self.confidence = ['--'] * len(self.param)
      self.confidence_apriori = ['--'] * len(self.param)
      # deal w/ parameter boundaries
      self.param_lower = [min(i, j) for i, j in zip(self.param_lower, self.param)]
      self.param_upper = [max(i, j) for i, j in zip(self.param_upper, self.param)]
  
      # update parameter table
      self.updateParamTable()
      
      # also recompile the fit function for good measure and plot
      self.setFfunc(keepBoot=False)
  
      # plot function not needed as already done by setFfunc()
      #self.parent.fit[self.parent.activeFit].handlePlot = self.parent.plotArea.plotFunction(\
      #  fitobject = self.parent.fit[self.parent.activeFit], handlePlot = self.parent.fit[self.parent.activeFit].handlePlot)
  
  def doMin(self):
    # check whether there is at least one floating parameter
    if(np.sum(self.param_active) == 0):
      self.parent.statusbar.showMessage('At least one parameter has to be free for minimization!', self.parent.STATUS_TIME)
      return False
    else:
      # get data from data object and start the fit procedure
      # check whether we have a data selection
      if(self.parent.selectedData.isLive):
        data = self.parent.selectedData.value()
      else:
        data = self.parent.data[self.parent.activeData].value()
      # check number of fit parameters vs. data points
      if((not 'x' in data) or (len(data['x']) < np.sum(self.param_active))):
        try:
          nop = len(data['x'])
        except:
          nop = 0
        self.parent.statusbar.showMessage('No. of data points (' + str(nop) + ') should at least equal no. of free parameters (' + str(np.sum(self.param_active)) + ')!', self.parent.STATUS_TIME)
        return False
      elif (('x' in data) and ('y' in data)):
        # prepare fit
        xtol, maxfev = self.advancedMinimization['xtol'], self.advancedMinimization['maxfev']
        self.fev, self.currResid = 0, 0

        # prepare yerr according to chosen error model
        if(self.errorSwitch):
          if(self.errorModel == 0):
            # use assigned deltaY
            if('yerr' in data):
              try:
                data['yerr'] = [i * self.errorMultiply for i in data['yerr']]
              except:
                pass
          elif(self.errorModel == 1):
            # use constant error
            try:
              data['yerr'] = [self.errorConst for i in data['x']]
            except:
              pass
          elif(self.errorModel == 2):
            # use proportional error
            if('y' in data):
              try:
                data['yerr'] = [i * self.errorPercent for i in data['y']]
              except:
                pass
        else:
          # remove errors if present
          if('yerr' in data):
            data.pop('yerr')

        # assign and check y error
        if('yerr' in data):
          sigma = data['yerr']
          sigma = [i if(i > 0) else self.EPSILON for i in sigma]
        else:
          sigma = [1] * len(data['y'])
      
        # get active parameters
        self.param_active_list = [self.param[i] for i, x in enumerate(self.param_active) if (x == 1)]
        initpa = [i for i in self.param_active_list]
        self.fitpa = [i for i in initpa]
        self.daughterWindow = None
        self.runFlag = True
        
        try:
          # define callback and wrapper functions
          def minimizeCallback(args=0):
            self.fev += 1
            self.fitpa = args
            if((self.fev > maxfev) or (not self.runFlag)):
              # have to raise a warning to terminate
              raise ValueError('exceeded')
            # process events once in a while
            if(not ((self.fev - 1) % 100)):
              if(self.daughterWindow != None):
                self.daughterWindow.iterLabel.setText('iter ' + str(int(self.fev)) + ' / ' + str(int(maxfev)))
                self.daughterWindow.minLabel.setText('resid ' + self.formatNumber(self.currResid))
              QtCore.QCoreApplication.processEvents()
          
          def XX_minWrapper(param):
            # calculates square deviation b/w data and curve
            x, fval = self.parent.fit[self.parent.activeFit].evaluateFunc(data['x'], param)
            resid = (fval - data['y']) / sigma
            resid = resid ** 2
            self.currResid = np.sum(resid)
            return self.currResid

          # deal w/ boundaries
          usedAlgorithm = self.advancedMinimization['algorithm']
          boundaryLow = np.array([i for i, j in zip(self.param_lower, self.param_active) if j])
          boundaryHigh = np.array([i for i, j in zip(self.param_upper, self.param_active) if j])
          boundaryLowCheck = len(boundaryLow[boundaryLow != -np.inf])
          boundaryHighCheck = len(boundaryHigh[boundaryHigh != np.inf])
          boundaryCheck = boundaryLowCheck or boundaryHighCheck
          if(boundaryCheck):
            # adjust algorithm to use?
            if(usedAlgorithm in ['Powell', 'BFGS']):
              # algorithms that support bounds
              if(usedAlgorithm == 'BFGS'):
                usedAlgorithm = 'L-BFGS-B'
            else:
              self.parent.statusbar.showMessage('As at least some parameters were bounded, global fit was done by L-BFGS-B.', self.parent.STATUS_TIME, color='blue')
              usedAlgorithm = 'L-BFGS-B'
        
            # prepare boundaries
            boundsLow = [None if(i == -np.inf) else i for i in boundaryLow]
            boundsHigh = [None if(i == np.inf) else i for i in boundaryHigh]
            bounds = [(i, j) for i, j in zip(boundsLow, boundsHigh)]

          # display busy pointer
          QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
          # open an extra window to interact with procedure
          self.daughterWindow = MinWindow(self, usedAlgorithm + ' Minimization')
          # apply styles to popup window
          if(QSTYLE != None):
            self.daughterWindow.setStyle(QSTYLE)
          if(QSTYLESHEET != None):
            self.daughterWindow.setStyleSheet(QSTYLESHEET)
          self.daughterWindow.show()

          # now do the minimization
          options = {'disp': False}
          if(usedAlgorithm in ['Nelder-Mead', 'Powell']):
            options.update({'xtol': xtol})
          if(boundaryCheck):
            res = optim.minimize(XX_minWrapper, initpa, method=usedAlgorithm, options=options, callback=minimizeCallback, bounds=bounds)
          else:
            res = optim.minimize(XX_minWrapper, initpa, method=usedAlgorithm, options=options, callback=minimizeCallback)
          
          # restore pointer
          QtWidgets.QApplication.restoreOverrideCursor()
          success = True
        except ValueError as err:
          # restore pointer
          QtWidgets.QApplication.restoreOverrideCursor()
          if((len(err.args)) and (err.args[0] == 'exceeded')):
            success = True
          else:
            if(self.daughterWindow != None):
              self.daughterWindow.close()
              self.daughterWindow = None
            success = False
            return False
        except:
          # restore pointer
          QtWidgets.QApplication.restoreOverrideCursor()
          self.parent.statusbar.showMessage('Encountered some problem when searching minimum!', self.parent.STATUS_TIME)
          if(self.daughterWindow != None):
            self.daughterWindow.close()
            self.daughterWindow = None
          success = False
          return False
        
        if(success):
          # finish, close window if still visible
          if(self.daughterWindow != None):
            self.daughterWindow.close()
            self.daughterWindow = None
          # now update plot and parameter table
          self.param_active_list = [i for i in self.fitpa]
          x, numVal = self.parent.fit[self.parent.activeFit].evaluateFunc(x=self.parent.data[self.parent.activeData].x, param=self.fitpa)
          # calc. residuals even if no improvement
          self.parent.data[self.parent.activeData].setFval(numVal)
          # reset confidences in fit object
          self.confidence, self.confidence_apriori = self.parent.fit[self.parent.activeFit].clearConfidence()
          # after procedure, set new parameters and update curve
          self.updateBruteParam(True)
          # success
          return True
      else:
        self.parent.statusbar.showMessage('No y values found!', self.parent.STATUS_TIME)
        return False

  def doFit(self):
    # check whether there is at least one floating parameter
    if(np.sum(self.param_active) == 0):
      self.parent.statusbar.showMessage('At least one parameter has to be free for fitting!', self.parent.STATUS_TIME)
      return False
    else:
      # get data from data object and start the fit procedure
      # check whether we have a data selection
      if(self.parent.selectedData.isLive):
        data = self.parent.selectedData.value()
        # have to also retrieve and process further down the full dataset
        # that is b/c we may change y errors and hence cannot resort to dataset anymore to later retrieve them
        dataFull = self.parent.data[self.parent.activeData].value()
      else:
        data = self.parent.data[self.parent.activeData].value()
      # check number of fit parameters vs. data points
      if((not 'x' in data) or (len(data['x']) < np.sum(self.param_active))):
        try:
          nop = len(data['x'])
        except:
          nop = 0
        self.parent.statusbar.showMessage('No. of data points (' + str(nop) + ') should at least equal no. of free parameters (' + str(np.sum(self.param_active)) + ')!', self.parent.STATUS_TIME)
        return False
      else:
        # prepare yerr according to chosen error model
        if(self.errorSwitch):
          if(self.errorModel == 0):
            # use assigned deltaY
            if('yerr' in data):
              try:
                data['yerr'] = [i * self.errorMultiply for i in data['yerr']]
                if(self.parent.selectedData.isLive):
                  dataFull['yerr'] = [i * self.errorMultiply for i in dataFull['yerr']]
              except:
                pass
          elif(self.errorModel == 1):
            # use constant error
            try:
              data['yerr'] = [self.errorConst for i in data['x']]
              if(self.parent.selectedData.isLive):
                dataFull['yerr'] = [self.errorConst for i in dataFull['x']]
            except:
              pass
          elif(self.errorModel == 2):
            # use proportional error
            if('y' in data):
              try:
                data['yerr'] = [i * self.errorPercent for i in data['y']]
                if(self.parent.selectedData.isLive):
                  dataFull['yerr'] = [i * self.errorPercent for i in dataFull['y']]
              except:
                pass
        else:
          # remove errors if present
          if('yerr' in data):
            data.pop('yerr')
            
        # display busy pointer
        QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
        boundaryLow = [i for i, j in zip(self.param_lower, self.param_active) if j]
        boundaryHigh = [i for i, j in zip(self.param_upper, self.param_active) if j]
        success, fitpa, covar, usedAlgorithm = self.parent.fit[self.parent.activeFit].fitFunc(data, boundaryLow=boundaryLow, boundaryHigh=boundaryHigh)
        if (success):
          # evaluate fitted function at x values and store in data object
          x, fval = self.parent.fit[self.parent.activeFit].simulateFunc(x = self.parent.data[self.parent.activeData].x)
          self.parent.data[self.parent.activeData].setFval(fval)

          # calculate confidences
          normcovar = deepcopy(covar)
          try:
            confidence = np.power(covar.diagonal(), 0.5)
            nanList = confidence[np.isnan(confidence)]
            if(nanList.size):
              normcovar = np.ones((len(confidence), len(confidence)))
            else:
              try:
                for index, value in enumerate(confidence):
                  if(value != 0):
                    normcovar[:, index] /= value
                    normcovar[index, :] /= value
              except:
                normcovar = np.ones((len(confidence), len(confidence)))
          except:
            # takes care of NaN and similar errors
            confidence = ['--'] * len(fitpa)
            normcovar = np.ones((len(confidence), len(confidence)))
            
          # check for 'nan' in confidence
          if(type(confidence) == type(np.array([]))):
            nanCheck = np.isnan(confidence)
            nanList = confidence[nanCheck]
            if(nanList.size):
              confidence = ['--'] * len(fitpa)
          
          # check for 'nan' in normcovar
          if(type(normcovar) == type(np.array([]))):
            nanCheck = np.isnan(normcovar)
            nanList = normcovar[nanCheck]
            if(nanList.size):
              normcovar = np.ones((len(confidence), len(confidence)))
          
          # calculate chi_square and reduced chi_square
          if('yerr' in data):
            ###yerr = self.parent.data[self.parent.activeData].yerr
            ###temp_data = self.parent.data[self.parent.activeData].value()
            if(self.parent.selectedData.isLive):
              yerr = np.array(dataFull['yerr'])
            else:
              yerr = np.array(data['yerr'])
            # weed out zero sigma entries
            zerosigma = yerr[yerr <= 0]
            if(len(zerosigma) > 0):
              self.parent.statusbar.showMessage('Encountered zero/negative sigma values => set to ' + str(self.EPSILON), self.parent.STATUS_TIME, color='blue')
              yerr = np.array([i if(i > 0) else self.EPSILON for i in yerr])
          else:
            yerr = np.array([1.0 for i in self.parent.data[self.parent.activeData].x])
          self.chisquare = self.parent.data[self.parent.activeData].resid ** 2 / yerr ** 2
          self.chisquare = np.sum(self.chisquare)
          nop = np.sum(self.param_active)
          dof = len(self.parent.data[self.parent.activeData].x) - nop
          if(dof > 0):
            self.red_chisquare = self.chisquare / dof
          else:
            self.red_chisquare = 'inf'
          # and now calculate a priori errors
          if((self.red_chisquare != 'inf') and ('yerr' in data)):
            covar_apriori = covar / self.red_chisquare
            confidence_apriori = np.power(covar_apriori.diagonal(), 0.5)
          else:
            confidence_apriori = ['--'] * len(fitpa)
            
          # provide R-square statistics as well
          ymean = np.average(self.parent.data[self.parent.activeData].y)
          sstot = np.sum((self.parent.data[self.parent.activeData].y - ymean) ** 2)
          ssfit = np.sum(self.parent.data[self.parent.activeData].resid ** 2)
          try:
            self.rsquare = 1 - ssfit / sstot
          except:
            self.parent.statusbar.showMessage('Encountered some error when calculating r-square value!', self.parent.STATUS_TIME, color='blue')
            self.rsquare = 0
          ###print(ymean, sstot, ssfit, rsquare)
            
          # we should determine whether fit function is linear or not
          # better escape this, lest unforeseen fun happens
          isLinear = False
          try:
            deltaX, deltaY = x[-1] - x[0], fval[-1] - fval[0]
            slope = deltaY / deltaX
            checko = np.sum(((fval[0] + (x - x[0]) * slope) - fval) ** 2)
            isLinear = np.isclose(0, checko)
          except:
            pass
          
          # also evaluate fitted function at selected x values
          if(self.parent.selectedData.isLive):
            x_sel, fval_sel = self.parent.fit[self.parent.activeFit].simulateFunc(x = self.parent.selectedData.x)
            self.parent.selectedData.setFval(fval_sel)

            if('yerr' in data):
              ###yerr_sel = self.parent.selectedData.yerr
              ###temp_data = self.parent.selectedData.value()
              yerr_sel = np.array(data['yerr'])
              # weed out zero sigma entries
              zerosigma_sel = yerr_sel[yerr_sel <= 0]
              if(len(zerosigma_sel) > 0):
                self.parent.statusbar.showMessage('Encountered zero/negative sigma values => set to ' + str(self.EPSILON), self.parent.STATUS_TIME, color='blue')
                yerr_sel = np.array([i if(i > 0) else self.EPSILON for i in yerr_sel])
            else:
              yerr_sel = np.array([1.0 for i in self.parent.selectedData.x])
            chisquare_sel = self.parent.selectedData.resid ** 2 / yerr_sel ** 2
            chisquare_sel = np.sum(chisquare_sel)
            dof_sel = len(self.parent.selectedData.x) - nop
            if(dof_sel > 0):
              red_chisquare_sel = chisquare_sel / dof_sel
            else:
              red_chisquare_sel = 'inf'
            # have to recalculate a priori errors under these circumstances
            if((red_chisquare_sel != 'inf') and ('yerr' in data)):
              covar_apriori = covar / red_chisquare_sel
              confidence_apriori = np.power(covar_apriori.diagonal(), 0.5)
            else:
              confidence_apriori = ['--'] * len(fitpa)

          # did we use x errors or boundaries?
          if(usedAlgorithm == 'odr'):
            self.parent.statusbar.showMessage('As data contained x errors, fit was done by orthogonal distance regression (odr).', self.parent.STATUS_TIME, color='blue')
          elif(usedAlgorithm == 'trf'):
            self.parent.statusbar.showMessage('As at least some parameters were bounded, fit was done by trust region reflective (trf).', self.parent.STATUS_TIME, color='blue')
          
          # update parameters in list and table
          counter = 0
          for index, entry in enumerate(self.param_active):
            # in rare circumstances, testing for entry only threw an error, which however is hard to reproduce
            # let alone fix. Therefore, put in additional check that should take care of it
            if(entry and (counter < len(fitpa))):
              self.param[index] = fitpa[counter]
              self.confidence[index] = confidence[counter]
              self.confidence_apriori[index] = confidence_apriori[counter]
              counter += 1
              
          self.changeParamTable()
          
          # clear bootstrap data
          self.parent.fit[self.parent.activeFit].clearBoot()
          self.parent.fit[self.parent.activeFit].clearLastFitResults()
          
          # in case of 'odr', check whether we had boundaries that would need to be expanded to new value
          if(usedAlgorithm == 'odr'):
            self.changeParamTableBoundaries()
          
          # generate resid style on the fly
          self.parent.data[self.parent.activeData].Residstyle.update(self.parent.data[self.parent.activeData].style)
          # copy style definitions for line from curve object used for fitting
          if(self.parent.data[self.parent.activeData].Residstyle['linestyle'] == 'None'):
            tempDict = {}
            for item in ['linewidth', 'linestyle', 'dash_capstyle', 'color']:
              if(item in self.parent.fit[self.parent.activeFit].style):
                tempDict[item] = self.parent.fit[self.parent.activeFit].style[item]
            self.parent.data[self.parent.activeData].Residstyle.update(tempDict)
          self.parent.data[self.parent.activeData].ResidBarstyle.update(self.parent.data[self.parent.activeData].Barstyle)
          self.parent.data[self.parent.activeData].ResidStackstyle.update(self.parent.data[self.parent.activeData].Stackstyle)
          self.parent.data[self.parent.activeData].ResidTextstyle.update(self.parent.data[self.parent.activeData].Textstyle)
          self.parent.data[self.parent.activeData].ResidScatterstyle.update(self.parent.data[self.parent.activeData].Scatterstyle)
          for item in ['linewidth', 'linestyle', 'color']:
            self.parent.data[self.parent.activeData].ResidLinestyle[item] = deepcopy(self.parent.fit[self.parent.activeFit].style[item])
          # ensure line is visible to connect dots
          if(self.parent.data[self.parent.activeData].Residstyle['linestyle'] == 'None'):
            self.parent.data[self.parent.activeData].Residstyle['linestyle'] = 'solid'
          # plot function
          self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
            fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
          self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
            fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
          # copy in case split axes are shown
          curve = self.parent.fit[self.parent.activeFit]
          if(self.parent.plotArea.splitY and curve.onBothAxes):
            curve.duplicateForSplit()
          self.parent.plotArea.dataplotwidget.myRefresh()
          # plot residuals
          # plot residuals on selected data object
          if(self.parent.selectedData.isLive):
            self.parent.selectedData.drawMeResid(self.parent.plotArea.ax_resid, self.parent.plotArea.ax_resid_div, redraw=False)
          self.parent.data[self.parent.activeData].handleResid, self.parent.plotArea.handleResidZero,\
            self.parent.data[self.parent.activeData].handleResidBar, self.parent.data[self.parent.activeData].handleResidStack,\
            self.parent.data[self.parent.activeData].handleResidStackNeg, self.parent.data[self.parent.activeData].handleResidText,\
            self.parent.data[self.parent.activeData].handleResidScatter = self.parent.plotArea.plotResid(\
            dataobject = self.parent.data[self.parent.activeData], handleResid = self.parent.data[self.parent.activeData].handleResid,\
            handleResidZero = self.parent.plotArea.handleResidZero, handleResidBar = self.parent.data[self.parent.activeData].handleResidBar,\
            handleResidStack = self.parent.data[self.parent.activeData].handleResidStack, handleResidStackNeg = self.parent.data[self.parent.activeData].handleResidStackNeg,\
            handleResidText = self.parent.data[self.parent.activeData].handleResidText, handleResidScatter=self.parent.data[self.parent.activeData].handleResidScatter, redraw=False)
          self.parent.data[self.parent.activeData].handleResid_div, self.parent.plotArea.handleResidZero_div,\
            self.parent.data[self.parent.activeData].handleResidBar_div, self.parent.data[self.parent.activeData].handleResidStack_div,\
            self.parent.data[self.parent.activeData].handleResidStackNeg_div, self.parent.data[self.parent.activeData].handleResidText_div,\
            self.parent.data[self.parent.activeData].handleResidScatter_div = self.parent.plotArea.plotResid(\
            dataobject = self.parent.data[self.parent.activeData], handleResid = self.parent.data[self.parent.activeData].handleResid_div,\
            handleResidZero = self.parent.plotArea.handleResidZero_div, handleResidBar = self.parent.data[self.parent.activeData].handleResidBar_div,\
            handleResidStack = self.parent.data[self.parent.activeData].handleResidStack_div, handleResidStackNeg = self.parent.data[self.parent.activeData].handleResidStackNeg_div,\
            handleResidText = self.parent.data[self.parent.activeData].handleResidText_div, handleResidScatter=self.parent.data[self.parent.activeData].handleResidScatter_div, splitX=True)
          # and we should update the results table
          self.parent.resultsarea.setDataSet(currIndex=self.parent.activeData)

          # and we should update the fit information
          freeparameters = []; fixedparameters = []
          for index, entry in enumerate(self.param_active):
            if(entry):
              if(self.confidence_apriori[index] != '--'):
                freeparameters.append(self.paramList[index] + ' = ' + self.parent.formatNumber(self.param[index])\
                  + ' &plusmn; ' + self.parent.formatNumber(self.confidence[index]) + ' (' + self.parent.formatNumber(self.confidence_apriori[index]) + ')')
              else:
                freeparameters.append(self.paramList[index] + ' = ' + self.parent.formatNumber(self.param[index]) + ' &plusmn; ' + self.parent.formatNumber(self.confidence[index]))
            else:
              fixedparameters.append(self.paramList[index] + ' = ' + self.parent.formatNumber(self.param[index]))
          freestring = '  ' + '\n  '.join(freeparameters)
          fixedstring = '  ' + '\n  '.join(fixedparameters)
          self.outstring = self.fitResultsHeader
          self.outstring += 'algorithm: ' + usedAlgorithm + '\n'
          if(self.parent.selectedData.isLive):
            self.outstring += '\n<u>selected data</u>\ndegrees of freedom: ' + str(dof_sel) + '\n'
            self.outstring += u'<i>\N{GREEK CAPITAL LETTER CHI}</i><sup>2</sup> ' + self.parent.formatNumber(chisquare_sel) + '\n'
            self.outstring += u'red. <i>\N{GREEK CAPITAL LETTER CHI}</i><sup>2</sup> ' + self.parent.formatNumber(red_chisquare_sel) + '\n\n'
            self.outstring += '<u>all data</u>\n'
          self.outstring += 'degrees of freedom: ' + str(dof) + '\n'
          self.outstring += u'<i>\N{GREEK CAPITAL LETTER CHI}</i><sup>2</sup> ' + self.parent.formatNumber(self.chisquare) + '\n'
          self.outstring += u'red. <i>\N{GREEK CAPITAL LETTER CHI}</i><sup>2</sup> ' + self.parent.formatNumber(self.red_chisquare) + '\n'
          if(not isLinear):
            self.outstring += u'pseudo-'
          self.outstring += u'<i>R</i><sup>2</sup> ' + self.parent.formatNumber(self.rsquare) + '\n\n'

          currFunc = self.currentFunction
          self.outstring += '<u>function: ' + currFunc + '</u>\n<span style="white-space: pre;">'
          self.outstring += '  ' + '\n  '.join(str(self.fitFormula.toPlainText()).splitlines()) + '\n\n</span>\n\n'
          self.outstring += '<u>free parameters</u>\n<span style="white-space: pre;">' + freestring + '\n\n</span>\n'
          if(usedAlgorithm == 'trf'):
            self.outstring += '<u>parameter bounds</u>\n<span style="white-space: pre;">'
            for index, entry in enumerate(self.param_active):
              if(entry and ((self.param_lower[index] != -np.inf) or (self.param_upper[index] != np.inf))):
                self.outstring += '  ' + self.parent.formatNumber(self.param_lower[index]) + ' &le; ' + self.parent.formatNumber(self.paramList[index])
                self.outstring += ' &le; ' + self.parent.formatNumber(self.param_upper[index]) + '\n'
            self.outstring += '\n</span>\n'
          if(len(fixedparameters)):
            self.outstring += '<u>fixed parameters</u>\n<span style="white-space: pre;">' + fixedstring + '</span>\n'
          self.outstring = self.outstring.replace('\n', '<br/>')
          self.fitResults.setText(self.outstring)
          self.bootString = ''
          self.parent.fit[self.parent.activeFit].fitresults = self.outstring
          
          # remember that last fit was local
          self.parent.lastFitType = 'local'
          self.parent.lastFitGlobalDataSets = []
          self.parent.lastFitGlobalCurves = []
          
          # remember certain fit parameters to enable calculation of confidence bands
          if(self.parent.selectedData.isLive):
            lastFitResults = {'dof': dof, 'stderr': np.sqrt(np.sum(self.parent.selectedData.resid ** 2) / dof),
                              'xdata': [i for i in self.parent.selectedData.x]}
          else:
            lastFitResults = {'dof': dof, 'stderr': np.sqrt(np.sum(self.parent.data[self.parent.activeData].resid ** 2) / dof),
                              'xdata': [i for i in self.parent.data[self.parent.activeData].x]}

          # update fit information in object
          ffuncstr_base, ffunc_header = self.parent.fit[self.parent.activeFit].ffuncstr_base, self.parent.fit[self.parent.activeFit].ffunc_header
          self.parent.fit[self.parent.activeFit].storeInfo(self.paramList, self.param, self.param_active,\
                         self.confidence, self.confidence_apriori, ffuncstr_base, ffunc_header, self.outstring, self.chisquare, self.red_chisquare, self.rsquare, lastFitResults)

          # remember covariances and parameter names
          self.covar = normcovar
          self.covarParameters = [i for index, i in enumerate(self.paramList) if self.param_active[index]]
          self.covarButton.show()
          self.bootButton.show()
          self.confidenceButton.show()

        # restore pointer
        QtWidgets.QApplication.restoreOverrideCursor()
        # used for handling keyboard shortcuts
        return True
      
  def changeParamTable(self):
    # fills in values into the parameter table
    for index, entry in enumerate(self.paramList):
      self.ParamTable.cellWidget(index, 1).setText(self.parent.formatNumber(self.param[index]))
      self.ParamTable.cellWidget(index, 2).setText(self.parent.formatNumber(self.confidence[index]))
      self.ParamTable.cellWidget(index, 3).setText(self.parent.formatNumber(self.confidence_apriori[index]))

  def useFit(self, redraw=True, keepBoot=False):
    # update parameter table
    self.updateParamTable()
    # set fit function
    self.setFfunc(redraw=redraw, keepBoot=keepBoot)
    # update fit information on global tab as well
    if(hasattr(self.parent, 'globalarea')):
      self.parent.globalarea.updateDataSetTable()

  def setFfunc(self, redraw=True, keepBoot=False):
    # dynamically assign fit function
    # function body
    ffunc_orig = str(self.fitFormula.toPlainText())
    ffunc = '\t' + '\n\t'.join(ffunc_orig.split('\n'))
    
    ffunc_top = ''; ffunc_header = ''; fitpa = []
    # cycle over all parameters
    for index, entry in enumerate(self.paramList):
      if(self.param_active[index]):
        fitpa.append(float(self.param[index]))
        ffunc_header += ', ' + entry
      else:
        ffunc_top += '\t' + entry + ' = ' + str(float(self.param[index])) + '\n'
    
    ffunc = ffunc_top + ffunc
    
    # update the function in the fit object
    success, message = self.parent.fit[self.parent.activeFit].updateFunc(ffunc, ffunc_header, testParam=fitpa)
    if (success):
      # update parameters
      self.parent.fit[self.parent.activeFit].updateParam(fitpa)
      self.parent.fit[self.parent.activeFit].updateChiSquare(self.chisquare, self.red_chisquare)
      # store this information in current fit function
      if(hasattr(self, 'fitResults')):
        fitresults = self.fitResults.toPlainText()
      else:
        fitresults = ''
      self.parent.fit[self.parent.activeFit].storeInfo(self.paramList, self.param, self.param_active,\
        self.confidence, self.confidence_apriori, ffunc_orig, ffunc_header, fitresults, self.chisquare, self.red_chisquare, self.rsquare, {})
      # plot function
      self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
        fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot,\
        handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
      self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
        fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div,\
        handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
      # copy in case split axes are shown
      curve = self.parent.fit[self.parent.activeFit]
      if(self.parent.plotArea.splitY and curve.onBothAxes):
        curve.duplicateForSplit()
      # delete bootstrap information
      if(not keepBoot):
        curve.clearBoot()
        curve.clearLastFitResults()
      # issue redraw?
      if(redraw):
        self.parent.plotArea.dataplotwidget.myRefresh()
      # don't use palettes as these are incompatible with style sheets
      ###self.fitFormula.styleBase = 'color:rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_ALTERNATE_BASE_STRING + '; '
      self.fitFormula.styleBase = 'color:rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_BASE_STRING + '; '
      self.fitFormula.setStyleSheet('myQTextEditBase {' + self.fitFormula.styleBase + 'font-size:' + str(self.fitFormula.fontSize) + 'pt;}')
    else:
      # some kind of failure
      if(hasattr(self.parent, 'statusbar')):
        self.parent.statusbar.showMessage(message, self.parent.STATUS_TIME)
        print('Error in fit function:\n' + message)
        # don't use palettes as these are incompatible with style sheets
        if(DARK_MODE):
          self.fitFormula.styleBase = 'color:rgba' + UI_TEXT_STRING + '; background-color: rgb(220, 70, 70); '
        else:
          self.fitFormula.styleBase = 'color:rgba' + UI_TEXT_STRING + '; background-color: rgb(250, 190, 190); '
        self.fitFormula.setStyleSheet('myQTextEditBase {' + self.fitFormula.styleBase + 'font-size:' + str(self.fitFormula.fontSize) + 'pt;}')
   
  def updateParamTable(self):
    # store and organize current parameters as we may want to reuse them
    param_old, param_active_old, param_lower_old, param_upper_old = {}, {}, {}, {}
    for index, key in enumerate(self.paramList):
      if(index < len(self.param)):
        param_old[key] = self.param[index]
      if(index < len(self.param_active)):
        param_active_old[key] = self.param_active[index]
      if(index < len(self.param_lower)):
        param_lower_old[key] = self.param_lower[index]
      if(index < len(self.param_upper)):
        param_upper_old[key] = self.param_upper[index]
      
    # check which params we have
    self.paramList = str(self.declareParamEntry.text()).split(',')
    self.paramList = [i.strip() for i in self.paramList]
    
    # initialize newly added parameters to 1
    while(len(self.param) < len(self.paramList)):
      self.param.append(1.0)
    while(len(self.confidence) < len(self.paramList)):
      self.confidence.append('--')
    while(len(self.confidence_apriori) < len(self.paramList)):
      self.confidence_apriori.append('--')
    while(len(self.param_active) < len(self.paramList)):
      self.param_active.append(1)
    while(len(self.param_lower) < len(self.paramList)):
      self.param_lower.append(-np.inf)
    while(len(self.param_upper) < len(self.paramList)):
      self.param_upper.append(np.inf)
    
    # check whether any parameters were retained from before
    for index, param in enumerate(self.paramList):
      if(param in param_old):
        self.param[index] = param_old[param]
      if(param in param_active_old):
        self.param_active[index] = param_active_old[param]
      if(param in param_lower_old):
        self.param_lower[index] = param_lower_old[param]
      if(param in param_upper_old):
        self.param_upper[index] = param_upper_old[param]
      
    # truncate self.param if parameters have been deleted
    if(len(self.param) > len(self.paramList)):
      self.param = self.param[:len(self.paramList)]
      self.confidence = self.confidence[:len(self.paramList)]
      self.confidence_apriori = self.confidence_apriori[:len(self.paramList)]
      self.param_active = self.param_active[:len(self.paramList)]
      self.param_lower = self.param_lower[:len(self.paramList)]
      self.param_upper = self.param_upper[:len(self.paramList)]

    # prepare table
    self.ParamTable.setRowCount(len(self.paramList))
    
    # set row height and fix
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.ParamTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    vheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    hheader = self.ParamTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    
    # initialize boundary fields
    self.boundaryLowCheck, self.boundaryLowEntry, self.boundaryHighCheck, self.boundaryHighEntry = [], [], [], []
    
    # set up new param entries
    for index, entry in enumerate(self.paramList):
      self.ParamTable.setVerticalHeaderItem(index, QtWidgets.QTableWidgetItem(entry))

      qchkbox_item = QPushButtonCheckable()
      if(self.param_active[index]):
        qchkbox_item.setChecked(True)
      else:
        qchkbox_item.setChecked(False)
      qchkbox_item.setPadMe(2)
      qchkbox_item.setText('')
      qchkbox_item.setToolTip('Vary parameter ' + str(index + 1) + ' during fit/minimization')
      qchkbox_item.setCheckMe(True)
      qchkbox_item.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      qchkbox_item.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      qchkbox_item.clicked.connect(partial(self.clickParam, index))
      self.ParamTable.setCellWidget(index, 0, qchkbox_item)

      qline_item = QLineEditClick(self.parent.formatNumber(self.param[index]))
      qline_item.setToolTip('Value of parameter ' + str(index + 1))
      qline_item.setValidator(self.validFloat)
      qline_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      qline_item.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qline_item.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qline_item.editingFinished.connect(partial(self.editParam, index))
      self.ParamTable.setCellWidget(index, 1, qline_item)

      qlabel_item = QLabelToolTip(self.parent.formatNumber(self.confidence[index]))
      qlabel_item.setToolTip('A posteriori error of parameter ' + str(index + 1))
      qlabel_item.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
      self.ParamTable.setCellWidget(index, 2, qlabel_item)
      
      qlabel_item = QLabelToolTip(self.parent.formatNumber(self.confidence_apriori[index]))
      qlabel_item.setToolTip('A priori error of parameter ' + str(index + 1))
      qlabel_item.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      qlabel_item.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignVCenter)
      self.ParamTable.setCellWidget(index, 3, qlabel_item)
      
      # set up boundary area
      qitem = QtWidgets.QWidget()
      qitem.setMinimumWidth(int(132 * SCALEFONT))
      qitem.setMaximumWidth(int(132 * SCALEFONT))
      self.ParamTable.setCellWidget(index, 4, qitem)
      qitem_Layout = QtWidgets.QGridLayout(qitem)
      qitem_Layout.setContentsMargins(0, 0, 0, 0)
      qitem_Layout.setSpacing(1)
      
      self.boundaryLowCheck.append(QPushButtonCheckable())
      self.boundaryLowCheck[-1].setText('lower')
      self.boundaryLowCheck[-1].setToolTip('Activate lower fit boundary for parameter ' + str(index + 1))
      self.boundaryLowCheck[-1].setChecked(bool(self.param_lower[index] != -np.inf))
      self.boundaryLowCheck[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowCheck[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowCheck[-1].clicked.connect(partial(self.clickBoundary, index, True))
      qitem_Layout.addWidget(self.boundaryLowCheck[-1], 0, 0, 1, 1)
      
      self.boundaryLowEntry.append(QLineEditClick())
      self.boundaryLowEntry[-1].setToolTip('Lower fit boundary for parameter ' + str(index + 1))
      self.boundaryLowEntry[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowEntry[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryLowEntry[-1].editingFinished.connect(partial(self.editBoundary, index, True))
      self.boundaryLowEntry[-1].setValidator(self.validFloat)
      qitem_Layout.addWidget(self.boundaryLowEntry[-1], 1, 0, 1, 1)
      if(self.boundaryLowCheck[-1].isChecked()):
        self.boundaryLowEntry[-1].setText(self.parent.formatNumber(self.param_lower[index]))
        # explicitly set row height as PyQt layout does not update correctly
        self.ParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.boundaryLowEntry[-1].hide()
      
      self.boundaryHighCheck.append(QPushButtonCheckable())
      self.boundaryHighCheck[-1].setText('upper')
      self.boundaryHighCheck[-1].setToolTip('Activate upper fit boundary for parameter ' + str(index + 1))
      self.boundaryHighCheck[-1].setChecked(bool(self.param_upper[index] != np.inf))
      self.boundaryHighCheck[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighCheck[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighCheck[-1].clicked.connect(partial(self.clickBoundary, index, False))
      qitem_Layout.addWidget(self.boundaryHighCheck[-1], 0, 1, 1, 1)
      
      self.boundaryHighEntry.append(QLineEditClick())
      self.boundaryHighEntry[-1].setToolTip('Upper fit boundary for parameter ' + str(index + 1))
      self.boundaryHighEntry[-1].setMaximumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighEntry[-1].setMinimumSize(int(66 * SCALEFONT), int(BASE_SIZE))
      self.boundaryHighEntry[-1].editingFinished.connect(partial(self.editBoundary, index, False))
      self.boundaryHighEntry[-1].setValidator(self.validFloat)
      qitem_Layout.addWidget(self.boundaryHighEntry[-1], 1, 1, 1, 1)
      if(self.boundaryHighCheck[-1].isChecked()):
        self.boundaryHighEntry[-1].setText(self.parent.formatNumber(self.param_upper[index]))
        # explicitly set row height as PyQt layout does not update correctly
        self.ParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.boundaryHighEntry[-1].hide()
      
    # set automatic column size
    self.ParamTable.resizeColumnsToContents()

  def clickBoundary(self, index, lower=True):
    # toggle lower/upper boundary fields
    if(lower):
      value = self.boundaryLowCheck[index].isChecked()
      if(value):
        if(self.param_lower[index] == -np.inf):
          self.param_lower[index] = min(0, 10 * self.param[index])
        else:
          self.param_lower[index] = min(self.param_lower[index], 0, 10 * self.param[index])
        self.boundaryLowEntry[index].show()
        self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.param_lower[index]))
        self.ParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.param_lower[index] = -np.inf
        self.boundaryLowEntry[index].setText('')
        self.boundaryLowEntry[index].hide()
        if(self.boundaryHighCheck[index].isChecked()):
          self.ParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
        else:
          self.ParamTable.setRowHeight(index, self.rowHeight)
    else:
      value = self.boundaryHighCheck[index].isChecked()
      if(value):
        if(self.param_upper[index] == np.inf):
          self.param_upper[index] = max(0, 10 * self.param[index])
        else:
          self.param_upper[index] = max(self.param_upper[index], 0, 10 * self.param[index])
        self.boundaryHighEntry[index].show()
        self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.param_upper[index]))
        self.ParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
      else:
        self.param_upper[index] = np.inf
        self.boundaryHighEntry[index].setText('')
        self.boundaryHighEntry[index].hide()
        if(self.boundaryLowCheck[index].isChecked()):
          self.ParamTable.setRowHeight(index, 2 * int(BASE_SIZE) + 1)
        else:
          self.ParamTable.setRowHeight(index, self.rowHeight)
    
  def editBoundary(self, index, lower=True):
    # edit boundary value
    if(lower):
      try:
        self.param_lower[index] = float(self.boundaryLowEntry[index].text())
      except:
        self.param_lower[index] = 0
    else:
      try:
        self.param_upper[index] = float(self.boundaryHighEntry[index].text())
      except:
        self.param_upper[index] = 0

    # check other boundary and parameter value
    flag = False
    if(self.param_lower[index] > self.param_upper[index]):
      self.param_lower[index], self.param_upper[index] = self.param_upper[index], self.param_lower[index]
      self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.param_lower[index]))
      self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.param_upper[index]))
    if(self.param[index] < self.param_lower[index]):
      self.param[index] = self.param_lower[index]
      flag = True
    if(self.param[index] > self.param_upper[index]):
      self.param[index] = self.param_upper[index]
      flag = True
    if(flag):
      self.ParamTable.cellWidget(index, 1).setText(self.parent.formatNumber(self.param[index]))
      # set fit function
      self.setFfunc(redraw=True, keepBoot=True)

  def changeParamTableBoundaries(self):
    # function called by odr optimization to ensure that afterwards lower and upper limits are adjusted if needed
    for index, entry in enumerate(self.paramList):
      if(self.boundaryLowCheck[index].isChecked()):
        if(self.param[index] < self.param_lower[index]):
          self.param_lower[index] = self.param[index]
          self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.param_lower[index]))
      if(self.boundaryHighCheck[index].isChecked()):
        if(self.param[index] > self.param_upper[index]):
          self.param_upper[index] = self.param[index]
          self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.param_upper[index]))

  def editParam(self, index):
    try:
      self.param[index] = float(self.ParamTable.cellWidget(index, 1).text())
    except:
      self.param[index] = 0

    # update parameters
    # check whether this parameter is fixed or not
    if (self.param_active[index]):
      fitpa = []
      for index2, entry in enumerate(self.param):
        if(self.param_active[index2]):
          fitpa.append(entry)
      self.parent.fit[self.parent.activeFit].updateParam(fitpa)
      self.parent.fit[self.parent.activeFit].clearBoot()
      self.parent.fit[self.parent.activeFit].clearLastFitResults()
    else:
      # also have to recompile the fit function
      self.setFfunc(keepBoot=False)

    # plot function
    self.parent.fit[self.parent.activeFit].handlePlot, self.parent.fit[self.parent.activeFit].handleBoot = self.parent.plotArea.plotFunction(\
      fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot, redraw=False)
    self.parent.fit[self.parent.activeFit].handlePlot_div, self.parent.fit[self.parent.activeFit].handleBoot_div = self.parent.plotArea.plotFunction(\
      fitobject=self.parent.fit[self.parent.activeFit], handlePlot=self.parent.fit[self.parent.activeFit].handlePlot_div, handleBoot=self.parent.fit[self.parent.activeFit].handleBoot_div, redraw=False, splitX=True)
    # copy in case split axes are shown
    curve = self.parent.fit[self.parent.activeFit]
    if(self.parent.plotArea.splitY and curve.onBothAxes):
      curve.duplicateForSplit()
    # refresh plot
    self.parent.plotArea.dataplotwidget.myRefresh()
    
    # update parameter boundaries if required
    if(self.param[index] < self.param_lower[index]):
      self.param_lower[index] = self.param[index]
      self.boundaryLowEntry[index].setText(self.parent.formatNumber(self.param_lower[index]))
    if(self.param[index] > self.param_upper[index]):
      self.param_upper[index] = self.param[index]
      self.boundaryHighEntry[index].setText(self.parent.formatNumber(self.param_upper[index]))

    # should we also update the global param table?
    # => I guess that's a no
    
  def clickParam(self, index):
    # test what state the checkbox now has
    if(self.ParamTable.cellWidget(index, 0).isChecked()):
      self.param_active[index] = 1
    else:
      self.param_active[index] = 0
      self.confidence[index] = '--'
      self.ParamTable.cellWidget(index, 2).setText(str(self.confidence[index]))
      self.confidence_apriori[index] = '--'
      self.ParamTable.cellWidget(index, 3).setText(str(self.confidence_apriori[index]))
    
    # update ffunc and parameters
    fitpa = []
    for index, entry in enumerate(self.param):
      if(self.param_active[index]):
        fitpa.append(entry)
    self.parent.fit[self.parent.activeFit].updateActive(self.param_active)
    self.parent.fit[self.parent.activeFit].updateParam(fitpa)
    self.setFfunc(keepBoot=True)
    # but we should not need to update the plot itself

  def initFfunc(self, searchPath='', pattern='*.ffunc'):
    # returns list of available fit functions
    if(searchPath == ''):
      searchPath = WORKINGDIR + PATH_SEPARATOR + 'functions' + PATH_SEPARATOR
    ffunc_list = [path.absolute().__str__() for path in Path(searchPath).rglob(pattern)]
    return ffunc_list
    
  def loadFfunc(self, filename):
    # load fit function
    try:
      # read file contents
      with open(filename, 'r', encoding='utf-8') as readhandle:
        red = readhandle.readline()
        params = []; formula = ''; mode = 0; values = []; active = []
        while(red):
          if ('<PARAMETERS>' in red):
            mode = 1
          elif ('<FORMULA>' in red):
            mode = 2
          elif (mode == 1):
            red = red.strip()
            if (',' in red):
              temparray = red.split(',')
              params.append(temparray[0])
              try:
                values.append(float(temparray[1]))
              except:
                values.append(0.0)
              if(red.count(',') > 1):
                try:
                  active.append(bool(float(temparray[2])))
                except:
                  active.append(True)
              else:
                active.append(True)
            else:
              params.append(red)
              values.append(1.0)
              active.append(True)
          elif (mode == 2):
            formula += red
          
          red = readhandle.readline()
        
      success = True
    except:
      success = False; params = []; formula = ''; values = []; active = []
    
    return success, params, formula, values, active
       
  def selectFfunc(self, filename):
    # a new fit function was selected in the list
    success, parameters, formula, values, active = self.loadFfunc(filename)
    if(success):
      self.currentFunction = filename
      label = Path(self.currentFunction)
      label = label.name.split('.ffunc')[0]
      self.functionSelector.setText('\u25be ' + label)
      
      self.paramList = parameters
      parameters = ', '.join(parameters)
      self.declareParamEntry.setText(parameters)
      self.fitFormula.setText(formula)
      self.param = values
      self.storeParam = [i for i in self.param]
      self.confidence = ['--'] * len(values)
      self.confidence_apriori = ['--'] * len(values)
      self.param_active = [1 if i else 0 for i in active]
      self.param_lower, self.param_upper = [-np.inf] * len(values), [np.inf] * len(values)
      self.updateParamTable()
      self.useFit(keepBoot=False)
    else:
      self.parent.statusbar.showMessage('Cannot load function ' + filename + '!', self.parent.STATUS_TIME)
    
  def restoreFfunc(self, parameters, formula, values, active, fitresults, confidence, confidence_apriori, redraw=True):
    # an existing fit function was restored
    self.paramList = parameters
    parameters = ', '.join(parameters)
    self.declareParamEntry.setText(parameters)
    self.fitFormula.setText(formula)
    self.fitResults.setText(fitresults)
    self.param = values
    self.storeParam = [i for i in self.param]
    # need these checks for backward compatibility
    if((type(confidence) != type([])) or (len(confidence) != len(values))):
      self.confidence = ['--'] * len(values)
    else:
      self.confidence = confidence
    if((type(confidence_apriori) != type([])) or (len(confidence_apriori) != len(values))):
      self.confidence_apriori = ['--'] * len(values)
    else:
      self.confidence_apriori = confidence_apriori
    self.param_active = [1 if i else 0 for i in active]
    self.param_lower, self.param_upper = [-np.inf] * len(values), [np.inf] * len(values)
    self.updateParamTable()
    self.useFit(redraw=redraw, keepBoot=True)
    
  def setAdvancedRandomSearch(self, key=None, value=None):
    # updates advanced random search settings
    if(key in self.advancedRandomSearch):
      self.advancedRandomSearch[key] = value

  def setAdvancedBootstrap(self, key=None, value=None):
    # updates advanced bootstrap settings
    if(key in self.advancedBootstrap):
      self.advancedBootstrap[key] = value

  def setAdvancedMinimization(self, key=None, value=None):
    # updates advanced minmization settings
    if(key in self.advancedMinimization):
      self.advancedMinimization[key] = value

    if(key == 'algorithm'):
      self.doMinButton.setText(' ' + self.advancedMinimization['algorithm'])

  def formatNumber(self, number):
    # formats number for output
    NUMBER_SWITCH = 1e3
    FORMAT_DECIMAL = '{:.3f}'
    FORMAT_SCIENTIFIC = '{:.3e}'
    # determine return string
    if((self.isNumber(number)) and (np.isfinite(float(number)))):
      if((np.abs(number)>NUMBER_SWITCH) or (np.abs(number)<1.0/NUMBER_SWITCH)):
        numberstr = FORMAT_SCIENTIFIC.format(number)
        zerostr = FORMAT_SCIENTIFIC.format(0.0)
      else:
        numberstr = FORMAT_DECIMAL.format(number)
        zerostr = FORMAT_DECIMAL.format(0.0)
      if(numberstr == zerostr):
        numberstr = '0'
    else:
      numberstr = str(number)
    
    return numberstr

  def isNumber(self, s):
    # checks whether string is a number
    try:
      float(s)
      return True
    except ValueError:
      pass
   
    try:
      import unicodedata
      unicodedata.numeric(s)
      return True
    except (TypeError, ValueError):
      pass
    
    return False
  
class FitObject(object):
  def __init__(self, parent = None):
    self.parent = parent
    self.parent.zcount += 1
    self.zorder = self.parent.zcount
    self.onSecondAxes = False

    # locally import numpy again
    import numpy as np
    # import common functions from numpy for ease of access
    from numpy import abs, arccos, arcsin, arctan, exp, cos, cosh, log, log2, log10, power, sin, sinh, sqrt, tan, tanh
    self.mySpace = locals()
    
    self.param = np.array([])
    self.active = []
    self.paramNames =[]
    self.paramAll = []
    self.confidence = []
    self.confidence_apriori = []
    self.ffuncstr_base = ''
    self.ffunc_header = ''
    self.fitresults = ''
    self.chisq = '--'
    self.redchisq = '--'
    self.bootFpar, self.bootActive, self.bootType = [], [], True
    self.confidenceData = {}
    self.lastFitResults = {}
    
    # use epsilon as minimum error value
    self.EPSILON = 1e-9
    # initialize handles for graphics
    self.handlePlot = None
    self.handlePlot_div = None
    self.handleBoot = None
    self.handleBoot_div = None
    # initialize name
    self.setName('Curve_' + str(len(self.parent.fit) + 1))
    # initialize visibility
    self.visibility = True
    # initialize notes
    self.notes = ''
    # initialize activity state
    self.retired = False
    # initialize data storage
    self.x = np.array([])
    self.y = np.array([])
    self.x_div = np.array([])
    self.y_div = np.array([])
    # initalize style
    self.style = {}
    self.style['linewidth'] = 1.5
    self.style['linestyle'] = 'solid'
    self.style['dash_capstyle'] = 'butt'
    #self.style['solid_capstyle'] = 'butt'
    self.style['color'] = [0.89, 0.29, 0.2, 1.0]
    self.style['markerfacecolor'] = [0.89, 0.29, 0.2, 1.0]
    self.style['markerfacecoloralt'] = [0.0, 0.0, 0.0, 1.0]
    self.style['markeredgecolor'] = [0.0, 0.0, 0.0, 1.0]
    self.style['markeredgewidth'] = 1.0
    self.style['markersize'] = 14.0
    self.style['marker'] = 'None'
    self.style['fillstyle'] = 'full'
    self.style['markevery'] = 100
    self.style['doFill'] = True
    self.style['gapcolor'] = [0, 0, .8, 1.]
    self.style['doGap'] = False
    # initalize bootstrap style
    self.bootStyle = {}
    self.bootStyle['visible'] = True
    self.bootStyle['facecolor'] = [0.8, 0.2, 0.2, 0.5]
    self.bootStyle['edgecolor'] = [0.8, 0.2, 0.2, 1.0]
    self.bootStyle['linestyle'] = 'solid'
    self.bootStyle['linewidth'] = 0.0
    self.bootStyle['hatch'] = ''
    self.bootStyle['hatchMultiply'] = 1
    self.bootStyle['doFill'] = True
        
    # restrict plot interval?
    self.restrictMeLow, self.restrictMeHigh, self.restrictLow, self.restrictHigh = False, False, 0.0, 1.0

    # stuff for split axes
    self.onBothAxes = False
    self.handlesBothAxes = {}
    
    # initialize funcstr_base
    self.ffuncstr_base = ''
    self.ffunc_header = ''
    self.fitresults = ''
    
  def reportState(self):
    # reports data content for saveState function
    items = 'name,param,ffuncstr_base,ffunc_header,active,paramNames,confidence,confidence_apriori,fitresults,paramAll,zorder,retired,x,y,x_div,y_div,onSecondAxes,onBothAxes,chisq,redchisq,restrictMeLow,restrictMeHigh,restrictLow,restrictHigh,notes,bootFpar,bootActive,lastFitResults'.split(',')
    retv = {}
    
    for entry in items:
      if(hasattr(self, entry)):
        value = self.__dict__[entry]
        if(entry == 'bootFpar'):
          value = [i.tolist() for i in value]
        elif(type(value) == type(np.array([]))):
          value = value.tolist()
        retv[entry] = value
    
    return retv

  def restoreState(self, data={}, zoffset=0):
    # restores data content for loadState function
    for entry in data:
      if(hasattr(self, entry)):
        if(entry in ['x', 'y', 'x_div', 'y_div']):
          value = np.array(data[entry])
        elif(entry == 'bootFpar'):
          value = [np.array(i) for i in data[entry]]
        elif(entry == 'zorder'):
          value = data[entry] + zoffset
        elif(entry == 'name'):
          value = data[entry]
          self.setName(value)
        else:
          value = data[entry]
        self.__dict__[entry] = value
    
    # redefine fit function
    if((self.ffuncstr_base != '') or (self.ffunc_header != '')):
      self.updateFunc(self.ffuncstr_base, self.ffunc_header)
      
    # now restore bootstrap information as updateFunc() kills this information
    if('bootFpar' in data):
      self.bootFpar = [np.array(i) for i in data['bootFpar']]
    if('bootActive' in data):
      self.bootActive = data['bootActive']
    if('lastFitResults' in data):
      self.lastFitResults = data['lastFitResults']
      
  def retrieveInfo(self):
    # returns previous fit formula and values etc.
    return self.paramNames, self.ffuncstr_base, self.paramAll, self.active, self.fitresults, self.confidence, self.confidence_apriori

  def retrieveInfoReport(self):
    # returns previous fit formula and values etc. -- for use in generating HTML report
    return self.paramNames, self.active, self.paramAll, self.confidence, self.confidence_apriori, self.chisq, self.redchisq
    
  def storeInfo(self, paramNames, paramAll, active, confidence, confidence_apriori, ffuncstr_base, ffunc_header, fitresults, chisq, redchisq, rsquare, lastFitResults):
    # ensure to make actual copies not soft links!!
    self.paramNames = [i for i in paramNames]
    self.paramAll = [i for i in paramAll]
    self.active = [i for i in active]
    self.confidence = [i for i in confidence]
    self.confidence_apriori = [i for i in confidence_apriori]
    self.ffuncstr_base = ffuncstr_base
    self.ffunc_header = ffunc_header
    self.fitresults = fitresults
    self.chisq = deepcopy(chisq)
    self.redchisq = deepcopy(redchisq)
    self.rsquare = deepcopy(rsquare)
    self.lastFitResults = {i:j for i, j in lastFitResults.items()}

  def storeConfidenceBand(self, xv=[], yvlo=[], yvhi=[], splitX=False):
    # stores data for confidence band to enable dupliacte on split
    if(splitX):
      useKeys = ['xv_div', 'yvlo_div', 'yvhi_div']
    else:
      useKeys = ['xv', 'yvlo', 'yvhi']
    self.confidenceData[useKeys[0]], self.confidenceData[useKeys[1]], self.confidenceData[useKeys[2]] = xv, yvlo, yvhi

  def retrieveConfidenceBand(self, splitX=False):
    # restores data for confidence band to enable dupliacte on split
    if(splitX):
      useKeys = ['xv_div', 'yvlo_div', 'yvhi_div']
    else:
      useKeys = ['xv', 'yvlo', 'yvhi']
    return [self.confidenceData[i] if i in self.confidenceData else [] for i in useKeys]

  def storeBoot(self, fparam=None, bootType=True):
    # stores information from a bootstrap call
    self.bootType = bootType
    # need to remember which parameters were active when bootstrapping
    self.bootActive = [i for i in self.active]
    self.bootFpar = []
    for trial in fparam:
      current = np.array([i for i in self.paramAll])
      current[np.where(self.active)] = trial
      self.bootFpar.append(current)

  def getBoot(self):
    # returns bootstrapped parameters but chekc whether active parameters changed
    testActive = [0 if (i == j) else 1 for i, j in zip(self.bootActive, self.active)]
    # filter for currently active parameters (bc only those are passed to fit function)
    retv = [[j for j, k in zip(i, self.active) if k] for i in self.bootFpar]
    return retv, sum(testActive) > 0

  def clearBoot(self):
    # clears bootstrap information
    self.bootActive, self.bootFpar = [], []
    self.confidenceData = {}
    # delete plot items
    for handle in ['handleBoot', 'handleBoot_div']:
      if(self.__dict__[handle] != None):
        self.__dict__[handle].remove()
        self.__dict__[handle] = None
      if((handle in self.handlesBothAxes) and (self.handlesBothAxes[handle] != None)):
        self.handlesBothAxes[handle].remove()
        self.handlesBothAxes[handle] = None
    
  def clearLastFitResults(self):
    # clears information for confidence band calculation
    self.lastFitResults = {}

  def clearConfidence(self):
    # clears confidence, e.g., after a minimization call
    self.confidence = ['--'] * len(self.paramAll)
    self.confidence_apriori = ['--'] * len(self.paramAll)
    return [i for i in self.confidence], [i for i in self.confidence_apriori]

  def setName(self, name='Jane Doe'):
    # updates name of object
    self.name = name
    # update plot if necessary
    if(self.handlePlot != None):
      self.handlePlot.set_label(name)
    
  def setZOrder(self, zorder=0, redraw=True):
    # updates z order
    if(self.zorder == zorder):
      redraw, changed = False, False
    else:
      changed = True
    self.zorder = zorder
    # update plot if necessary
    updateFlag = False
    if(self.handlePlot != None):
      self.handlePlot.set_zorder(self.zorder + self.parent.zOffset)
      if(self.parent.plotArea.splitY and ('handlePlot' in self.handlesBothAxes)):
        self.handlesBothAxes['handlePlot'].set_zorder(self.zorder + self.parent.zOffset)
      updateFlag = True
    if(self.handlePlot_div != None):
      self.handlePlot_div.set_zorder(self.zorder + self.parent.zOffset)
      if(self.parent.plotArea.splitY and ('handlePlot_div' in self.handlesBothAxes)):
        self.handlesBothAxes['handlePlot_div'].set_zorder(self.zorder + self.parent.zOffset)
      updateFlag = True
    if(self.handleBoot != None):
      self.handleBoot.set_zorder(self.zorder + self.parent.zOffset - 0.2)
      if(self.parent.plotArea.splitY and ('handleBoot' in self.handlesBothAxes)):
        self.handlesBothAxes['handleBoot'].set_zorder(self.zorder + self.parent.zOffset - 0.2)
    if(self.handleBoot_div != None):
      self.handleBoot_div.set_zorder(self.zorder + self.parent.zOffset - 0.2)
      if(self.parent.plotArea.splitY and ('handleBoot_div' in self.handlesBothAxes)):
        self.handlesBothAxes['handleBoot_div'].set_zorder(self.zorder + self.parent.zOffset - 0.2)
      updateFlag = True
    # update plot
    if(redraw and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return changed

  def setBothAxes(self, state=False, redraw=True):
    # moves plot between axes
    self.onBothAxes = state
    # remove surplus items?
    if(self.onBothAxes):
      self.duplicateForSplit()
    else:
      self.removeFromSplit()

  def setSecondAxes(self, state=False, redraw=True):
    # moves plot between axes
    self.onSecondAxes = state
    # check whether dual display is turned on
    if(self.parent.plotArea.splitY and self.onBothAxes):
      # we can just swap the handles
      for item in ['handlePlot', 'handlePlot_div']:
        if(item in self.handlesBothAxes):
          self.__dict__[item], self.handlesBothAxes[item] = self.handlesBothAxes[item], self.__dict__[item]
      return
    # trigger redraw
    self.drawMe(redraw=redraw, rescale=not self.parent.plotArea.splitY)

  def setVisibility(self, state=True, redraw=True):
    # toggles visibility of curve
    if(self.visibility == state):
      redraw, changed = False, False
    else:
      changed = True
    self.visibility = state
    updateFlag = False
    if(self.handlePlot != None):
      self.handlePlot.set_visible(state)
      if(self.visibility):
        self.handlePlot.set_label(self.name)
      else:
        self.handlePlot.set_label('_nolegend_')
      updateFlag = True
      if(self.parent.plotArea.splitY and ('handlePlot' in self.handlesBothAxes)):
        self.handlesBothAxes['handlePlot'].set_visible(state)
    if(self.handlePlot_div != None):
      self.handlePlot_div.set_visible(state)
      updateFlag = True
      if(self.parent.plotArea.splitY and ('handlePlot_div' in self.handlesBothAxes)):
        self.handlesBothAxes['handlePlot_div'].set_visible(state)
    if(self.handleBoot != None):
      self.handleBoot.set_visible(state and self.bootStyle['visible'])
      updateFlag = True
      if(self.parent.plotArea.splitY and ('handleBoot' in self.handlesBothAxes)):
        self.handlesBothAxes['handleBoot'].set_visible(state and self.bootStyle['visible'])
    if(self.handleBoot_div != None):
      self.handleBoot_div.set_visible(state and self.bootStyle['visible'])
      updateFlag = True
      if(self.parent.plotArea.splitY and ('handleBoot_div' in self.handlesBothAxes)):
        self.handlesBothAxes['handleBoot_div'].set_visible(state and self.bootStyle['visible'])

    # if visibility is activated, check for autozoom
    # on second thought, should also do this if inactivated
    if(True):
      if(self.parent.plotArea.autoScaleY):
        self.parent.plotArea.doAutoScale(axis='y', redraw=False)
      if(self.parent.plotArea.autoScaleY2):
        self.parent.plotArea.doAutoScale(axis='y2', redraw=False)

    # update plot
    if(redraw and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return changed

  def spawned(self, source=None):
    # copies contents of the source object to current object
    # note: we do not need to copy ffunc() since we will redefine it in the new object
    if(source != None):
      copyItems = 'param,style,x,y,visibility,ffuncstr_base,ffunc_header,active,paramNames,confidence,fitresults,paramAll,onSecondAxes,onBothAxes,chisq,redchisq,restrictMeLow,restrictMeHigh,restrictLow,restrictHigh,notes,bootFpar,bootActive,lastFitResults'.split(',') #, ffunc
      for item in copyItems:
        if(hasattr(source, item)):
          sourceItem = deepcopy(getattr(source, item))
          setattr(self, item, sourceItem)
      # redefine fit function
      if((self.ffuncstr_base != '') or (self.ffunc_header != '')):
        self.updateFunc(self.ffuncstr_base, self.ffunc_header)
    
  def initialize(self):
    # initialize object with default values
    success = False
    if (len(self.parent.fitarea.ffuncList)):
      defaultFunction = 'binding_isotherm.ffunc'
      functionNo = 0
      for index, entry in enumerate(self.parent.fitarea.ffuncList):
        if(entry.endswith(defaultFunction)):
          functionNo = index
      success, self.paramNames, self.ffuncstr_base, self.paramAll, self.active = self.parent.fitarea.loadFfunc(self.parent.fitarea.ffuncList[functionNo])
      self.parent.fitarea.currentFunction = self.parent.fitarea.ffuncList[functionNo]
      label = Path(self.parent.fitarea.currentFunction)
      label = label.name.split('.ffunc')[0]
      self.parent.fitarea.functionSelector.setText('\u25be ' + label)
    # opt for default function if problems arise
    if(not success):
      self.paramNames = ['A0', 'A1', 'KD']
      self.ffuncstr_base = '# a binding isotherm\ny = A0 + A1 * x / (x + KD)'
      self.paramAll = [1, 1, 1]
      self.active = [True] * 3
      
    # prepare and set fit function
    self.param = [i for i, j in zip(self.paramAll, self.active) if j]
    nonactive = [str(i) for i, j in zip(self.paramAll, self.active) if (not j)]
    self.ffunc_header = ', '.join(nonactive)
    self.confidence = ['--'] * len(self.param)

    # redefine fit function
    if((self.ffuncstr_base != '') or (self.ffunc_header != '')):
      self.updateFunc(self.ffuncstr_base, self.ffunc_header)
    
  def drawMe(self, redraw=True, rescale=True):
    # causes curve to be drawn on canvas
    self.handlePlot, self.handleBoot = self.parent.plotArea.plotFunction(fitobject=self, handlePlot=self.handlePlot, handleBoot=self.handleBoot, redraw=False, doAutoZoom=rescale)
    # set visibility
    if (self.handlePlot != None):
      self.handlePlot.set_visible(self.visibility)

    self.handlePlot_div, self.handleBoot_div = self.parent.plotArea.plotFunction(fitobject=self, handlePlot=self.handlePlot_div, handleBoot=self.handleBoot_div, redraw=False, splitX=True, doAutoZoom=rescale)
    # set visibility
    if (self.handlePlot_div != None):
      self.handlePlot_div.set_visible(self.visibility)

    # copy in case split axes are shown
    curve = self
    if(self.parent.plotArea.splitY and curve.onBothAxes):
      curve.duplicateForSplit()
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def styleMe(self, handlePlot=None, handleBoot=None):
    # applies current styles to plot handle
    if(handlePlot != None):
      for key in self.style:
        method = 'set_' + key
        if (hasattr(handlePlot, method)):
          method2call = getattr(handlePlot, method)
          if((key == 'marker') and (self.style[key] in CUSTOM_MARKERS)):
            method2call(CUSTOM_MARKERS[self.style[key]])
          elif((key == 'marker') and (not (self.style[key] in matplotlib.lines.Line2D.markers))):
            # this is to ensure compatibility with future versions that may introduce markers we don't know yet
            method2call('o')
          elif((key in ['linestyle', 'linewidth']) and (self.style['linestyle'] != 'solid') and (self.style['linewidth'] < .01)):
            # need to set linestyle to 'solid' to avoid crash
            self.style['linestyle'] = 'solid'
            handlePlot.set_linestyle('solid')
            handlePlot.set_linewidth(self.style['linewidth'])
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          else:
            method2call(self.style[key])
      if(not self.style['doFill']):
        if(hasattr(handlePlot, 'set_markerfacecolor')):
          handlePlot.set_markerfacecolor('none')
      if(not self.style['doGap']):
        if(hasattr(handlePlot, 'set_gapcolor')):
          handlePlot.set_gapcolor('none')
      
      # set name
      if (hasattr(handlePlot, 'set_label')):
        method2call = getattr(handlePlot, 'set_label')
        method2call(self.name)

    if(handleBoot != None):
      for key in self.bootStyle:
        if(key in ['doFill']):
          key = 'facecolor'
          if(self.bootStyle['doFill']):
            value = self.bootStyle['facecolor']
          else:
            value = 'none'
        else:
          value = self.bootStyle[key]
        method = 'set_' + key
        if (hasattr(handleBoot, method)):
          method2call = getattr(handleBoot, method)
          if(key == 'visible'):
            method2call(value and self.visibility)
          elif((key == 'facecolor') and ('doFill' in self.bootStyle) and (not self.bootStyle['doFill'])):
            method2call('none')
          elif((key == 'hatch') and ('hatchMultiply' in self.bootStyle)):
            method2call(value * self.bootStyle['hatchMultiply'])
          else:
            method2call(value)

  def getStyle(self):
    # returns the style object
    return self.style

  def getBootStyle(self):
    # returns boot style of object
    return self.bootStyle

  def getRestricted(self):
    # returns restriction info
    return self.restrictMeLow, self.restrictMeHigh, self.restrictLow, self.restrictHigh

  def setRestricted(self, key, value, redraw=True):
    # toggles plot interval restriction
    if(key in ('restrictMeLow', 'restrictMeHigh', 'restrictLow', 'restrictHigh')):
      # actually, we should disable this check to allow correct updating in all instances
      ###if((key in ['restrictMeLow', 'restrictMeHigh']) or (self.__dict__[key] != value)):
      self.__dict__[key] = value
      if(self.retired):
        # temporarily unretire and update
        prevActive = self.parent.activeFit
        self.parent.activeFit = self.parent.fit.index(self)
        self.retired, self.parent.fit[prevActive].retired = False, True
        # change fit formula
        parameters, formula, values, active, fitresults, confidence, confidence_apriori = self.retrieveInfo()
        self.parent.fitarea.restoreFfunc(parameters, formula, values, active, fitresults, confidence, confidence_apriori, redraw=False)
        self.drawMe(redraw=redraw, rescale=False)
        # restore previous function
        self.retired, self.parent.fit[prevActive].retired = True, False
        self.parent.activeFit = prevActive
        parameters, formula, values, active, fitresults, confidence, confidence_apriori = self.parent.fit[prevActive].retrieveInfo()
        self.parent.fitarea.restoreFfunc(parameters, formula, values, active, fitresults, confidence, confidence_apriori, redraw=redraw)
      else:
        self.drawMe(redraw=False, rescale=False)
        if((self.onBothAxes or (not self.onSecondAxes)) and self.parent.plotArea.autoScaleY):
          # need to do the autoscale via the plotArea function to heed split axes
          self.parent.plotArea.doAutoScale(axis='y', redraw=redraw)
        elif((self.onBothAxes or self.onSecondAxes) and self.parent.plotArea.autoScaleY2):
          self.parent.plotArea.doAutoScale(axis='y2', redraw=redraw)
        elif(redraw):
          self.parent.plotArea.dataplotwidget.myRefresh()

  def toggleMarkerFill(self, value, redraw=True):
    # toggles marker fill
    if(self.style['doFill'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.style['doFill'] = value
    # adjust value
    if(value):
      value = self.style['markerfacecolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    if(self.handlePlot != None):
      self.handlePlot.set_markerfacecolor(value)
    if(self.handlePlot_div != None):
      self.handlePlot_div.set_markerfacecolor(value)

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return (changed and self.visibility)

  def toggleDoGap(self, value, redraw=True):
    # toggles line gap color
    if(self.style['doGap'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.style['doGap'] = value
    # adjust value
    if(value):
      value = self.style['gapcolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    if(hasattr(self.handlePlot, 'set_gapcolor')):
      if(self.handlePlot != None):
        self.handlePlot.set_gapcolor(value)
      if(self.handlePlot_div != None):
        self.handlePlot_div.set_gapcolor(value)

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return (changed and self.visibility)
    
  def setStyle(self, key, value, redraw=True):
    # changes the style value
    if(key in self.style):
      if(self.style[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.style[key] = value
      # cause plot to be updated
      updateFlag = False
      # special treatment for markerfacecolor
      if((key != 'markerfacecolor') or self.style['doFill']):
        handles, handles_div = [self.handlePlot], [self.handlePlot_div]
        if(self.parent.plotArea.splitY and ('handlePlot' in self.handlesBothAxes)):
          handles += [self.handlesBothAxes['handlePlot']]
        if(self.parent.plotArea.splitY and ('handlePlot_div' in self.handlesBothAxes)):
          handles_div += [self.handlesBothAxes['handlePlot_div']]

        method = 'set_' + key
        for handlePlot in handles:
          if(handlePlot != None):
            if (hasattr(handlePlot, method)):
              method2call = getattr(handlePlot, method)
              if(key == 'marker'):
                if(value in CUSTOM_MARKERS):
                  method2call(CUSTOM_MARKERS[value])
                elif(not (value in matplotlib.lines.Line2D.markers)):
                  # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                  method2call('o')
                else:
                  method2call(value)
              elif((key in ['linestyle', 'linewidth']) and (self.style['linestyle'] != 'solid') and (self.style['linewidth'] < .01)):
                # need to set linestyle to 'solid' to avoid crash
                self.style['linestyle'] = 'solid'
                handlePlot.set_linestyle('solid')
                handlePlot.set_linewidth(self.style['linewidth'])
                self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              else:
                method2call(value)
              updateFlag = True
              # special treatment for capstyle
              if((key == 'dash_capstyle') and (hasattr(handlePlot, 'set_solid_capstyle'))):
                method2call = getattr(handlePlot, 'set_solid_capstyle')
                method2call(value)

        for handlePlot_div in handles_div:
          if(handlePlot_div != None):
            method = 'set_' + key
            if (hasattr(handlePlot_div, method)):
              method2call = getattr(handlePlot_div, method)
              if(key == 'marker'):
                if(value in CUSTOM_MARKERS):
                  method2call(CUSTOM_MARKERS[value])
                elif(not (value in matplotlib.lines.Line2D.markers)):
                  # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                  method2call('o')
                else:
                  method2call(value)
              elif((key in ['linestyle', 'linewidth']) and (self.style['linestyle'] != 'solid') and (self.style['linewidth'] < .01)):
                # need to set linestyle to 'solid' to avoid crash
                self.style['linestyle'] = 'solid'
                handlePlot_div.set_linestyle('solid')
                handlePlot_div.set_linewidth(self.style['linewidth'])
                self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              else:
                method2call(value)
              updateFlag = True
              # special treatment for capstyle
              if((key == 'dash_capstyle') and (hasattr(handlePlot_div, 'set_solid_capstyle'))):
                method2call = getattr(handlePlot_div, 'set_solid_capstyle')
                method2call(value)
      else:
        # prevent update of plot
        changed = False

      if(redraw and self.visibility and updateFlag):
        self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def setFitStyleSet(self, index=0):
    # called to update an entire style set for a newly generated data object
    if(index in self.parent.fitStyles):
      for entry in self.parent.fitStyles[index]:
        if(entry == 'curve'):
          useDict = {key:value for key, value in self.parent.fitStyles[index][entry].items() if (key in self.style)}
          self.style.update(useDict)
        elif(entry == 'cboot'):
          useDict = {key:value for key, value in self.parent.fitStyles[index][entry].items() if (key in self.bootStyle)}
          self.bootStyle.update(useDict)
    
  def setBootStyle(self, key, value, redraw=True):
    # changes the confidence band
    if(key in self.bootStyle):
      if(self.bootStyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.bootStyle[key] = value
      # cause plot to be updated
      if(changed):
        handles = [self.handleBoot, self.handleBoot_div]
        if(self.parent.plotArea.splitY):
          handles += [self.handlesBothAxes[i] for i in ['handleBoot', 'handleBoot_div'] if i in self.handlesBothAxes]
        for handleBoot in handles:
          if(handleBoot != None):
            if(key in ['doFill', 'facecolor']):
              if(self.bootStyle['doFill']):
                self.handleBoot.set_facecolor(self.bootStyle['facecolor'])
              else:
                self.handleBoot.set_facecolor('none')
            else:
              if(key == 'hatchMultiply'):
                key, value = 'hatch', self.bootStyle['hatch']
              method = 'set_' + key
              if (hasattr(handleBoot, method)):
                method2call = getattr(handleBoot, method)
                # extra check b/c of hatchMultiply
                if((key == 'hatch') and ('hatchMultiply' in self.bootStyle)):
                  method2call(value * self.bootStyle['hatchMultiply'])
                else:
                  method2call(value)

      if(redraw and self.visibility):
        self.parent.plotArea.dataplotwidget.myRefresh()

  def updateFunc(self, funcstr_base, ffunc_header, testParam=[]):
    try:
      funcstr = 'def ffunc(self, x' + ffunc_header + '):\n' + funcstr_base + '\n\treturn y'
      # generate ffunc in global namespace (this is needed for Python3 vs. Python2, bummer)
      namespace = self.mySpace
      exec(funcstr, namespace)
      # we need to do some initial test to see whether the function can be called
      ###if(len(testParam)):
      # actually, we should always do the test here because if all parameters are fixed
      # we would not test but absolutely need to to catch all kinds of errors
      if(True):
        # determine x-range over which function will be applied => we should test this
        xmin, xmax = self.parent.plotArea.minX, self.parent.plotArea.maxX
        if(self.parent.plotArea.modeX == 'linear'):
          testRange = np.linspace(xmin, xmax, self.parent.plotArea.DATAPOINTS_SIMULATION)
        elif(self.parent.plotArea.modeX == 'log'):
          testRange = np.linspace(np.log(xmin), np.log(xmax), self.parent.plotArea.DATAPOINTS_SIMULATION)
          testRange = np.exp(testRange)
        # call function for test purposes
        retv = namespace['ffunc'](self, testRange, *testParam)
        # check for dimension mismatch
        if(retv.shape != testRange.shape):
          # we found some mismatch b/w x and y -- raise error to prevent program crash
          raise ValueError
      # now define the new function in the object scope
      setattr(FitObject, 'ffunc', namespace['ffunc'])
      self.funcstr_base = funcstr_base
      self.ffunc_header = ffunc_header
      return True, ''
    except ValueError as e:
      try:
        returnstr ='Shape of y values ' + repr(retv.shape) + ' does not match that of x values ' + repr(testRange.shape) + '.'
      except:
        returnstr = repr(e)
      return False, returnstr
    except Exception as e:
      returnstr = repr(e)
      try:
        # determine line numbe of error (subtract 1 because of function header)
        lineno = exc_info()[-1].tb_next.tb_lineno - len(self.paramAll) + len(self.param) - 1
        returnstr = 'line ' + str(lineno) + ': ' + returnstr
      except:
        # need to except this, not least for indentation errors
        pass
      return False, returnstr

  def updateXY(self, x=[], y=[], splitX=False):
    # called by global fit to update fit curve
    if((len(x)) and (len(y))):
      if(splitX):
        self.x_div, self.y_div = x, y
      else:
        self.x, self.y = x, y

  def updateChiSquare(self, chisquare, red_chisquare):
    self.chisq, self.redchisq = chisquare, red_chisquare

  def updateActive(self, active=[]):
    # changes active parameters
    self.active = deepcopy(active)

  def updateParam(self, param=[]):
    self.param = deepcopy(param)
    # also need to change self.paramAll accordingly
    index = 0
    for index2, entry in enumerate(self.paramAll):
      if(self.active[index2]):
        if(index < len(self.param)):
          self.paramAll[index2] = self.param[index]
          index += 1

  def updateParamSelect(self, names=[], values=[]):
    # called by global fit to update parameters
    updated = False
    paramActiveNames = [i for i, j in zip(self.paramNames, self.active) if j]
    for index, entry in enumerate(names):
      if(entry in self.paramNames):
        updated = True
        self.paramAll[self.paramNames.index(entry)] = values[index]
      if(entry in paramActiveNames):
        self.param[paramActiveNames.index(entry)] = values[index]
    
    # reset all confidences and a priori confidences to '--'
    if(updated):
      self.confidence = ['--'] * len(self.paramAll)
      self.confidence_apriori = ['--'] * len(self.paramAll)
      
  def evaluateFunc(self, x=np.array([]), param=[]):
    # evaluate function for a given param array
    if(len(param) == 0):
      param = self.param
    if(not self.retired):
      # evaluate function
      try:
        retv = self.ffunc(x, *param)
      except:
        # some kind of function problem, return empty array
        # for the future: could do this for each data point individually
        retv = np.array([0 for i in x])
    else:
      retv = np.array([0 for i in x])

    return x, retv

  def simulateFunc(self, x=np.array([]), splitX=False):
    # is this function still active?
    if(splitX):
      if(not self.retired):
        # check for empty x and, if so, use existing self.x
        if(not x.size):
          x = self.x_div
        # evaluate function
        try:
          retv = self.ffunc(x, *self.param)
        except:
          # some kind of function problem, return empty array
          # for the future: could do this for each data point individually
          retv = np.array([0 for i in x])
          
        # store results for future use
        self.x_div, self.y_div = x, retv
      
      # implement check for size -- if mismatch then better return x/y for principal axis
      if((self.x_div.size > 0) and (self.x_div.size == self.y_div.size)):
        return self.x_div, self.y_div
      else:
        return self.x, self.y
    else:
      if(not self.retired):
        # check for empty x and, if so, use existing self.x
        if(not x.size):
          x = self.x
        # evaluate function
        try:
          retv = self.ffunc(x, *self.param)
        except:
          # some kind of function problem, return empty array
          # for the future: could do this for each data point individually
          retv = np.array([0 for i in x])
          
        # store results for future use
        self.x, self.y = x, retv
      
      return self.x, self.y
    
  def fitFunc(self, data={}, initpa=[], boundaryLow=[], boundaryHigh=[]):
    # ensure that boundaries are numpy arrays
    boundaryLow, boundaryHigh = np.array(boundaryLow), np.array(boundaryHigh)
    
    # check if ffunc is defined
    if (hasattr(FitObject, 'ffunc')):
      if (('x' in data) and ('y' in data)):
        # prepare fit
        maxfev = 100000
        # assign and check y error
        if('yerr' in data):
          sigma = data['yerr']
          sigma = [i if (i > 0) else self.EPSILON for i in sigma]
        else:
          sigma = [1] * len(data['y'])
          
        if(not len(initpa)):
          initpa = self.param
          
        # do the actual fit
        if('xerr' in data):
          useAlgorithm = 'odr'
          try:
            # generate wrapper function to refactor x and param vectors
            def odrWrapper(param, x):
              return self.ffunc(x, *param)
            # populate x errors
            sigmaX = data['xerr']
            sigmaX = [i if (i > 0) else self.EPSILON for i in sigmaX]
            # call odr
            odrData = odr.RealData(data['x'], data['y'], sigmaX, sigma)
            odrModel = odr.Model(odrWrapper)
            odrODR = odr.ODR(odrData, odrModel, beta0=initpa)
            odrODR.set_job(fit_type=2)
            odrOut = odrODR.run()
            fitpa, covar = odrOut.beta, odrOut.cov_beta
          except:
            # catch all kind of fit problems
            fitpa = initpa
            covar = np.zeros((len(fitpa), len(fitpa)))
        else:
          # determine whether we have boundaries
          useAlgorithm = 'lm'
          if(len(boundaryLow) or len(boundaryHigh)):
            if(len(boundaryLow[boundaryLow != -np.inf]) or len(boundaryHigh[boundaryHigh != np.inf])):
              # careful -- we need to check whether trf is supported
              try:
                # fake fit call
                optim.curve_fit(lambda y, p: y + 3 + p, [1, 2, 3], [0, -1, 2], [2], method='trf', bounds=(-10, 10))
                useAlgorithm = 'trf'
              except:
                self.parent.statusbar.showMessage('Trf fitting not supported on your platform, will switch to lm!', self.parent.STATUS_TIME)

          # do appropriate fit
          if(useAlgorithm == 'trf'):
            # we do have boundaries here
            # ensure that we start within bounds
            initpa = [min(max(init, lower), upper) for init, lower, upper in zip(initpa, boundaryLow, boundaryHigh)]
            try:
              fitpa, covar = optim.curve_fit(self.ffunc, data['x'], data['y'], initpa, sigma, maxfev=maxfev,\
                                             method=useAlgorithm, bounds=(boundaryLow, boundaryHigh))
            except:
              # catch all kind of fit problems
              fitpa = initpa
              covar = np.zeros((len(fitpa), len(fitpa)))
          else:
            # no boundaries, use Levenberg Marquardt
            try:
              # careful -- we need to check whether method argument is supported
              try:
                optim.curve_fit(lambda y, p: y + 3 + p, [1, 2, 3], [0, -1, 2], [2], method='lm')
                fitpa, covar = optim.curve_fit(self.ffunc, data['x'], data['y'], initpa, sigma, maxfev=maxfev, method=useAlgorithm)
              except:
                fitpa, covar = optim.curve_fit(self.ffunc, data['x'], data['y'], initpa, sigma, maxfev=maxfev)
            except:
              # catch all kind of fit problems
              fitpa = initpa
              covar = np.zeros((len(fitpa), len(fitpa)))

        # update parameters
        self.param = fitpa

        return True, fitpa, covar, useAlgorithm
      else:
        return False, [], [], ''
    else:
      self.parent.statusbar.showMessage('No fit function defined!', self.parent.STATUS_TIME)
      return False, [], [], ''

  def duplicateForSplit(self):
    # duplicates current curve and draws it on split y axis
    # delete all previous items (if any)
    self.removeFromSplit()

    if(self.onSecondAxes):
      axisobjectBase = 'ax'
    else:
      axisobjectBase = 'ax2'
    
    for handle in ['handlePlot', 'handlePlot_div']:
      if(self.__dict__[handle] != None):
        if(handle == 'handlePlot'):
          axisobject = self.parent.plotArea.__dict__[axisobjectBase]
        else:
          axisobject = self.parent.plotArea.__dict__[axisobjectBase + '_div']
        # better get x and y from curve to duplicate (can weed out nans and infs this way)
        xval, yval = self.__dict__[handle].get_xdata(), self.__dict__[handle].get_ydata()
        self.handlesBothAxes[handle], = axisobject.plot(xval, yval, 'ko', zorder=self.zorder + self.parent.zOffset)
        self.styleMe(handlePlot=self.handlesBothAxes[handle])
        # take care of object visibility
        self.handlesBothAxes[handle].set_visible(self.visibility)

    for handle in ['handleBoot', 'handleBoot_div']:
      if(self.__dict__[handle] != None):
        if(handle == 'handleBoot'):
          axisobject = self.parent.plotArea.__dict__[axisobjectBase]
        else:
          axisobject = self.parent.plotArea.__dict__[axisobjectBase + '_div']
        # retrieve data for plotting confidence band
        xv, yvlo, yvhi = self.retrieveConfidenceBand(splitX=(handle=='handleBoot_div'))
        self.handlesBothAxes[handle] = axisobject.fill_between(xv, yvlo, yvhi, zorder=self.zorder + self.parent.zOffset - .1)
        ###self.handlesBothAxes[handle].set_pickradius(PICK_TOLERANCE)
        self.styleMe(handleBoot=self.handlesBothAxes[handle])

  def removeFromSplit(self):
    # removes duplicated curve from split y axes
    for handle in self.handlesBothAxes:
      if(hasattr(self.handlesBothAxes[handle], 'remove')):
        self.handlesBothAxes[handle].remove()
    self.handlesBothAxes = {}

# the extras object is used to draw annotation and text on the canvas
class ExtrasObject(object):
  def __init__(self, parent=None):
    self.parent = parent
    self.parent.zcount += 1
    self.zorder = self.parent.zcount
    self.onSecondAxes = False

    self.name = ''
    self.visibility = True
    self.handle = None
    self.handle_div = None
    self.extrasType = 'text'
    self.x, self.y = 1, 1
    self.labeltext = 'text'
    self.color = [0.0, 0.0, 0.0, 1.0]
    self.fontsize = 12.0
    # set font according to last selected
    if(len(FontoBox.recentFonts)):
      self.fontname = FontoBox.recentFonts[0]
    else:
      self.fontname = 'DejaVu Sans'
    self.rotation = 0.0
    self.horizontalalignment = 'center'
    self.verticalalignment = 'center'
    self.fontweight = 'normal'
    self.fontstyle = 'normal'
    self.linespacing = 1.
    
    self.arrow__x, self.arrow__y = 2, 2
    self.arrow__arrowstyle = '->'
    self.arrow__head_length, self.arrow__head_width, self.arrow__tail_width = 0.4, 0.4, 0.2
    self.arrow__facecolor, self.arrow__edgecolor = [1.0, 1.0, 1.0, 0.7], [0.0, 0.0, 0.0, 1.0]
    self.arrow__linewidth, self.arrow__linestyle, self.arrow__dash_capstyle = 1.0, 'solid', 'butt'
    self.arrow__shrinkA, self.arrow__shrinkB = 5, 5
    self.arrow__lengthA, self.arrow__lengthB = 0.5, 0.5
    self.arrow__widthA, self.arrow__widthB = 0.5, 0.5
    self.arrow__connector = 'arc3'
    self.arrow__hatch = ''
    self.arrow__hatchMultiply = 1
    self.arrow__hatch_linewidth = 1.
    self.arrow__doFill = True
    
    self.bbox__show = True
    self.bbox__boxstyle = 'square'
    self.bbox__facecolor, self.bbox__edgecolor = [1.0, 1.0, 1.0, 0.7], [0.0, 0.0, 0.0, 1.0]
    self.bbox__linewidth, self.bbox__linestyle, self.bbox__dash_capstyle = 1.0, 'solid', 'butt'
    self.bbox__pad = 0.5
    self.bbox__hatch = ''
    self.bbox__hatchMultiply = 1
    self.bbox__hatch_linewidth = 1.
    self.bbox__tooth_size, self.bbox__rounding_size = 0.5, 0.5
    self.bbox__doFill = True
    
    self.x2, self.y2 = self.arrow__x, self.arrow__y
    self.line__linewidth = 1.0
    self.line__linestyle = 'solid'
    self.line__dash_capstyle = 'butt'
    self.line__solid_capstyle = 'butt'
    self.line__color = self.color
    self.line__gapcolor = [0, 0, .8, 1.]
    self.line__dogap = False

    self.shape__type = 'rect'
    self.shape__facecolor = [1.0, 1.0, 1.0, 1.0]
    self.shape__hatch = ''
    self.shape__hatchMultiply = 1
    self.shape__hatch_linewidth = 1.
    self.shape__numSpokes = 5
    self.shape__spikiness = 0.6
    self.shape__doFill = True
    self.shape__innerRadius = 0.5
    self.shape__pie = 0.5
    self.shape__vertices = []
    self.shape__closed = False
    self.shape__dx, self.shape__dy, self.shape__ind = 0.5, 0.5, 0.0
    
    self.area__x, self.area__y1, self.area__y2 = [], [], []
    
    # extra handle for drawing line when interactively modifying annotation object
    self.handleMarkerLine = None

    # stuff for split axes
    self.onBothAxes = False
    self.handlesBothAxes = {}

  def reportState(self):
    # reports data content for saveState function
    items = 'name,zorder,extrasType,x,y,labeltext,color,fontsize,fontname,linespacing,rotation,horizontalalignment,verticalalignment,fontweight,fontstyle'
    items += ',arrow__x,arrow__y,arrow__facecolor,arrow__edgecolor,arrow__linewidth,arrow__linestyle'
    items += ',arrow__shrinkA,arrow__shrinkB,arrow__arrowstyle,arrow__dash_capstyle,arrow__connector,arrow__hatch,arrow__hatchMultiply,arrow__hatch_linewidth,arrow__doFill'
    items += ',arrow__lengthA,arrow__lengthB,arrow__widthA,arrow__widthB'
    items += ',bbox__show,bbox__boxstyle,bbox__facecolor,bbox__edgecolor'
    items += ',bbox__linewidth,bbox__linestyle,bbox__dash_capstyle,bbox__pad,bbox__hatch,bbox__hatchMultiply,bbox__hatch_linewidth,bbox__tooth_size,bbox__rounding_size,bbox__doFill'
    items += ',x2,y2,line__linewidth,line__linestyle,line__dash_capstyle,line__solid_capstyle,line__color,line__gapcolor,line__dogap'
    items += ',shape__type,shape__facecolor,shape__hatch,shape__hatchMultiply,shape__hatch_linewidth,shape__numSpokes,shape__spikiness,shape__doFill,shape__innerRadius,shape__pie,shape__vertices,shape__closed,shape__dx,shape__dy,shape__ind,onSecondAxes,onBothAxes'
    items += ',area__x,area__y1,area__y2'
    items = items.split(',')
    retv = {}
    
    for entry in items:
      if(hasattr(self, entry)):
        value = self.__dict__[entry]
        if(type(value) == type(np.array([]))):
          value = value.tolist()
        retv[entry] = value
    
    return retv

  def restoreState(self, data={}, zoffset=0):
    # restores data content for loadState function
    for entry in data:
      if(hasattr(self, entry)):
        if(entry == 'zorder'):
          value = data[entry] + zoffset
        else:
          value = data[entry]
        self.__dict__[entry] = value

  def drawMe(self, redraw=True):
    # delete previous object if present
    if(self.handle != None):
      self.handle.remove()
    if(self.handle_div != None):
      self.handle_div.remove()
      
    # call helper function to enable drawing of extras for duplication
    self.handle, self.handle_div = self.drawMeHelper(redraw=redraw, duplicate=False)
      
  def drawMeHelper(self, redraw=True, duplicate=False):
    # determine which axes to draw on
    if(self.onSecondAxes):
      if(duplicate):
        useAxes, useAxes2 = self.parent.plotArea.ax, self.parent.plotArea.ax_div
      else:
        useAxes, useAxes2 = self.parent.plotArea.ax2, self.parent.plotArea.ax2_div
    elif(duplicate):
      useAxes, useAxes2 = self.parent.plotArea.ax2, self.parent.plotArea.ax2_div
    else:
      useAxes, useAxes2 = self.parent.plotArea.ax, self.parent.plotArea.ax_div
    
    # treat lines differently
    if(self.extrasType == 'line'):
      handle, = useAxes.plot([self.x, self.x2], [self.y, self.y2])
      handle.set_pickradius(PICK_TOLERANCE)
      handle.set_marker('None')
      handle_div, = useAxes2.plot([self.x, self.x2], [self.y, self.y2])
      handle.set_pickradius(PICK_TOLERANCE)
      handle_div.set_marker('None')
      
      # apply styles
      # check for potential problems arising from zero line width
      if((self.line__linestyle != 'solid') and (self.line__linewidth < .01)):
        self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
        self.line__linestyle = 'solid'
      for entry in ['linewidth', 'linestyle', 'color', 'dash_capstyle', 'solid_capstyle', 'gapcolor']:
        if(hasattr(handle, 'set_' + entry)):
          method2call = getattr(handle, 'set_' + entry)
          method2call(self.__dict__['line__' + entry])
        if(hasattr(handle_div, 'set_' + entry)):
          method2call = getattr(handle_div, 'set_' + entry)
          method2call(self.__dict__['line__' + entry])
      
      if(hasattr(handle, 'set_gapcolor') and (not self.line__dogap)):
        handle.set_gapcolor('none')
        
      # treat z order separately
      handle.set_zorder(self.zorder + self.parent.zOffset)
      handle_div.set_zorder(self.zorder + self.parent.zOffset)

      # heed visibility
      handle.set_visible(self.visibility)
      handle_div.set_visible(self.visibility)
      if(redraw):
        self.parent.plotArea.dataplotwidget.myRefresh()
    elif(self.extrasType == 'shape'):
      # generate shape
      if(self.shape__type == 'rect'):
        useX, useY = self.x - self.x2 / 2.0, self.y - self.y2 / 2.0
        useWidth, useHeight = self.x2, self.y2
        # generate default rectangle
        extraPatch = matplotlib.patches.Rectangle(xy=(-0.5, -0.5), width=1.0, height=1.0)
        extraPatch2 = matplotlib.patches.Rectangle(xy=(-0.5, -0.5), width=1.0, height=1.0)
        # apply transforms (needed b/c rectangle rotates around bottom left corner, not center)
        transform, transform2 = self.masterTransform(useX, useY, useWidth, useHeight, self.rotation, duplicate=duplicate)
        extraPatch.set_transform(transform)
        extraPatch2.set_transform(transform2)
      elif(self.shape__type == 'circle'):
        useX, useY = self.x, self.y
        useWidth, useHeight = self.x2, self.y2
        extraPatch = matplotlib.patches.Ellipse(xy=(useX, useY), width=useWidth, height=useHeight, angle=self.rotation)
        extraPatch2 = matplotlib.patches.Ellipse(xy=(useX, useY), width=useWidth, height=useHeight, angle=self.rotation)
      elif(self.shape__type == 'poly'):
        useX, useY = self.x, self.y
        useWidth, useHeight = self.x2, self.y2
        # generate regular polygon
        extraPatch = matplotlib.patches.RegularPolygon(xy=(-0.5, -0.5), numVertices=self.shape__numSpokes, radius=0.5)
        extraPatch2 = matplotlib.patches.RegularPolygon(xy=(-0.5, -0.5), numVertices=self.shape__numSpokes, radius=0.5)
        # apply transforms
        transform, transform2 = self.masterTransform(useX, useY, useWidth, useHeight, self.rotation, duplicate=duplicate)
        extraPatch.set_transform(transform)
        extraPatch2.set_transform(transform2)
      elif(self.shape__type == 'freehand'):
        if(len(self.shape__vertices)):
          ### mayhaps implement this in a future version but not for now
          ###codes = len(self.shape__vertices) * [matplotlib.path.Path.CURVE4]
          ###codes[0] = matplotlib.path.Path.MOVETO
          ###codes[-1] = matplotlib.path.Path.STOP
          codes = None
          lassoPath = matplotlib.path.Path(self.shape__vertices, codes=codes, closed=self.shape__closed)
          extraPatch = matplotlib.patches.PathPatch(lassoPath)
          extraPatch2 = matplotlib.patches.PathPatch(lassoPath)

          # apply transforms
          useX, useY, useWidth, useHeight = self.x, self.y, self.x2, self.y2
          transform, transform2 = self.masterTransform(useX, useY, useWidth, useHeight, self.rotation, duplicate=duplicate)
          extraPatch.set_transform(transform)
          extraPatch2.set_transform(transform2)
      elif(self.shape__type == 'arrow'):
        useX, useY = self.x, self.y
        useWidth, useHeight = self.x2, self.y2
        # generate default rectangle
        extraPatch = self.arrowShape(xy=(-0.5, -0.5), width=1., height=1., dx=self.shape__dx, dy=self.shape__dy, ind=self.shape__ind)
        extraPatch2 = self.arrowShape(xy=(-0.5, -0.5), width=1., height=1., dx=self.shape__dx, dy=self.shape__dy, ind=self.shape__ind)
        # apply transforms (needed b/c rectangle rotates around bottom left corner, not center)
        transform, transform2 = self.masterTransform(useX, useY, useWidth, useHeight, self.rotation, duplicate=duplicate)
        extraPatch.set_transform(transform)
        extraPatch2.set_transform(transform2)
      else:
        # use star shape as default
        useX, useY = self.x, self.y
        useWidth, useHeight = self.x2, self.y2
        if(self.shape__type == 'star'):
          extraPatch = self.pointyStar(xy=(-0.5, -0.5), numVertices=self.shape__numSpokes, radius=0.5, ratio=self.shape__spikiness)
          extraPatch2 = self.pointyStar(xy=(-0.5, -0.5), numVertices=self.shape__numSpokes, radius=0.5, ratio=self.shape__spikiness)
        else:
          extraPatch = self.torus(xy=(-0.5, -0.5), radius=0.5, innerRadius=self.shape__innerRadius, pie=self.shape__pie)
          extraPatch2 = self.torus(xy=(-0.5, -0.5), radius=0.5, innerRadius=self.shape__innerRadius, pie=self.shape__pie)
        if(extraPatch != None):
          # apply transforms
          transform, transform2 = self.masterTransform(useX, useY, useWidth, useHeight, self.rotation, duplicate=duplicate)
          extraPatch.set_transform(transform)
          extraPatch2.set_transform(transform2)

      if(extraPatch != None):
        handle = useAxes.add_patch(extraPatch)
      if(extraPatch2 != None):
        handle_div = useAxes2.add_patch(extraPatch2)
      
      # apply line styles
      # check for potential problems arising from zero line width
      if((self.line__linestyle != 'solid') and (self.line__linewidth < .01)):
        self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
        self.line__linestyle = 'solid'
      for entry in ['linewidth', 'linestyle', 'color', 'capstyle', 'solid_capstyle']:
        if(hasattr(handle, 'set_' + entry)):
          method2call = getattr(handle, 'set_' + entry)
          if(entry == 'capstyle'):
            method2call(self.__dict__['line__dash_capstyle'])
          else:
            method2call(self.__dict__['line__' + entry])
        if(hasattr(handle_div, 'set_' + entry)):
          method2call = getattr(handle_div, 'set_' + entry)
          if(entry == 'capstyle'):
            method2call(self.__dict__['line__dash_capstyle'])
          else:
            method2call(self.__dict__['line__' + entry])

      # apply fill styles
      for entry in ['hatch', 'facecolor', 'hatch_linewidth']:
        if(hasattr(handle, 'set_' + entry)):
          method2call = getattr(handle, 'set_' + entry)
          if(entry == 'hatch'):
            hatchString = self.shape__hatch * self.shape__hatchMultiply
            method2call(hatchString)
          else:
            method2call(self.__dict__['shape__' + entry])
        if(hasattr(handle_div, 'set_' + entry)):
          method2call = getattr(handle_div, 'set_' + entry)
          if(entry == 'hatch'):
            hatchString = self.shape__hatch * self.shape__hatchMultiply
            method2call(hatchString)
          else:
            method2call(self.__dict__['shape__' + entry])
            
      # check whether to fill shape
      if(not self.shape__doFill):
        handle.set_facecolor('none')
        handle_div.set_facecolor('none')

      # treat z order separately
      handle.set_zorder(self.zorder + self.parent.zOffset)
      handle_div.set_zorder(self.zorder + self.parent.zOffset)

      # heed visibility
      handle.set_visible(self.visibility)
      handle_div.set_visible(self.visibility)
      if(redraw):
        self.parent.plotArea.dataplotwidget.myRefresh()
    elif(self.extrasType == 'area'):
      if(len(self.area__x)):
        handle = useAxes.fill_between(self.area__x, self.area__y1, self.area__y2, zorder=self.zorder + self.parent.zOffset)
        handle_div = useAxes2.fill_between(self.area__x, self.area__y1, self.area__y2, zorder=self.zorder + self.parent.zOffset)
        ####print(dir(handle), self.shape__type, self.x, self.y, self.x2, self.y2)
        # apply transforms
        useX, useY = self.x, self.y
        useWidth, useHeight = self.x2 / (np.max(self.area__x) - np.min(self.area__x)), self.y2 / (np.max((self.area__y1, self.area__y2)) - np.min((self.area__y1, self.area__y2)))
        ###print(useX, useY, useWidth, useHeight)
        transform, transform2 = self.masterTransform(useX, useY, useWidth, useHeight, self.rotation, duplicate=duplicate)
        handle.set_transform(transform)
        handle_div.set_transform(transform2)
        
        # apply styles (hijack bbox styles)
        if(self.bbox__hatch == ''):
          hatchString = self.bbox__hatch
        else:
          hatchString = self.bbox__hatch * self.bbox__hatchMultiply
          
        # check for potential problems arising from zero line width
        if((self.bbox__linestyle != 'solid') and (self.bbox__linewidth < .01)):
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          self.bbox__linestyle = 'solid'
        for useHandle in [handle, handle_div]:
          for item in ['linewidth', 'linestyle', 'edgecolor', 'facecolor', 'hatch', 'hatch_linewidth']:
            if(hasattr(useHandle, 'set_' + item)):
              method2call = getattr(useHandle, 'set_' + item)
              if(item == 'hatch'):
                method2call(hatchString)
              elif((item == 'facecolor') and (not self.bbox__doFill)):
                method2call('none')
              else:
                method2call(self.__dict__['bbox__' + item])

      # heed visibility
      handle.set_visible(self.visibility)
      handle_div.set_visible(self.visibility)
      if(redraw):
        self.parent.plotArea.dataplotwidget.myRefresh()
    else:
      # prepare bbox
      if(self.bbox__show):
        if(self.bbox__hatch == ''):
          hatchString = self.bbox__hatch
        else:
          hatchString = self.bbox__hatch * self.bbox__hatchMultiply
        # implement check to account for different bbox styles on different operating systems
        availableBoxStyles = list(matplotlib.patches.BoxStyle.get_styles().keys())
        if(not(self.bbox__boxstyle in availableBoxStyles)):
          self.bbox__boxstyle = 'square'
        
        # check whether to fill box
        if(self.bbox__doFill):
          useFaceColor = self.bbox__facecolor
        else:
          useFaceColor = 'none'
        # check for potential problems arising from zero line width
        if((self.bbox__linestyle != 'solid') and (self.bbox__linewidth < .01)):
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          self.bbox__linestyle = 'solid'
        # need to implement pad and rounding_size and tooth_size
        bboxProps = {'boxstyle': self.bbox__boxstyle, 'facecolor': useFaceColor, 'edgecolor': self.bbox__edgecolor,\
                     'linewidth': self.bbox__linewidth, 'linestyle': self.bbox__linestyle, 'hatch': hatchString, 'hatch_linewidth': self.bbox__hatch_linewidth, 'capstyle': self.bbox__dash_capstyle}
        #bboxProps['pad'] = self.bbox__pad  # this effing will not work under Linux
        bboxProps['boxstyle'] = bboxProps['boxstyle'] + ',pad=' + str(self.bbox__pad)  # this will work under Linux as well
        # create extra bboxProps without hatch_linewidth
        bboxProps2 = deepcopy(bboxProps)
        del bboxProps2['hatch_linewidth']
        
      # causes extras to be drawn on canvas
      if(self.extrasType == 'text'):
        if(self.bbox__show):
          # older Python versions do not understand antialiased argument
          try:
            handle = useAxes.text(self.x, self.y, self.labeltext, bbox=bboxProps, rotation_mode='anchor', antialiased=False)
            handle_div = useAxes2.text(self.x, self.y, self.labeltext, bbox=bboxProps, rotation_mode='anchor', antialiased=False)
          except:
            try:
              handle = useAxes.text(self.x, self.y, self.labeltext, bbox=bboxProps, rotation_mode='anchor')
              handle_div = useAxes2.text(self.x, self.y, self.labeltext, bbox=bboxProps, rotation_mode='anchor')
            except:
              handle = useAxes.text(self.x, self.y, self.labeltext, bbox=bboxProps2, rotation_mode='anchor')
              handle_div = useAxes2.text(self.x, self.y, self.labeltext, bbox=bboxProps2, rotation_mode='anchor')
          handle.set_clip_on(True)
          handle_div.set_clip_on(True)
          # apply bbox properties post creation
          styleObject = handle.get_bbox_patch().get_boxstyle()
          boxstyleExtension = ''
          for entry in ['tooth_size', 'rounding_size', 'pad']:
            if(hasattr(styleObject, entry)):
              boxstyleExtension += ',' + entry + '=' + str(self.__dict__['bbox__' + entry])
          bboxProps['boxstyle'] = self.bbox__boxstyle + boxstyleExtension
          handle.get_bbox_patch().set_boxstyle(bboxProps['boxstyle'])
          # and do this for handle_div
          handle_div.get_bbox_patch().set_boxstyle(bboxProps['boxstyle'])
        else:
          handle = useAxes.text(self.x, self.y, self.labeltext)
          handle_div = useAxes2.text(self.x, self.y, self.labeltext)
      else:
        # implement check for quadratic connector
        safeConnector = 'arc3'
        if(self.arrow__arrowstyle in ['fancy', 'simple', 'wedge']):
          if(not(self.arrow__connector in ['arc3', 'angle3'])):
            self.parent.statusbar.showMessage('Connector style ' + self.arrow__connector + ' incompatible with ' + self.arrow__arrowstyle + '. Reverting to ' + safeConnector + '!', self.parent.STATUS_TIME)
            self.arrow__connector = safeConnector
  
        # check whether to fill arrow
        if(self.arrow__doFill):
          useFaceColor = self.arrow__facecolor
        else:
          useFaceColor = 'none'
        # check for potential problems arising from zero line width
        if((self.arrow__linestyle != 'solid') and (self.arrow__linewidth < .01)):
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          self.arrow__linestyle = 'solid'
        # draw annotation (unfortunately, matplotlib hard-codes 'round' capstyle, so this setting won't do anything)
        arrowProps = {'facecolor': useFaceColor, 'arrowstyle': self.arrow__arrowstyle,\
                      'edgecolor': self.arrow__edgecolor, 'linewidth': self.arrow__linewidth, 'linestyle': self.arrow__linestyle,\
                      'capstyle': self.arrow__dash_capstyle, 'shrinkA': self.arrow__shrinkA, 'shrinkB': self.arrow__shrinkB,\
                      'connectionstyle': self.arrow__connector}#, 'head_length': self.arrow__head_length}
        if(self.bbox__show):
          try:
            handle = useAxes.annotate(self.labeltext, xytext=(self.x, self.y), xy=(self.arrow__x, self.arrow__y), arrowprops=arrowProps, bbox=bboxProps, rotation_mode='anchor')
            handle_div = useAxes2.annotate(self.labeltext, xytext=(self.x, self.y), xy=(self.arrow__x, self.arrow__y), arrowprops=arrowProps, bbox=bboxProps, rotation_mode='anchor')
          except:
            handle = useAxes.annotate(self.labeltext, xytext=(self.x, self.y), xy=(self.arrow__x, self.arrow__y), arrowprops=arrowProps, bbox=bboxProps2, rotation_mode='anchor')
            handle_div = useAxes2.annotate(self.labeltext, xytext=(self.x, self.y), xy=(self.arrow__x, self.arrow__y), arrowprops=arrowProps, bbox=bboxProps2, rotation_mode='anchor')
          # apply bbox properties post creation
          styleObject = handle.get_bbox_patch().get_boxstyle()
          boxstyleExtension = ''
          for entry in ['tooth_size', 'rounding_size', 'pad']:
            if(hasattr(styleObject, entry)):
              boxstyleExtension += ',' + entry + '=' + str(self.__dict__['bbox__' + entry])
          bboxProps['boxstyle'] = self.bbox__boxstyle + boxstyleExtension
          handle.get_bbox_patch().set_boxstyle(bboxProps['boxstyle'])
          handle_div.get_bbox_patch().set_boxstyle(bboxProps['boxstyle'])
        else:
          handle = useAxes.annotate(self.labeltext, xytext=(self.x, self.y), xy=(self.arrow__x, self.arrow__y), arrowprops=arrowProps)
          handle_div = useAxes2.annotate(self.labeltext, xytext=(self.x, self.y), xy=(self.arrow__x, self.arrow__y), arrowprops=arrowProps)
        
        # apply properties to annotation
        if(self.arrow__hatch == ''):
          hatchString = self.arrow__hatch
        else:
          hatchString = self.arrow__hatch * self.arrow__hatchMultiply
        handle.arrow_patch.set_hatch(hatchString)
        handle_div.arrow_patch.set_hatch(hatchString)
        
        # apply arrow properties post creation
        goodProperties = []
        styleObject = handle.arrow_patch.get_arrowstyle()
        self.arrow__head_width, self.arrow__head_length = self.arrow__widthB, self.arrow__lengthB
        self.arrow__tail_width = self.arrow__widthA
        properties = 'arrow__lengthA,arrow__lengthB,arrow__widthA,arrow__widthB,arrow__head_width,arrow__head_length,arrow__tail_width'
        for entry in properties.split(','):
          workArrowStyle = self.arrow__arrowstyle
          currProp = entry.split('__')[-1]
          if(hasattr(styleObject, currProp)):
            workArrowStyle += ', ' + currProp + '=' + str(self.__dict__[entry])
            # try setting this property to thus check wether current style accepts it (this is really ugly but matplotlib wants it like this, grrrrrrrrr)
            try:
              handle.arrow_patch.set_arrowstyle(workArrowStyle)
              handle_div.arrow_patch.set_arrowstyle(workArrowStyle)
              goodProperties.append(entry)
            except:
              # suck it up and do nothing
              pass
        # check whether any property survived
        if(len(goodProperties)):
          workArrowStyle = self.arrow__arrowstyle
          for entry in goodProperties:
            currProp = entry.split('__')[-1]
            workArrowStyle += ', ' + currProp + '=' + str(self.__dict__[entry])
          # throw in another try for safety's measure
          try:
            handle.arrow_patch.set_arrowstyle(workArrowStyle)
            handle_div.arrow_patch.set_arrowstyle(workArrowStyle)
          except:
            pass
  
      # set visibility
      if (handle != None):
        handle.set_visible(self.visibility)
        
        styleItems = 'color,fontsize,fontname,linespacing,rotation,horizontalalignment,verticalalignment,fontweight,fontstyle'.split(',')
        for entry in styleItems:
          if((hasattr(handle, 'set_' + entry)) and (hasattr(self, entry))):
            method2call = getattr(handle, 'set_' + entry)
            if((entry == 'verticalalignment') and (self.__dict__[entry] == 'center')):
              try:
                method2call('center_baseline')
              except:
                method2call('center')
            else:
              method2call(self.__dict__[entry])
          if((handle_div != None) and (hasattr(handle_div, 'set_' + entry)) and (hasattr(self, entry))):
            method2call = getattr(handle_div, 'set_' + entry)
            if((entry == 'verticalalignment') and (self.__dict__[entry] == 'center')):
              try:
                method2call('center_baseline')
              except:
                method2call('center')
            else:
              method2call(self.__dict__[entry])
        
        # treat z order separately
        handle.set_zorder(self.zorder + self.parent.zOffset)
        if(handle_div != None):
          handle_div.set_visible(self.visibility)
          handle_div.set_zorder(self.zorder + self.parent.zOffset)
  
        # process text and check for bad math text
        try:
          handle._get_layout(self.parent.plotArea.matplot.canvas.renderer)
          if(handle_div != None):
            handle_div._get_layout(self.parent.plotArea.matplot.canvas.renderer)
        except:
          self.parent.statusbar.showMessage('Problems setting label to ' + self.labeltext + '!', self.parent.STATUS_TIME)
          self.labeltext = self.labeltext.replace('$', '')
        
        self.labeltext = self.labeltext.replace('\n', '\\n')
        self.labeltext = self.labeltext.replace('\t', '\\t')
        self.labeltext = '\n'.join([i for i in self.labeltext.split('\\n') if (len(i.strip()))])
        self.labeltext = '\t'.join([i for i in self.labeltext.split('\\t') if (len(i.strip()))])
        handle.set_text(self.labeltext)
        if(handle_div != None):
          handle_div.set_text(self.labeltext)
  
        # need to implement extra check for fontname due to erroneous fonts
        try:
          if(redraw):
            self.parent.plotArea.dataplotwidget.myRefresh()
        except:
          ###safeFont = 'DejaVu Sans'
          self.parent.statusbar.showMessage('Experiencing problems with font ' + self.fontname + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
          self.fontname = SAFE_FONT
          handle.set_fontname(SAFE_FONT)
          if(handle_div != None):
            handle_div.set_fontname(SAFE_FONT)
  
          if(redraw):
            self.parent.plotArea.dataplotwidget.myRefresh()
        
    # return handles
    return handle, handle_div

  def tempTransparency(self):
    # helper function for interactive modification of extras
    # temp. set extra to semi-transparent fill
    handles = [self.handle, self.handle_div]
    # also consider splitY objects
    for extraHandle in ['handle', 'handle_div']:
      if(extraHandle in self.handlesBothAxes):
        handles.append(self.handlesBothAxes[extraHandle])
    for handle in handles:
      if(hasattr(handle, 'set_facecolor')):
        if(self.extrasType == 'area'):
          fc = self.bbox__facecolor
        else:
          fc = self.shape__facecolor
        if(not (fc[-1] < 1)):
          handle.set_facecolor(fc[:-1] + [0.5])

  def resetTransform(self, redraw=True):
    # resets all transformations on an area object
    xmin, ymin = np.min(self.area__x), np.min((self.area__y1, self.area__y2))
    width, height = np.max(self.area__x) - xmin, np.max((self.area__y1, self.area__y2)) - ymin
    xmid, ymid = xmin + width / 2., ymin + height / 2.
    self.x, self.y, self.x2, self.y2, self.rotation = xmid, ymid, width, height, 0.
    self.drawMe(redraw=redraw)

  def masterTransform(self, useX, useY, useWidth, useHeight, useRotation, duplicate=False):
    # helper function that calculates transforms for extras object
    # needed to outsource it to here as also required by updateMe()
    if(self.onSecondAxes):
      if(duplicate):
        useAxes, useAxes2 = self.parent.plotArea.ax, self.parent.plotArea.ax_div
      else:
        useAxes, useAxes2 = self.parent.plotArea.ax2, self.parent.plotArea.ax2_div
    elif(duplicate):
      useAxes, useAxes2 = self.parent.plotArea.ax2, self.parent.plotArea.ax2_div
    else:
      useAxes, useAxes2 = self.parent.plotArea.ax, self.parent.plotArea.ax_div

    if(self.extrasType == 'area'):
      realx, realy = np.min(self.area__x), np.min((self.area__y1, self.area__y2))
      realWidth, realHeight = np.max(self.area__x) - realx, np.max((self.area__y1, self.area__y2)) - realy
      transform = matplotlib.transforms.Affine2D().translate(tx=-realx - realWidth / 2.0, ty=-realy - realHeight / 2.0)
      transform += matplotlib.transforms.Affine2D().scale(sx=useWidth, sy=useHeight)
      transform += matplotlib.transforms.Affine2D().rotate_deg(useRotation)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX, ty=useY)
    elif(self.shape__type == 'rect'):
      # apply transforms (needed b/c rectangle rotates around bottom left corner, not center)
      transform = matplotlib.transforms.Affine2D().scale(sx=useWidth, sy=useHeight)
      transform += matplotlib.transforms.Affine2D().rotate_deg(useRotation)
      transform += matplotlib.transforms.Affine2D().translate(tx=useWidth / 2.0, ty=useHeight / 2.0)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX, ty=useY)
    elif(self.shape__type == 'circle'):
      # return matrix for circle (first return to origin before scaling)
      transform = matplotlib.transforms.Affine2D().translate(tx=-self.x, ty=-self.y)
      transform += matplotlib.transforms.Affine2D().scale(sx=useWidth, sy=useHeight)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX + self.x, ty=useY + self.y)
    elif(self.shape__type == 'poly'):
      # transformation for polygon
      transform = matplotlib.transforms.Affine2D().scale(sx=useWidth, sy=useHeight)
      transform += matplotlib.transforms.Affine2D().translate(tx=useWidth / 2.0, ty=useHeight / 2.0)
      transform += matplotlib.transforms.Affine2D().rotate_deg(useRotation)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX, ty=useY)
    elif(self.shape__type == 'arrow'):
      # transformation for polygon
      transform = matplotlib.transforms.Affine2D().scale(sx=useWidth, sy=useHeight)
      transform += matplotlib.transforms.Affine2D().translate(tx=useWidth / 2.0, ty=useHeight / 2.0)
      transform += matplotlib.transforms.Affine2D().rotate_deg(useRotation)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX, ty=useY)
    elif(self.shape__type == 'freehand'):
      # transformations for freehand
      intrinsicX, intrinsicY, intrinsicWidth, intrinsicHeight = self.getFreehandDimensions()
      try:
        sx = useWidth / intrinsicWidth
      except:
        sx = 1
      try:
        sy = useHeight / intrinsicHeight
      except:
        sy = 1
      transform = matplotlib.transforms.Affine2D().scale(sx=sx, sy=sy)
      transform += matplotlib.transforms.Affine2D().rotate_deg(useRotation)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX - intrinsicX, ty=useY - intrinsicY)
    else:
      # use star shape and torus as default
      transform = matplotlib.transforms.Affine2D().scale(sx=useWidth, sy=useHeight)
      transform += matplotlib.transforms.Affine2D().rotate_deg(useRotation)
      transform += matplotlib.transforms.Affine2D().translate(tx=useX, ty=useY)

    transform2 = transform + useAxes2.transData
    transform += useAxes.transData

    return transform, transform2

  def torus(self, xy=(0, 0), radius=1, innerRadius=0.5, pie=1):
    # draws a toroidal shape
    def drawCircle(r):
      # copied from Matplotlib documentation
      t = np.arange(0, np.pi * 2.0 * pie, 0.01)
      t = t.reshape((len(t), 1))
      x = r * np.cos(t)
      y = r * np.sin(t)
      return np.hstack((x, y))

    # calculate coordinates for inside and outside coordinates
    outside = drawCircle(radius)
    inside = drawCircle(radius * innerRadius)

    # prepare path coordinates
    codes = [matplotlib.path.Path.LINETO for i in range(len(outside))]
    if(pie < 1):
      codes.extend(codes)
      codes[0] = matplotlib.path.Path.MOVETO
    else:
      codes[0] = matplotlib.path.Path.MOVETO
      codes.extend(codes)

    # calculate the path
    coords = np.concatenate((outside, inside[::-1]))
    if(pie < 1):
      codes.append(matplotlib.path.Path.LINETO)
      coords = np.concatenate((coords, outside[:1]))
    path = matplotlib.path.Path(coords, codes)

    # draw the shape
    extrasPatch = matplotlib.patches.PathPatch(path)
    return extrasPatch

  def pointyStar(self, xy=(0, 0), numVertices=5, radius=1, ratio=0.7):
    # draws a pointy star with arbitrary spokes
    if(numVertices > 2):
      # calculate the coords
      coords = []
      angles = np.linspace(0, 2.0 * np.pi, numVertices + 1)
      for index, entry in enumerate(angles):
        coords.append([radius * np.sin(entry), radius * np.cos(entry)])
        if(index < len(angles) - 1):
          coords.append([ratio * radius * np.sin((entry + angles[index + 1]) / 2.0), ratio * radius * np.cos((entry + angles[index + 1]) / 2.0)])
        
      # draw the shape
      extrasPatch = matplotlib.patches.Polygon(xy=coords, closed=True)
      return extrasPatch

    return None

  def arrowShape(self, xy=(0, 0), width=1, height=1, dx=0.5, dy=0.5, ind=0.5):
    # draws an arrow shape
    try:
      x, y = xy
    except:
      return None
    else:
      coords = []
      coords.append([x - width / 2., y - dy * height / 2.])
      coords.append([x + (dx - .5) * width, y - dy * height / 2.])
      coords.append([x + (dx - .5) * width, y - height / 2.])
      coords.append([x + width / 2., y])
      coords.append([x + (dx - .5) * width, y + height / 2.])
      coords.append([x + (dx - .5) * width, y + dy * height / 2.])
      coords.append([x - width / 2., y + dy * height / 2.])
      coords.append([x + (ind - .5) * width, y])
    
      # draw the shape
      extrasPatch = matplotlib.patches.Polygon(xy=coords, closed=True)
      return extrasPatch

  def setValues(self, valueDict, redraw=True):
    # sets values of the extras object
    tempRedraw = False
    if(type(valueDict) == type({})):
      keys = valueDict.keys()
      for entry in keys:
        if(hasattr(self, entry)):
          # implement additional check for type equality to prevent broadcast error under later Python version
          if(type(self.__dict__[entry]) != type(valueDict[entry])):
            tempRedraw =True
          elif(self.__dict__[entry] != valueDict[entry]):
            tempRedraw = True
          self.__dict__[entry] = valueDict[entry]
          
      # check whether we updated vertices => need to initialize coordinates, width and height
      if(('shape__vertices' in keys) and (self.shape__type == 'freehand')):
        deltaX, deltaY, self.x2, self.y2 = self.getFreehandDimensions()
        self.shape__vertices = self.shiftFreehand(-deltaX, -deltaY)
        self.x, self.y = deltaX, deltaY
          
      if(redraw and tempRedraw):
        self.drawMe(redraw=redraw)

  def getFreehandDimensions(self):
    # returns center position and height and width of freehand shape
    if(len(self.shape__vertices)):
      tempVertices = np.array(self.shape__vertices)
      xmin, xmax = np.min(tempVertices[:, 0]), np.max(tempVertices[:, 0])
      ymin, ymax = np.min(tempVertices[:, 1]), np.max(tempVertices[:, 1])
      x, y = (xmax + xmin) / 2., (ymax + ymin) / 2.
      width, height = np.abs(xmax - xmin), np.abs(ymax - ymin)
      return x, y, width, height
    else:
      return 0, 0, 1, 1

  def shiftFreehand(self, deltaX, deltaY):
    # moves freehand shape by deltaX and deltaY
    if(len(self.shape__vertices)):
      tempVertices = np.array(self.shape__vertices)
      tempVertices[:, 0] += deltaX
      tempVertices[:, 1] += deltaY
      return tempVertices
    else:
      return []
    
  def simplifyMe(self, redraw=True):
    # reduces number of vertices and hence simplifies freehand object
    windowSize = 5 # should be at least 3
    if(len(self.shape__vertices)):
      # check for length of vertex list
      if(len(self.shape__vertices) <= windowSize):
        self.parent.statusbar.showMessage('Warning -- Cannot simplify shape any further!', self.parent.STATUS_TIME, color='blue')
      else:
        nuVertices = []
        oldBatchPos = [i / windowSize for i in range(windowSize - 1)]
        newBatchPos = [i / windowSize for i in range(windowSize - 2)]
        currpos = 0
        while(currpos + windowSize <= len(self.shape__vertices)):
          for index, value in enumerate(newBatchPos):
            # determine fraction for calculating new coordinates
            fraction = (oldBatchPos[index + 1] - value) / (oldBatchPos[index + 1] - oldBatchPos[index])
            x1, y1 = self.shape__vertices[currpos + index]
            x2, y2 = self.shape__vertices[currpos + index + 1]
            nuVertices.append([x1 * fraction + (1 - fraction) * x2, y1 * fraction + (1 - fraction) * y2])
          currpos += windowSize - 1
          
        # append final vertices
        nuVertices = np.vstack((nuVertices, self.shape__vertices[currpos:]))
        ###nuVertices.extend(self.shape__vertices[currpos:])
        self.shape__vertices = nuVertices
  
        if(redraw):
          self.drawMe(redraw=redraw)

  def setBothAxes(self, state=False, redraw=True):
    # moves plot between axes
    self.onBothAxes = state
    # remove surplus items?
    if(self.onBothAxes):
      self.duplicateForSplit()
    else:
      self.removeFromSplit()

  def setSecondAxes(self, state=False, redraw=True):
    # moves extra between axes
    self.onSecondAxes = state
    # check whether dual display is turned on
    if(self.parent.plotArea.splitY and self.onBothAxes):
      # we can just swap the handles
      for item in ['handle', 'handle_div']:
        if(item in self.handlesBothAxes):
          self.__dict__[item], self.handlesBothAxes[item] = self.handlesBothAxes[item], self.__dict__[item]
      return
    # update plot
    self.drawMe(redraw=redraw)
          
  def setVisibility(self, state=True, redraw=True):
    # toggles visibility of extra
    self.visibility = state
    updateFlag = False
    if(self.handle != None):
      self.handle.set_visible(state)
      if(self.parent.plotArea.splitY and ('handle' in self.handlesBothAxes)):
        self.handlesBothAxes['handle'].set_visible(state)
      updateFlag = True
    if(self.handle_div != None):
      self.handle_div.set_visible(state)
      if(self.parent.plotArea.splitY and ('handle_div' in self.handlesBothAxes)):
        self.handlesBothAxes['handle_div'].set_visible(state)
      updateFlag = True

    # update plot
    if(redraw and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def spawned(self, source=None):
    # copies contents of the source object to current object
    if(source != None):
      copyItems = 'x,y,visibility,extrasType,labeltext,color,fontsize,fontname,linespacing,rotation,horizontalalignment,verticalalignment,fontweight,fontstyle'
      copyItems += ',arrow__x,arrow__y,arrow__facecolor,arrow__edgecolor,arrow__linewidth,arrow__linestyle,arrow__dash_capstyle'
      copyItems += ',arrow__shrinkA,arrow__shrinkB,arrow__arrowstyle,arrow__connector,arrow__hatch,arrow__hatchMultiply,arrow__hatch_linewidth,arrow__doFill'
      copyItems += ',arrow__lengthA,arrow__lengthB,arrow__widthA,arrow__widthB'
      copyItems += ',bbox__show,bbox__boxstyle,bbox__facecolor,bbox__edgecolor'
      copyItems += ',bbox__linewidth,bbox__linestyle,bbox__dash_capstyle,bbox__pad,bbox__hatch,bbox__hatchMultiply,bbox__hatch_linewidth,bbox__tooth_size,bbox__rounding_size,bbox__doFill'
      copyItems += ',x2,y2,line__linewidth,line__linestyle,line__dash_capstyle,line__solid_capstyle,line__color,line__gapcolor,line__dogap'
      copyItems += ',shape__type,shape__facecolor,shape__hatch,shape__hatchMultiply,shape__hatch_linewidth,shape__numSpokes,shape__spikiness,shape__doFill,shape__innerRadius,shape__pie,shape__vertices,shape__closed,shape__dx,shape__dy,shape__ind,onSecondAxes,onBothAxes'
      copyItems += ',area__x,area__y1,area__y2'
      copyItems = copyItems.split(',')
      for item in copyItems:
        if(hasattr(source, item)):
          sourceItem = deepcopy(getattr(source, item))
          setattr(self, item, sourceItem)
          
  def getStyle(self):
    # returns as dictionary various style settings
    copyItems = 'x,y,visibility,extrasType,labeltext,color,fontsize,fontname,linespacing,rotation,horizontalalignment,verticalalignment,fontweight,fontstyle'
    copyItems += ',arrow__x,arrow__y,arrow__facecolor,arrow__edgecolor,arrow__linewidth,arrow__linestyle,arrow__dash_capstyle'
    copyItems += ',arrow__shrinkA,arrow__shrinkB,arrow__arrowstyle,arrow__connector,arrow__hatch,arrow__hatchMultiply,arrow__hatch_linewidth,arrow__doFill'
    copyItems += ',arrow__lengthA,arrow__lengthB,arrow__widthA,arrow__widthB'
    copyItems += ',bbox__show,bbox__boxstyle,bbox__facecolor,bbox__edgecolor'
    copyItems += ',bbox__linewidth,bbox__linestyle,bbox__dash_capstyle,bbox__pad,bbox__hatch,bbox__hatchMultiply,bbox__hatch_linewidth,bbox__tooth_size,bbox__rounding_size,bbox__doFill'
    copyItems += ',x2,y2,line__linewidth,line__linestyle,line__dash_capstyle,line__solid_capstyle,line__color,line__gapcolor,line__dogap'
    copyItems += ',shape__type,shape__facecolor,shape__hatch,shape__hatchMultiply,shape__hatch_linewidth,shape__numSpokes,shape__spikiness,shape__doFill,shape__innerRadius,shape__pie,shape__vertices,shape__closed,shape__dx,shape__dy,shape__ind,onSecondAxes,onBothAxes'
    copyItems = copyItems.split(',')
    style = {}
    for entry in copyItems:
      if(hasattr(self, entry)):
        style[entry] = self.__dict__[entry]
    
    return style

  def setStyle(self, key=None, value=0, redraw=True):
    # sets a style setting
    if(hasattr(self, key)):
      if(self.__dict__[key] == value):
        redraw = False
      self.__dict__[key] = value
      if((key == 'shape__facecolor') and (not self.shape__doFill)):
        redraw = False
      if(redraw):
        if(self.parent.plotArea.splitY and self.onBothAxes):
          self.removeFromSplit()
          self.handlesBothAxes['handle'], self.handlesBothAxes['handle_div'] = self.drawMeHelper(redraw=False, duplicate=True)
        self.drawMe()

  def setZOrder(self, zorder=0, redraw=True):
    # updates z order
    self.zorder = zorder
    # update plot if necessary
    updateFlag = False
    if(self.handle != None):
      self.handle.set_zorder(self.zorder + self.parent.zOffset)
      if(self.parent.plotArea.splitY and ('handle' in self.handlesBothAxes)):
        self.handlesBothAxes['handle'].set_zorder(self.zorder + self.parent.zOffset)
      updateFlag = True
    if(self.handle_div != None):
      self.handle_div.set_zorder(self.zorder + self.parent.zOffset)
      if(self.parent.plotArea.splitY and ('handle_div' in self.handlesBothAxes)):
        self.handlesBothAxes['handle_div'].set_zorder(self.zorder + self.parent.zOffset)
      updateFlag = True

    # update plot
    if(redraw and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def updateMe(self, deltaX, deltaY, pickedAxes, pickedBackground, pickedMode=0, quadrantX='left', quadrantY='bottom', transient=True):
    # function called by interactive drag and resize
    if((pickedAxes != None) and (pickedBackground != None)):
      tempX, tempX2, tempY, tempY2 = self.x + deltaX, self.x2 + deltaX, self.y + deltaY, self.y2 + deltaY
      renderer = self.parent.plotArea.matplot.canvas.renderer
      
      # determine which axis we are on -- don't confuse second axis with div
      if(pickedAxes in (self.parent.plotArea.ax_div, self.parent.plotArea.ax2_div)):
        useHandle = self.handle_div
        if(self.parent.plotArea.splitY and self.onBothAxes):
          if(self.onSecondAxes and (pickedAxes == self.parent.plotArea.ax_div)):
            useHandle = self.handlesBothAxes['handle_div']
          if((not self.onSecondAxes) and (pickedAxes == self.parent.plotArea.ax2_div)):
            useHandle = self.handlesBothAxes['handle_div']
      else:
        useHandle = self.handle
        if(self.parent.plotArea.splitY and self.onBothAxes):
          if(self.onSecondAxes and (pickedAxes == self.parent.plotArea.ax)):
            useHandle = self.handlesBothAxes['handle']
          if((not self.onSecondAxes) and (pickedAxes == self.parent.plotArea.ax2)):
            useHandle = self.handlesBothAxes['handle']
        
      # calculate rotation value
      if(pickedMode == 2):
        if(deltaX):
          if(self.extrasType in ['text', 'annotation']):
            xlim, ylim = pickedAxes.get_xlim(), pickedAxes.get_ylim()
            xdist, ydist = np.abs(xlim[0] - xlim[1]), np.abs(ylim[0] - ylim[1])
            deltaAngle = np.arctan((deltaY / ydist) / (deltaX / xdist)) * 2 / np.pi * 180.0
          else:
            deltaAngle = np.arctan(deltaY / deltaX) * 2 / np.pi * 180.0
          useRotation = self.rotation + deltaAngle
          if(useRotation < 0):
            useRotation += 360.0
          if(useRotation > 360.0):
            useRotation -= 360.0
        else:
          useRotation = self.rotation
      else:
        useRotation = self.rotation
      
      # restore background
      pickedAxes.figure.canvas.restore_region(pickedBackground)

      if(self.extrasType == 'area'):
        tempX, tempX2, tempY, tempY2 = self.x, self.x2 / (np.max(self.area__x) - np.min(self.area__x)), self.y, self.y2 / (np.max((self.area__y1, self.area__y2)) - np.min((self.area__y1, self.area__y2)))
        if(pickedMode == 2):
          pass
        elif(pickedMode):
          tempX2, tempY2 = tempX2 + deltaX, tempY2 + deltaY
        else:
          tempX, tempY = tempX + deltaX, tempY + deltaY

        transform, transform2 = self.masterTransform(useX=tempX, useY=tempY, useWidth=tempX2, useHeight=tempY2, useRotation=useRotation)
        if(pickedAxes in (self.parent.plotArea.ax_div, self.parent.plotArea.ax2_div)):
          useHandle.set_transform(transform2)
        else:
          useHandle.set_transform(transform)

        if(not transient):
          if(pickedMode == 2):
            self.rotation = useRotation
          elif(pickedMode):
            self.x, self.x2, self.y, self.y2 = tempX, tempX2 * (np.max(self.area__x) - np.min(self.area__x)), tempY, tempY2 * (np.max((self.area__y1, self.area__y2)) - np.min((self.area__y1, self.area__y2)))
          else:
            self.x, self.x2, self.y, self.y2 = tempX, tempX2 * (np.max(self.area__x) - np.min(self.area__x)), tempY, tempY2 * (np.max((self.area__y1, self.area__y2)) - np.min((self.area__y1, self.area__y2)))
      elif(self.extrasType == 'line'):
        if(pickedMode):
          tempX, tempX2, tempY, tempY2 = self.x - deltaX / 2.0, self.x2 + deltaX / 2.0, self.y - deltaY / 2.0, self.y2 + deltaY / 2.0
  
        useHandle.set_xdata([tempX, tempX2])
        useHandle.set_ydata([tempY, tempY2])
  
        if(not transient):
          self.x, self.x2, self.y, self.y2 = tempX, tempX2, tempY, tempY2
      elif(self.extrasType == 'shape'):
        if(pickedMode == 2):
          tempX, tempX2, tempY, tempY2 = self.x, self.x2, self.y, self.y2
        elif(pickedMode):
          # consider which quadrant of extra object was clicked on
          # at present, only do this for rectangle
          if(self.shape__type == 'rect'):
            if(quadrantX == 'left'):
              tempX, tempX2 = self.x + deltaX, self.x2 - deltaX
            else:
              tempX, tempX2 = self.x, self.x2 + deltaX
            if(quadrantY == 'bottom'):
              tempY, tempY2 = self.y + deltaY, self.y2 - deltaY
            else:
              tempY, tempY2 = self.y, self.y2 + deltaY
          else:
            tempX, tempX2, tempY, tempY2 = self.x, self.x2 + deltaX, self.y, self.y2 + deltaY
        else:
          tempX, tempX2, tempY, tempY2 = self.x + deltaX, self.x2, self.y + deltaY, self.y2

        # here it will be better to go via set_transform
        if(self.shape__type == 'circle'):
          useHandle.angle = useRotation
          transform, transform2 = self.masterTransform(useX=tempX - self.x, useY=tempY - self.y, useWidth=tempX2 / self.x2, useHeight=tempY2 / self.y2, useRotation=useRotation)
        elif(self.shape__type == 'rect'):
          transform, transform2 = self.masterTransform(useX=tempX - self.x2 / 2.0, useY=tempY - self.y2 / 2.0, useWidth=tempX2, useHeight=tempY2, useRotation=useRotation)
        else:
          transform, transform2 = self.masterTransform(useX=tempX, useY=tempY, useWidth=tempX2, useHeight=tempY2, useRotation=useRotation)

        if(pickedAxes in (self.parent.plotArea.ax_div, self.parent.plotArea.ax2_div)):
          useHandle.set_transform(transform2)
        else:
          useHandle.set_transform(transform)

        if(not transient):
          if(pickedMode == 2):
            self.rotation = useRotation
          elif(pickedMode and (self.shape__type == 'rect')):
            self.x, self.x2, self.y, self.y2 = tempX - (self.x2 - tempX2) / 2.0, tempX2, tempY - (self.y2 - tempY2) / 2.0, tempY2
          else:
            self.x, self.x2, self.y, self.y2 = tempX, tempX2, tempY, tempY2
      elif(self.extrasType == 'text'):
        if(pickedMode == 2):
          useHandle.set_rotation(useRotation)
          if(not transient):
            self.rotation = useRotation
        elif(not pickedMode):
          useHandle.set_x(tempX)
          useHandle.set_y(tempY)
          if(not transient):
            self.x, self.y = tempX, tempY
      elif(self.extrasType == 'annotation'):
        if(pickedMode == 2):
          useHandle.set_rotation(useRotation)
        elif(pickedMode):
          tempX, tempX2, tempY, tempY2 = self.x, self.arrow__x + deltaX, self.y, self.arrow__y + deltaY
  
          # we need to generate a temp line here to serve as the marker for the interactive modification as the arrow object does not update
          if(self.handleMarkerLine == None):
            self.handleMarkerLine, = pickedAxes.plot([tempX, tempX2], [tempY, tempY2])
            self.handleMarkerLine.set_linestyle('solid')
            self.handleMarkerLine.set_linewidth(1)
            if(pickedAxes == self.parent.plotArea.ax_div):
              canvasColor = self.parent.plotArea.ax2_div.patch.get_facecolor()
            elif(pickedAxes == self.parent.plotArea.ax):
              canvasColor = self.parent.plotArea.ax2.patch.get_facecolor()
            else:
              canvasColor = pickedAxes.patch.get_facecolor()
            # we need this check to deal with canvas having 'none' facecolor
            if(np.isclose(sum(canvasColor), 0.0)):
              canvasColor = self.parent.plotArea.matplot.get_facecolor()
            if(sum(canvasColor[0:3]) < 1.5):
              self.handleMarkerLine.set_color('white')
            else:
              self.handleMarkerLine.set_color('black')
            self.handleMarkerLine.set_zorder(self.zorder + self.parent.zOffset + 0.1)
          else:
            self.handleMarkerLine.set_xdata([tempX, tempX2])
            self.handleMarkerLine.set_ydata([tempY, tempY2])
            
          self.handleMarkerLine.draw(renderer)
        else:
          tempX, tempX2, tempY, tempY2 = self.x + deltaX, self.arrow__x, self.y + deltaY, self.arrow__y
  
          useHandle.set_x(tempX)
          useHandle.set_y(tempY)
    
        if(not transient):
          if(pickedMode == 2):
            self.rotation = useRotation
          else:
            self.x, self.arrow__x, self.y, self.arrow__y = tempX, tempX2, tempY, tempY2
            if(self.handleMarkerLine != None):
              if(hasattr(self.handleMarkerLine, 'remove')):
                self.handleMarkerLine.remove()
              self.handleMarkerLine = None

      # update object
      useHandle.draw(renderer)
    
      # and blit just the redrawn area
      pickedAxes.figure.canvas.blit(pickedAxes.bbox)

  def duplicateForSplit(self):
    # duplicates current extra and draws it on split y axis
    # delete all previous items (if any)
    self.removeFromSplit()
    # draw me on split y axis
    self.handlesBothAxes['handle'], self.handlesBothAxes['handle_div'] = self.drawMeHelper(redraw=False, duplicate=True)

  def removeFromSplit(self):
    # removes duplicated extra from split y axes
    for handle in self.handlesBothAxes:
      if(hasattr(self.handlesBothAxes[handle], 'remove')):
        self.handlesBothAxes[handle].remove()
    self.handlesBothAxes = {}
    
# the data object holds individual data sets
class DataObject(object):
  def __init__(self, parent=None):
    self.initValues()
    self.parent = parent
    self.parent.zcount += 1
    self.zorder = self.parent.zcount
    self.zorderResid = len(self.parent.data) + 2
    self.onSecondAxes = False
    
    # stuff for split axes
    self.onBothAxes = False
    self.handlesBothAxes = {}
    
    # parameters for global fit
    self.globalFitCheck = True
    self.globalFitCurve = 0

    # initalize parameters
    self.initParam()

  def initValues(self):
    # initializes data values
    self.x = np.array([])
    self.y = np.array([])
    self.xerr = np.array([])
    self.yerr = np.array([])
    self.fval = np.array([])
    self.resid = np.array([])
    self.labels = []
    self.labels2 = []
    
  def initParam(self):    
    # initialize handles for graphics
    self.handleData = None
    self.handleErr = None
    self.handleBar = None
    self.handleStack = None
    self.handleStackNeg = None
    self.handleErrShady = None
    self.handleText = None
    self.handleViolin = None
    self.handleScatter = None
    self.handleResid = None
    self.handleResidBar = None
    self.handleResidStack = None
    self.handleResidStackNeg = None
    self.handleResidText = None
    self.handleResidScatter = None
    ##
    self.handleData_div = None
    self.handleErr_div = None
    self.handleBar_div = None
    self.handleStack_div = None
    self.handleStackNeg_div = None
    self.handleErrShady_div = None
    self.handleText_div = None
    self.handleViolin_div = None
    self.handleScatter_div = None
    self.handleResid_div = None
    self.handleResidBar_div = None
    self.handleResidStack_div = None
    self.handleResidStackNeg_div = None
    self.handleResidText_div = None
    self.handleResidScatter_div = None
    # initialize name
    self.setName('Data_' + str(len(self.parent.data) + 1))
    self.setNameResid('Resid_' + str(len(self.parent.data) + 1))
    # initialize visibility
    self.visibility = True
    self.visibilityResid = True
    # initialize notes
    self.notes = ''
    # initalize style
    self.style = {}
    self.style['linewidth'] = 1.0
    self.style['linestyle'] = 'None'
    self.style['dash_capstyle'] = 'butt'
    #self.style['solid_capstyle'] = 'butt'
    self.style['color'] = [0.89, 0.29, 0.2, 1.0]
    self.style['markerfacecolor'] = [0.89, 0.29, 0.2, 1.0]
    self.style['markerfacecoloralt'] = [0.0, 0.0, 0.0, 1.0]
    self.style['markeredgecolor'] = [0.0, 0.0, 0.0, 1.0]
    self.style['markeredgewidth'] = 0.5
    self.style['markersize'] = 10.0
    self.style['marker'] = 'o'
    self.style['fillstyle'] = 'full'
    self.style['markevery'] = 1
    self.style['doFill'] = True
    self.style['gapcolor'] = [0, 0, .8, 1.]
    self.style['doGap'] = False
    
    # intialize error style
    self.Errorstyle = {}
    self.Errorstyle['color'] = [0.0, 0.0, 0.0, 1.0]
    self.Errorstyle['linewidth'] = 1.0
    self.Errorstyle['linestyle'] = 'solid'
    self.Errorstyle['marker'] = '_'
    self.Errorstyle['markerX'] = '|'
    self.Errorstyle['markerfacecolor'] = [0.89, 0.29, 0.2, 0.5]
    self.Errorstyle['markerfacecoloralt'] = [0.0, 0.0, 0.0, 1.0]
    self.Errorstyle['markeredgecolor'] = [0.89, 0.29, 0.2, 1.0]
    self.Errorstyle['markeredgewidth'] = 0.5
    self.Errorstyle['markersize'] = 10.0
    self.Errorstyle['fillstyle'] = 'full'
    self.Errorstyle['doFill'] = True
    self.Errorstyle['errorInFront'] = False
    self.Errorstyle['visible'] = True
    self.Errorstyle['direction'] = 'both'
    
    # intialize error shading style
    self.ErrorShadystyle = {}
    self.ErrorShadystyle['visible'] = False
    self.ErrorShadystyle['facecolor'] = [0.8, 0.2, 0.2, 0.5]
    self.ErrorShadystyle['edgecolor'] = [0.8, 0.2, 0.2, 1.0]
    self.ErrorShadystyle['linestyle'] = 'solid'
    self.ErrorShadystyle['linewidth'] = 0.0
    
    # initialize bar style
    self.Barstyle = {}
    self.Barstyle['showBar'] = False
    self.Barstyle['linewidth'] = 0.5
    self.Barstyle['linestyle'] = 'solid'
    self.Barstyle['capstyle'] = 'butt'
    self.Barstyle['facecolor'] = [0.8, 0.2, 0.2, 1.0]
    self.Barstyle['facecolorAlt'] = [0.3, 0.2, 0.6, 1.0]
    self.Barstyle['edgecolor'] = [0.3, 0.3, 0.3, 1.0]
    self.Barstyle['width'] = 0.1
    self.Barstyle['hatch'] = ''
    self.Barstyle['hatchMultiply'] = 1
    self.Barstyle['hatch_linewidth'] = 1.
    self.Barstyle['offset'] = 0
    self.Barstyle['doFill'] = True
    self.Barstyle['bottom'] = 0
    self.Barstyle['horizontal'] = False
    
    # initialize stack style
    self.Stackstyle = {}
    self.Stackstyle['showStack'] = False
    self.Stackstyle['linewidth'] = 0.5
    self.Stackstyle['linestyle'] = 'solid'
    self.Stackstyle['facecolor'] = [0.2, 0.8, 0.2, 0.5]
    self.Stackstyle['facecolorAlt'] = [0.3, 0.2, 0.6, 1.0]
    self.Stackstyle['edgecolor'] = [0.3, 0.3, 0.3, 1.0]
    self.Stackstyle['hatch'] = ''
    self.Stackstyle['hatchMultiply'] = 1
    self.Stackstyle['hatch_linewidth'] = 1.
    self.Stackstyle['doFill'] = True
    self.Stackstyle['bottom'] = 0.
    
    # initialize text style
    self.Textstyle = {}
    self.Textstyle['showText'] = False
    self.Textstyle['skip'] = 1
    self.Textstyle['color'] = [0.2, 0.2, 0.2, 1.0]
    self.Textstyle['fontsize'] = 10
    self.Textstyle['fontweight'] = 'normal'
    self.Textstyle['fontstyle'] = 'normal'
    self.Textstyle['fontname'] = 'Dejavu Sans'
    self.Textstyle['rotation'] = 0.0
    self.Textstyle['padX'] = 0.0
    self.Textstyle['padY'] = 0.0
    self.Textstyle['type'] = 'y'
    self.Textstyle['linespacing'] = 1.
    
    # initialize violin style
    self.Violinstyle = {}
    self.Violinstyle['mode'] = 0
    self.Violinstyle['bodies'] = {'facecolor': [0.99, 0.59, 0.5, 1.0], 'edgecolor': [0.2, 0.2, 0.2, 1.0], 'linestyle': 'solid', 'linewidth': 1.0,\
                    'capstyle': 'butt', 'hatch': '', 'hatchMultiply': 1, 'hatch_linewidth': 1., 'visible': True, 'width': 0.5, 'doFill': True, 'type': 'full'}
    self.Violinstyle['cmaxes'] = {'linestyle': 'solid', 'linewidth': 1.0, 'color': [0.2, 0.2, 0.2, 1.0], 'capstyle': 'butt', 'visible': True}
    for slaved in ['cbars', 'cmeans']:
      self.Violinstyle[slaved] = {}
      self.Violinstyle[slaved].update(self.Violinstyle['cmaxes'])
    self.Violinstyle['cmedians'] = {'visible': False}
    self.Violinstyle['cmins'] = {'visible': True}

    # initialize boxplot style -- we can lumo this together with the violin style as the keys are non-overlapping
    self.Violinstyle['boxes'] = {'linestyle': 'solid', 'linewidth': 1.0, 'color': [0.89, 0.29, 0.2, 1.0], 'dash_capstyle': 'butt', 'visible': True}
    for slaved in ['whiskers', 'medians', 'caps']:
      self.Violinstyle[slaved] = {}
      self.Violinstyle[slaved].update(self.Violinstyle['boxes'])
    self.Violinstyle['boxes'].update({'edgecolor': [0.2, 0.2, 0.2, 1.0], 'width': 0.5, 'boxFill': True, 'facecolor': [0.99, 0.59, 0.5, 1.0], 'hatch': '', 'hatchMultiply': 1, 'hatch_linewidth': 1., 'type': 'full'})
    # only used this key temporarily for slaving whiskers etc.
    del self.Violinstyle['boxes']['color']
    self.Violinstyle['fliers'] = {'visible': True, 'marker': 'o', 'markerfacecolor': [0.89, 0.29, 0.2, 1.0], 'markerfacecoloralt': [0.0, 0.0, 0.0, 1.0],\
                    'markeredgecolor': [0.0, 0.0, 0.0, 1.0], 'markeredgewidth': 0.5, 'markersize': 10.0, 'fillstyle': 'full', 'doFill': True}

    # initialize scatter style
    self.Scatterstyle = {'showScatter': False, 'facecolor': [.5, .6, .7, 1.], 'facecolor__type': 'y', 'facecolor__cmap': 'jet', 'doFill': True,
                         'edgecolor': [.2, .2, .2, 1.], 'edgecolor__type': 'const', 'edgecolor__cmap': 'jet', 'linewidth': .5,
                         'sizes': 10, 'sizes__type': 'const', 'marker': 'o'}
    
    # initialize resid style
    self.Residstyle = deepcopy(self.style)
    self.ResidLinestyle = {}
    self.ResidLinestyle['linewidth'] = 1.0
    self.ResidLinestyle['linestyle'] = 'solid'
    self.ResidLinestyle['dash_capstyle'] = 'butt'
    self.ResidLinestyle['color'] = [0.8, 0.2, 0.2, 1.0]
    self.ResidLinestyle['gapcolor'] = [0, 0, .8, 1.]
    self.ResidLinestyle['doGap'] = False
    self.ResidBarstyle = deepcopy(self.Barstyle)
    self.ResidStackstyle = deepcopy(self.Stackstyle)
    self.ResidTextstyle = deepcopy(self.Textstyle)
    self.ResidScatterstyle = deepcopy(self.Scatterstyle)
    
    # fine-sort zorder
    self.relativeZOrderError = -0.4
    self.relativeZOrderBar = -0.2
    self.relativeZOrderText = 0.2
    self.relativeZOrderViolin = -0.05

  def addLabeli(self, labels=[], labels2=False):
    # adds labels to data object if not already existing
    tempList = labels
    # go via temp list to avoid evil shenanigans b/w data sets
    # (somehow empty labels list only instanced once between data sets ... what?)
    tempList += [i for i in self.labels[:len(tempList)]]
    i = 0
    while (len(tempList) < len(self.x)):
      tempList.append(i)
      i += 1
    if(labels2):
      self.labels2 = tempList
    else:
      self.labels = tempList
  
  def toggleResidBar(self, showBar):
    # turns on/off bar graphics
    self.ResidBarstyle['showBar'] = showBar
    self.drawMeResid(rescale=False)

  def toggleResidStack(self, showStack):
    # turns on/off stack graphics
    self.ResidStackstyle['showStack'] = showStack
    self.drawMeResid(rescale=False)

  def toggleBar(self, showBar):
    # turns on/off bar graphics
    self.Barstyle['showBar'] = showBar
    self.drawMe(rescale=False)

  def toggleBarHorizontal(self, horizontal):
    # toggles orientation of bar chart
    self.Barstyle['horizontal'] = horizontal
    self.drawMe(rescale=False)

  def toggleResidBarHorizontal(self, horizontal):
    # toggles orientation of bar chart
    self.ResidBarstyle['horizontal'] = horizontal
    self.drawMeResid(rescale=False)

  def toggleStack(self, showStack):
    # turns on/off stack graphics
    self.Stackstyle['showStack'] = showStack
    self.drawMe(rescale=False)

  def toggleText(self, showText):
    # turns on/off text graphics
    self.Textstyle['showText'] = showText
    self.drawMe(rescale=False)

  def toggleResidText(self, showText):
    # turns on/off text graphics
    self.ResidTextstyle['showText'] = showText
    self.drawMeResid(rescale=False)

  def toggleScatter(self, showScatter):
    # turns on/off bar graphics
    self.Scatterstyle['showScatter'] = showScatter
    self.drawMe(rescale=False)

  def toggleResidScatter(self, showScatter):
    # turns on/off bar graphics
    self.ResidScatterstyle['showScatter'] = showScatter
    self.drawMeResid(rescale=False)

  def toggleViolin(self, mode, redraw=False):
    # turns on/off violin graphics
    self.Violinstyle['mode'] = mode
    self.drawMe(rescale=False, redraw=redraw)

  def reportState(self):
    # reports data content for saveState function
    items = ['name', 'nameResid', 'zorder', 'zorderResid', 'x', 'y', 'xerr', 'yerr', 'fval', 'resid', 'labels', 'labels2', 'onSecondAxes', 'onBothAxes', 'globalFitCheck', 'globalFitCurve', 'notes']
    retv = {}
    
    for entry in items:
      if(hasattr(self, entry)):
        value = self.__dict__[entry]
        if(type(value) == type(np.array([]))):
          value = value.tolist()
        retv[entry] = value

    return retv

  def restoreState(self, data={}, zoffset=0, zoffsetResid=0, zoffsetCurve=0):
    # restores data content for loadState function
    for entry in data:
      if(hasattr(self, entry)):
        if(entry in ['x', 'y', 'xerr', 'yerr', 'fval', 'resid']):
          value = np.array(data[entry])
        elif(entry in ['zorder']):
          value = data[entry] + zoffset
        elif(entry in ['zorderResid']):
          value = data[entry] + zoffsetResid
        elif(entry == 'name'):
          value = data[entry]
          self.setName(value)
        elif(entry == 'nameResid'):
          value = data[entry]
          self.setNameResid(value)
        elif(entry in ['globalFitCurve']):
          value = data[entry] + zoffsetCurve
        else:
          value = data[entry]
        self.__dict__[entry] = value

  def setName(self, name='John Doe'):
    # updates name of object
    self.name = name
    # update plot if necessary
    if(self.handleData != None):
      self.handleData.set_label(self.name)
    
  def setNameResid(self, name='John Doe'):
    # updates name of object
    self.nameResid = name
    # update plot if necessary
    if(self.handleResid != None):
      self.handleResid.set_label(self.nameResid)
    
  def setZOrderError(self, state=True, redraw=True):
    # toggles relative z order of error bars
    self.Errorstyle['errorInFront'] = state
    if(state):
      self.relativeZOrderError = 0.4
    else:
      self.relativeZOrderError = -0.4
    # reassign z values
    # in case of redraw, ensure that this is done by temporarily altering z-order ever so slightly
    # but we need to store and restore original value, lest we inadvertently convert zorder to float
    # which will throw an error eventually on the objects tab
    if(redraw):
      storedZ = self.zorder
      self.zorder -= 0.01
      self.setZOrder(storedZ, redraw=redraw)
    else:
      self.setZOrder(self.zorder, redraw=redraw)

  def setZOrderResid(self, zorder=0, redraw=True):
    # updates z order of residuals
    self.zorderResid = zorder
    # update plot if necessary
    if(self.handleResid != None):
      self.handleResid.set_zorder(self.zorderResid + self.parent.zOffset)
    if(self.handleResidBar != None):
      for entry in self.handleResidBar.patches:
        entry.set_zorder(self.zorderResid + self.relativeZOrderBar + self.parent.zOffset)
    if(self.handleResidStack != None):
      self.handleResidStack.set_zorder(self.zorderResid + self.relativeZOrderBar + self.parent.zOffset)
    if(self.handleResidStackNeg != None):
      self.handleResidStackNeg.set_zorder(self.zorderResid + self.relativeZOrderBar + self.parent.zOffset)
    if(self.handleResidText != None):
      for entry in self.handleResidText:
        entry.set_zorder(self.zorderResid + self.parent.zOffset + self.relativeZOrderText)
      
    if(self.handleResid_div != None):
      self.handleResid_div.set_zorder(self.zorderResid + self.parent.zOffset)
    if(self.handleResidBar_div != None):
      for entry in self.handleResidBar_div.patches:
        entry.set_zorder(self.zorderResid + self.relativeZOrderBar + self.parent.zOffset)
    if(self.handleResidStack_div != None):
      self.handleResidStack_div.set_zorder(self.zorderResid + self.relativeZOrderBar + self.parent.zOffset)
    if(self.handleResidStackNeg_div != None):
      self.handleResidStackNeg_div.set_zorder(self.zorderResid + self.relativeZOrderBar + self.parent.zOffset)
    if(self.handleResidText_div != None):
      for entry in self.handleResidText_div:
        entry.set_zorder(self.zorderResid + self.parent.zOffset + self.relativeZOrderText)
      
    # update plot
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def setZOrder(self, zorder=0, redraw=True):
    # updates z order
    if(self.zorder == zorder):
      redraw, changed = False, False
    else:
      changed = True
    self.zorder = zorder
    
    # prepare list of handles
    handlesData = [self.handleData, self.handleData_div] + [self.handlesBothAxes[item] for item in ['handleData', 'handleData_div'] if item in self.handlesBothAxes]
    handlesErr = [self.handleErr, self.handleErr_div] + [self.handlesBothAxes[item] for item in ['handleErr', 'handleErr_div'] if item in self.handlesBothAxes]
    handlesErrShady = [self.handleErrShady, self.handleErrShady_div] + [self.handlesBothAxes[item] for item in ['handleErrShady', 'handleErrShady_div'] if item in self.handlesBothAxes]
    handlesBar = [self.handleBar, self.handleBar_div] + [self.handlesBothAxes[item] for item in ['handleBar', 'handleBar_div'] if item in self.handlesBothAxes]
    handlesStack = [self.handleStack, self.handleStack_div] + [self.handlesBothAxes[item] for item in ['handleStack', 'handleStack_div'] if item in self.handlesBothAxes]
    handlesStackNeg = [self.handleStackNeg, self.handleStackNeg_div] + [self.handlesBothAxes[item] for item in ['handleStackNeg', 'handleStackNeg_div'] if item in self.handlesBothAxes]
    handlesText = [self.handleText, self.handleText_div] + [self.handlesBothAxes[item] for item in ['handleText', 'handleText_div'] if item in self.handlesBothAxes]
    handlesViolin = [self.handleViolin, self.handleViolin_div] + [self.handlesBothAxes[item] for item in ['handleViolin', 'handleViolin_div'] if item in self.handlesBothAxes]
    handlesScatter = [self.handleScatter, self.handleScatter_div] + [self.handlesBothAxes[item] for item in ['handleScatter', 'handleScatter_div'] if item in self.handlesBothAxes]
    
    # update plot if necessary
    for handleData in handlesData:
      if(handleData != None):
        handleData.set_zorder(self.zorder + self.parent.zOffset)
    for handleErr in handlesErr:
      if(handleErr != None):
        handleErr[0].set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset)
        for entry in handleErr[1]:
          entry.set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset)
        for entry in handleErr[2]:
          entry.set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset)
    for handleErrShady in handlesErrShady:
      if(handleErrShady != None):
        handleErrShady.set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset - 0.05)
    for handleBar in handlesBar:
      if(handleBar != None):
        for entry in handleBar.patches:
          entry.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset)
    for handleStack in handlesStack:
      if(handleStack != None):
        handleStack.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset)
    for handleStackNeg in handlesStackNeg:
      if(handleStackNeg != None):
        handleStackNeg.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset)
    for handleText in handlesText:
      if(handleText != None):
        for entry in handleText:
          entry.set_zorder(self.zorder + self.parent.zOffset + self.relativeZOrderText)
    for handleViolin in handlesViolin:
      if(handleViolin != None):
        for key in handleViolin:
          if(type(handleViolin[key]) == type([])):
            for entry in handleViolin[key]:
              entry.set_zorder(self.zorder + self.parent.zOffset + self.relativeZOrderViolin)
          else:
            self.handleViolin[key].set_zorder(self.zorder + self.parent.zOffset + self.relativeZOrderViolin)
    for handleScatter in handlesScatter:
      if(handleScatter != None):
        handleScatter.set_zorder(self.zorder + self.parent.zOffset)

    # update plot
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()
      
    return changed

  def collateViolinData(self):
    # arranges data for violin plot
    EPSILON, MINMEMBERS = 1e-6, 2
    
    if(len(self.x) * len(self.y)):
      xvector, yvector = [], []
      for x, y in zip(self.x, self.y):
        # check for close previous x entry
        closeness = np.isclose(x, xvector, rtol=EPSILON)
        position = [i for i in range(len(closeness)) if closeness[i]]
        if(len(position)):
          yvector[position[0]].append(y)
        else:
          # make new entry
          xvector.append(x)
          yvector.append([])
          yvector[-1].append(y)
      
      # exclude  groups that have too few points
      nu_xvector = [i for i, j in zip(xvector, yvector) if (len(j) >= MINMEMBERS)]
      nu_yvector = [j for j in yvector if (len(j) >= MINMEMBERS)]
      xvector, yvector = nu_xvector, nu_yvector
      return xvector, yvector
    else:
      return [], []

  def getData_n_Fit(self):
    # returns data, fit and residuals along with rolestr
    descriptors = ['x', 'xerr', 'y', 'yerr', 'fval', 'resid']
    roles = []
    values = np.array([])
    for entry in descriptors:
      if(len(self.__dict__[entry])):
        if(len(values)):
          values = np.vstack((values, self.__dict__[entry]))
        else:
          values = self.__dict__[entry]
        roles.append(entry)
    
    if(len(values)):
      values = values.transpose()
    return values, roles

  def getLabels(self, labels2=False):
    # returns data labels
    if(labels2):
      return list(self.labels2)
    else:
      return list(self.labels)

  def setFval(self, fval=np.array([]), indices=[]):
    # updates fitted values and residuals after fit
    if(fval.size):
      if(len(indices)):
        # we only operate on selected data given by incides
        # first initialize self.fval as needed
        if(len(self.fval) != len(indices)):
          self.fval = np.zeros(len(indices))
        # then set values for selected indices
        self.fval[indices] = fval
      else:
        self.fval = fval
      self.resid = np.array([i - j for i, j in zip(self.y, self.fval)])
      
  def setBothAxes(self, state=False, redraw=True):
    # moves plot between axes
    self.onBothAxes = state
    # remove surplus items?
    if(self.onBothAxes):
      self.duplicateForSplit()
    else:
      self.removeFromSplit()

  def setSecondAxes(self, state=False, redraw=True):
    # moves data between axes
    self.onSecondAxes = state
    # check whether dual display is turned on
    if(self.parent.plotArea.splitY and self.onBothAxes):
      # we can just swap the handles
      items = 'handleData,handleErr,handleErrShady,handleBar,handleStack,handleStackNeg,handleText,handleViolin,handleScatter'.split(',')
      items += [i + '_div' for i in items]
      for item in items:
        if(item in self.handlesBothAxes):
          self.__dict__[item], self.handlesBothAxes[item] = self.handlesBothAxes[item], self.__dict__[item]
      return
    # trigger redraw
    self.drawMe(redraw=redraw, rescale=not self.parent.plotArea.splitY)

  def setVisibility(self, state=True, redraw=True):
    # toggles visibility of data and error bars
    if(self.visibility == state):
      redraw, changed = False, False
    else:
      changed = True
    self.visibility = state

    # prepare list of handles
    handlesData = [self.handleData, self.handleData_div] + [self.handlesBothAxes[item] for item in ['handleData', 'handleData_div'] if item in self.handlesBothAxes]
    handlesErr = [self.handleErr, self.handleErr_div] + [self.handlesBothAxes[item] for item in ['handleErr', 'handleErr_div'] if item in self.handlesBothAxes]
    handlesErrShady = [self.handleErrShady, self.handleErrShady_div] + [self.handlesBothAxes[item] for item in ['handleErrShady', 'handleErrShady_div'] if item in self.handlesBothAxes]
    handlesBar = [self.handleBar, self.handleBar_div] + [self.handlesBothAxes[item] for item in ['handleBar', 'handleBar_div'] if item in self.handlesBothAxes]
    handlesStack = [self.handleStack, self.handleStack_div] + [self.handlesBothAxes[item] for item in ['handleStack', 'handleStack_div'] if item in self.handlesBothAxes]
    handlesStackNeg = [self.handleStackNeg, self.handleStackNeg_div] + [self.handlesBothAxes[item] for item in ['handleStackNeg', 'handleStackNeg_div'] if item in self.handlesBothAxes]
    handlesText = [self.handleText, self.handleText_div] + [self.handlesBothAxes[item] for item in ['handleText', 'handleText_div'] if item in self.handlesBothAxes]
    handlesViolin = [self.handleViolin, self.handleViolin_div] + [self.handlesBothAxes[item] for item in ['handleViolin', 'handleViolin_div'] if item in self.handlesBothAxes]
    handlesScatter = [self.handleScatter, self.handleScatter_div] + [self.handlesBothAxes[item] for item in ['handleScatter', 'handleScatter_div'] if item in self.handlesBothAxes]
    
    for handleData in handlesData:
      if(handleData != None):
        handleData.set_visible(state)
        if(handleData == self.handleData):
          if(self.visibility):
            handleData.set_label(self.name)
          else:
            handleData.set_label('_nolegend_')
    for handleErr in handlesErr:
      if(handleErr != None):
        workstate = state and self.Errorstyle['visible']
        handleErr[0].set_visible(workstate)
        for entry in handleErr[1]:
          entry.set_visible(workstate)
        for entry in handleErr[2]:
          entry.set_visible(workstate)
    for handleErrShady in handlesErrShady:
      if(handleErrShady != None):
        handleErrShady.set_visible(state)
    for handleBar in handlesBar:
      if(handleBar != None):
        for entry in handleBar.patches:
          entry.set_visible(state)
    for handleStack in handlesStack:
      if(handleStack != None):
        handleStack.set_visible(state)
    for handleStackNeg in handlesStackNeg:
      if(handleStackNeg != None):
        handleStackNeg.set_visible(state)
    for handleText in handlesText:
      if(handleText != None):
        for entry in handleText:
          entry.set_visible(state)
    for handleViolin in handlesViolin:
      if(handleViolin != None):
        for key in handleViolin:
          if((key in self.Violinstyle) and ('visible' in self.Violinstyle[key])):
            modState = self.Violinstyle[key]['visible']
          else:
            modState = True
          if(type(handleViolin[key]) == type([])):
            for entry in handleViolin[key]:
              entry.set_visible(state and modState)
          else:
            handleViolin[key].set_visible(state and modState)
    for handleScatter in handlesScatter:
      if(handleScatter != None):
        handleScatter.set_visible(state)
      
    # hide caps when error display is only in one direction
    if(self.Errorstyle['direction'] != 'both'):
      for handleErr in handlesErr:
        if(handleErr != None):
          if(self.xerr.size):
            offset = 2
            if(self.Errorstyle['direction'] == 'positive'):
              for entry in handleErr[1][:min(offset - 1, len(handleErr[1]))]:
                entry.set_visible(False)
            elif(self.Errorstyle['direction'] == 'negative'):
              for entry in handleErr[1][1:min(offset, len(handleErr[1]))]:
                entry.set_visible(False)
          else:
            offset = 0
          if(self.yerr.size):
            if(self.Errorstyle['direction'] == 'positive'):
              for entry in handleErr[1][offset:-1]:
                entry.set_visible(False)
            elif(self.Errorstyle['direction'] == 'negative'):
              for entry in handleErr[1][offset + 1:]:
                entry.set_visible(False)      

    # if visibility is activated, check for autozoom
    # on second thought, should also do this if inactivated
    if(True):
      if(self.parent.plotArea.autoScaleX):
        self.parent.plotArea.doAutoScale(axis='x', redraw=False)
      if(self.parent.plotArea.autoScaleX_div):
        self.parent.plotArea.doAutoScale(axis='x2', redraw=False)

      if(self.parent.plotArea.autoScaleY):
        self.parent.plotArea.doAutoScale(axis='y', redraw=False)
      if(self.parent.plotArea.autoScaleY2):
        self.parent.plotArea.doAutoScale(axis='y2', redraw=False)
      
    # update plot
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()
      
    return changed

  def setVisibilityResid(self, state=True, redraw=True):
    # toggles visibility of residuals
    if(self.visibilityResid == state):
      redraw, changed = False, False
    else:
      changed = True
    self.visibilityResid = state
    if(self.handleResid != None):
      self.handleResid.set_visible(state)
    if(self.handleResidBar != None):
      for entry in self.handleResidBar.patches:
        entry.set_visible(state)
    if(self.handleResidStack != None):
      self.handleResidStack.set_visible(state)
    if(self.handleResidStackNeg != None):
      self.handleResidStackNeg.set_visible(state)
    if(self.handleResidText != None):
      for entry in self.handleResidText:
        entry.set_visible(state)
      
    if(self.handleResid_div != None):
      self.handleResid_div.set_visible(state)
    if(self.handleResidBar_div != None):
      for entry in self.handleResidBar_div.patches:
        entry.set_visible(state)
    if(self.handleResidStack_div != None):
      self.handleResidStack_div.set_visible(state)
    if(self.handleResidStackNeg_div != None):
      self.handleResidStackNeg_div.set_visible(state)
    if(self.handleResidText_div != None):
      for entry in self.handleResidText_div:
        entry.set_visible(state)
      
    # update plot
    if(redraw):
      self.parent.plotArea.residplotwidget.myRefresh()
      
    return changed

  def spawned(self, source=None):
    # copies contents of the source object to current object
    if(source != None):
      copyItems = 'x,y,xerr,yerr,style,Errorstyle,ErrorShadystyle,fval,resid,visibility,visibilityResid,Barstyle,Stackstyle,Textstyle,Violinstyle,Residstyle,Scatterstyle,ResidBarstyle,ResidStackstyle,ResidTextstyle,ResidScatterstyle,labels,labels2,relativeZOrderError,relativeZOrderBar,relativeZOrderText,onSecondAxes,onBothAxes,globalFitCheck,globalFitCurve,notes'.split(',')
      for item in copyItems:
        if(hasattr(source, item)):
          sourceItem = deepcopy(getattr(source, item))
          setattr(self, item, sourceItem)
          
  def styleMe(self, handleData=None, handleErr=None, handleErrShady=None, handleBar=None, handleStack=None, handleStackNeg=None, handleText=None, handleViolin=None, handleScatter=None):
    # apply styles to plot object
    if(handleData != None):
      style = self.style
      for key in style:
        method = 'set_' + key
        if(hasattr(handleData, method)):
          method2call = getattr(handleData, method)
          if((key == 'marker') and (style[key] in CUSTOM_MARKERS)):
            method2call(CUSTOM_MARKERS[style[key]])
          elif((key == 'marker') and (not (style[key] in matplotlib.lines.Line2D.markers))):
            # this is to ensure compatibility with future versions that may introduce markers we don't know yet
            method2call('o')
          elif((key in ['linestyle', 'linewidth']) and (self.style['linestyle'] != 'solid') and (self.style['linewidth'] < .01)):
            # need to set linestyle to 'solid' to avoid crash
            self.style['linestyle'] = 'solid'
            handleData.set_linestyle('solid')
            handleData.set_linewidth(self.style['linewidth'])
            self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          else:
            method2call(style[key])
      if(not style['doFill']):
        handleData.set_markerfacecolor('none')
      if(not self.style['doGap']):
        if(hasattr(handleData, 'set_gapcolor')):
          handleData.set_gapcolor('none')

      # set name
      if(hasattr(handleData, 'set_label')):
        method2call = getattr(handleData, 'set_label')
        method2call(self.name)
        
      # set z order
      if(hasattr(handleData, 'set_zorder')):
        handleData.set_zorder(self.zorder + self.parent.zOffset)
          
    # apply styles to error bars
    if (handleErr != None):
      errorstyle = self.Errorstyle
      # Note: handleErr[1] are the caps, handleErr[2] are the lines (super confusing!!)
      for key in errorstyle:
        method = 'set_' + key
        if(key in ['marker', 'markerX']):
          # deal w/ x-errors
          if(len(self.xerr)):
            # we have x errors, those should be the first two entries in handleErr[1]
            offset = 2
            if(key == 'markerX'):
              for entry in handleErr[1][:min(offset, len(handleErr[1]))]:
                if (hasattr(entry, 'set_marker')):
                  marker = errorstyle[key]
                  if(marker in CUSTOM_MARKERS):
                    entry.set_marker(CUSTOM_MARKERS[marker])
                  elif(not (marker in matplotlib.lines.Line2D.markers)):
                    # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                    entry.set_marker('o')
                  else:
                    entry.set_marker(marker)
          else:
            offset = 0
          # deal w/ y-errors
          if(len(self.yerr)):
            # we have y errors, those should be the first two entries in handleErr[1]
            if(key == 'marker'):
              for entry in handleErr[1][offset:]:
                if (hasattr(entry, 'set_marker')):
                  marker = errorstyle[key]
                  if(marker in CUSTOM_MARKERS):
                    entry.set_marker(CUSTOM_MARKERS[marker])
                  elif(not (marker in matplotlib.lines.Line2D.markers)):
                    # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                    entry.set_marker('o')
                  else:
                    entry.set_marker(marker)
        else:
          for entry in handleErr[1]:
            if (hasattr(entry, method)):
              method2call = getattr(entry, method)
              method2call(errorstyle[key])
        for entry in handleErr[2]:
          if (hasattr(entry, method)):
            method2call = getattr(entry, method)
            method2call(errorstyle[key])
      # don't connect caps of error bars
      for entry in handleErr[1]:
        entry.set_linestyle('None')
      # check visibility of entire object
      if(not self.visibility):
        if(hasattr(handleErr[0], 'set_visible')):
          handleErr[0].set_visible(False)
        for entry in handleErr[1]:
          if(hasattr(entry, 'set_visible')):
            entry.set_visible(False)
        for entry in handleErr[2]:
          if(hasattr(entry, 'set_visible')):
            entry.set_visible(False)
      # hide caps when error display is only in one direction
      if(self.Errorstyle['direction'] != 'both'):
        if(len(self.xerr)):
          offset = 2
          if(self.Errorstyle['direction'] == 'positive'):
            for entry in handleErr[1][:min(offset - 1, len(handleErr[1]))]:
              entry.set_visible(False)
          elif(self.Errorstyle['direction'] == 'negative'):
            for entry in handleErr[1][1:min(offset, len(handleErr[1]))]:
              entry.set_visible(False)
        else:
          offset = 0
        if(len(self.yerr)):
          if(self.Errorstyle['direction'] == 'positive'):
            for entry in handleErr[1][offset:-1]:
              entry.set_visible(False)
          elif(self.Errorstyle['direction'] == 'negative'):
            for entry in handleErr[1][offset + 1:]:
              entry.set_visible(False)

      # special treatment for fill of error markers
      if(not self.Errorstyle['doFill']):
        for entry in handleErr[1]:
          if(hasattr(entry, 'set_markerfacecolor')):
            entry.set_markerfacecolor('none')
            
      # set z order
      if(hasattr(handleErr[0], 'set_zorder')):
        handleErr[0].set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset)
      for entry in (handleErr[1] + handleErr[2]):
        if(hasattr(entry, 'set_zorder')):
          entry.set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset)
            
    # error shading
    if (handleErrShady != None):
      style = self.ErrorShadystyle
      for key in style:
        method = 'set_' + key
        if (hasattr(handleErrShady, method)):
          method2call = getattr(handleErrShady, method)
          method2call(style[key])
      # check visibility of entire object
      if(not self.visibility):
        if(hasattr(handleErrShady, 'set_visible')):
          handleErrShady.set_visible(False)
      # set z order
      if(hasattr(handleErrShady, 'set_zorder')):
        handleErrShady.set_zorder(self.zorder + self.relativeZOrderError + self.parent.zOffset - 0.05)

    # apply bar styles
    if (handleBar != None):
      if(self.Barstyle['showBar']):
        barstyle = self.Barstyle
        for entry in handleBar.patches:
          for key in barstyle:
            method = 'set_' + key
            # treat width differently to avoid recentering of bars upon width change (does not heed center position)
            if ((key != 'width') and (hasattr(entry, method))):
              method2call = getattr(entry, method)
              # extra check b/c of hatchMultiply
              if((key == 'hatch') and ('hatchMultiply' in barstyle)):
                method2call(barstyle[key] * barstyle['hatchMultiply'])
              else:
                method2call(barstyle[key])
            # set alternate color?
            if((entry.get_height() < 0) and ('facecolorAlt' in barstyle)):
              entry.set_facecolor(barstyle['facecolorAlt'])
        # check visibility of entire object
        if(not self.visibility):
          for entry in handleBar.patches:
            if(hasattr(entry, 'set_visible')):
              entry.set_visible(False)
 
        # special treatment for fill of bars
        if(not self.Barstyle['doFill']):
          for entry in handleBar.patches:
            if(hasattr(entry, 'set_facecolor')):
              entry.set_facecolor('none')

        # set z order
        for patch in handleBar.patches:
          if(hasattr(patch, 'set_zorder')):
            patch.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset)
  
    # apply stack styles
    if((handleStack != None) or (handleStackNeg != None)):
      if(self.Stackstyle['showStack']):
        stackstyle = self.Stackstyle
        for handle in [handleStack, handleStackNeg]:
          if(handle != None):
            for key in stackstyle:
              method = 'set_' + key
              if (hasattr(handle, method)):
                method2call = getattr(handle, method)
                # extra check b/c of hatchMultiply
                if((key == 'hatch') and ('hatchMultiply' in stackstyle)):
                  method2call(stackstyle[key] * stackstyle['hatchMultiply'])
                else:
                  method2call(stackstyle[key])
            # set alternate color?
            if((handle == handleStackNeg) and ('facecolorAlt' in stackstyle)):
              handle.set_facecolor(stackstyle['facecolorAlt'])
            else:
              handle.set_facecolor(stackstyle['facecolor'])
        # check visibility of entire object
        if(not self.visibility):
          handles = [handleStack, handleStackNeg]
          for entry in handles:
            if(hasattr(entry, 'set_visible')):
              entry.set_visible(False)

        # special treatment for fill of error markers
        if(not self.Stackstyle['doFill']):
          for entry in [handleStack, handleStackNeg]:
            if(hasattr(entry, 'set_facecolor')):
              entry.set_facecolor('none')

        # set z order
        for entry in [handleStack, handleStackNeg]:
          if(hasattr(entry, 'set_zorder')):
            entry.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset)
 
    # apply text styles
    if(handleText != None):
      textstyle = self.Textstyle
      if((handleText != None) and (len(handleText))):
        # apply styles
        for key in textstyle:
          method = 'set_' + key
          if(hasattr(handleText[0], method)):
            value = textstyle[key]###; safeFont = 'DejaVu Sans'
            for item in handleText:
              method2call = getattr(item, method)
              method2call(value)
              if(key == 'fontname'):
                # test last element descent, should be larger than zero
                _, _, descent = item._get_layout(self.parent.plotArea.matplot.canvas.renderer)
                if(not (descent > 0)):
                  self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                  value = SAFE_FONT
                  method2call(value)
        # check visibility of entire object
        if(not self.visibility):
          for item in handleText:
            if(hasattr(item, 'set_visible')):
              item.set_visible(False)
        # set z order of entire object
        for item in handleText:
          if(hasattr(item, 'set_zorder')):
            item.set_zorder(self.zorder + self.parent.zOffset + self.relativeZOrderText)

    # apply violin styles
    if(handleViolin != None):
      violinstyle = self.Violinstyle
      if(handleViolin != None):
        # for boxplot ensure that mean has no symbol
        if(self.Violinstyle['mode'] == 2):
          for item in handleViolin['means']:
            item.set_marker('None')
        for entry in handleViolin:
          if(entry in violinstyle):
            if(type(handleViolin[entry]) == type([])):
              # slave cmins to cmaxes and cmedians to cmeans
              if(entry in ['cmaxes', 'cmins']):
                targetList = handleViolin['cmaxes'] + handleViolin['cmins']
              elif(entry in ['cmeans', 'cmedians']):
                targetList = handleViolin['cmeans'] + handleViolin['cmedians']
              else:
                targetList = handleViolin[entry]
              for key in violinstyle[entry]:
                method, value = 'set_' + key, violinstyle[entry][key]
                if((key == 'visible') and (entry in ['cmeans', 'cmedians'])):
                  for item in handleViolin[entry]:
                    if(hasattr(item, method)):
                      method2call = getattr(item, method)
                      method2call(value)
                else:
                  for item in targetList:
                    if(hasattr(item, method)):
                      method2call = getattr(item, method)
                      if((key == 'marker') and (value in CUSTOM_MARKERS)):
                        method2call(CUSTOM_MARKERS[value])
                      elif((key == 'marker') and (not (value in matplotlib.lines.Line2D.markers))):
                        # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                        method2call('o')
                      else:
                        method2call(value)
                    if(('color' in key) and (len(value) == 4) and (key != 'facecolor')):
                      item.set_alpha(value[-1])
              for item in targetList:
                if(hasattr(item, 'set_zorder')):
                  item.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset + self.relativeZOrderViolin)
                if(hasattr(item, 'set_pickradius')):
                  item.set_pickradius(PICK_TOLERANCE)
            else:
              # slave cmins to cmaxes and cmedians to cmeans
              if(entry in ['cmaxes', 'cmins']):
                targetList = ['cmaxes', 'cmins']
              elif(entry in ['cmeans', 'cmedians']):
                targetList = ['cmeans', 'cmedians']
              else:
                targetList = [entry]
              for key in violinstyle[entry]:
                method, value = 'set_' + key, violinstyle[entry][key]
                if((key == 'visible') and (entry in ['cmeans', 'cmedians', 'cmins', 'cmaxes'])):
                  if(hasattr(handleViolin[entry], method)):
                    method2call = getattr(handleViolin[entry], method)
                    method2call(value)
                else:
                  for item in targetList:
                    if(hasattr(handleViolin[item], method)):
                      method2call = getattr(handleViolin[item], method)
                      method2call(value)
                    if(('color' in key) and (len(value) == 4) and (key != 'facecolor')):
                      handleViolin[item].set_alpha(value[-1])
              for item in targetList:
                if(hasattr(handleViolin[item], 'set_zorder')):
                  handleViolin[item].set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset + self.relativeZOrderViolin)
                if(hasattr(handleViolin[item], 'set_pickradius')):
                  handleViolin[item].set_pickradius(PICK_TOLERANCE)
    
        # special treatment for fills of various items
        if(('bodies' in handleViolin) and (not self.Violinstyle['bodies']['doFill'])):
          for entry in handleViolin['bodies']:
            if(hasattr(entry, 'set_facecolor')):
              entry.set_facecolor('none')
        if(('fliers' in handleViolin) and (not self.Violinstyle['fliers']['doFill'])):
          for entry in handleViolin['fliers']:
            if(hasattr(entry, 'set_markerfacecolor')):
              entry.set_markerfacecolor('none')
        if(('boxes' in handleViolin) and (not self.Violinstyle['boxes']['boxFill'])):
          for entry in handleViolin['boxes']:
            if(hasattr(entry, 'set_color')):
              entry.set_color(self.Violinstyle['boxes']['edgecolor'])

        # set z order
        for key in handleViolin:
          if(type(handleViolin[key]) == type([])):
            for entry in handleViolin[key]:
              entry.set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset + self.relativeZOrderViolin)
          else:
            handleViolin[key].set_zorder(self.zorder + self.relativeZOrderBar + self.parent.zOffset + self.relativeZOrderViolin)

    # scatter graphics
    if(handleScatter != None):
      style = self.Scatterstyle
      for key in style:
        method = 'set_' + key
        if((key != 'marker') and hasattr(handleScatter, method)):
          if(key.startswith('sizes')):
            if(self.Scatterstyle['sizes__type'] in ['x', 'y']):
              sizesMultiply = self.__dict__[self.Scatterstyle['sizes__type']]
            elif(self.Scatterstyle['sizes__type'] == 'labels'):
              sizesMultiply = self.convertLabels2Numbers(labels2=False)
            elif(self.Scatterstyle['sizes__type'] == 'labels2'):
              sizesMultiply = self.convertLabels2Numbers(labels2=True)
            else:
              sizesMultiply = np.ones(len(self.x))
            
            handleScatter.set_sizes((self.Scatterstyle['sizes'] ** 2) * np.abs(sizesMultiply))
          elif(key.startswith('facecolor') or key.startswith('doFill')):
            if(not self.Scatterstyle['doFill']):
              # set transparent fill in this case
              facecolors = [[0.] * 4] * len(self.x)
            elif(self.Scatterstyle['facecolor__type'] == 'const'):
              facecolors = [self.Scatterstyle['facecolor']] * len(self.x)
            else:
              if(self.Scatterstyle['facecolor__type'] in ['x', 'y']):
                colorValue = self.__dict__[self.Scatterstyle['facecolor__type']]
              elif(self.Scatterstyle['facecolor__type'] == 'labels'):
                colorValue = self.convertLabels2Numbers(labels2=False)
              elif(self.Scatterstyle['facecolor__type'] == 'labels2'):
                colorValue = self.convertLabels2Numbers(labels2=True)
              else:
                colorValue = np.ones(len(self.x))
              # get color values
              facecolors = self.convertNumbers2ColorMap(colorValue=colorValue, cmap=self.Scatterstyle['facecolor__cmap'])
            handleScatter.set_facecolor(facecolors)
          elif(key.startswith('edgecolor')):
            if(self.Scatterstyle['edgecolor__type'] == 'const'):
              facecolors = [self.Scatterstyle['edgecolor']] * len(self.x)
            else:
              if(self.Scatterstyle['edgecolor__type'] in ['x', 'y']):
                colorValue = self.__dict__[self.Scatterstyle['edgecolor__type']]
              elif(self.Scatterstyle['edgecolor__type'] == 'labels'):
                colorValue = self.convertLabels2Numbers(labels2=False)
              elif(self.Scatterstyle['edgecolor__type'] == 'labels2'):
                colorValue = self.convertLabels2Numbers(labels2=True)
              else:
                colorValue = np.ones(len(self.x))
              # get color values
              facecolors = self.convertNumbers2ColorMap(colorValue=colorValue, cmap=self.Scatterstyle['edgecolor__cmap'])
            handleScatter.set_edgecolor(facecolors)
          else:
            method2call = getattr(handleScatter, method)
            method2call([self.Scatterstyle[key]])

      # set z order
      if(hasattr(handleScatter, 'set_zorder')):
        handleScatter.set_zorder(self.zorder + self.parent.zOffset)
    
  def drawMe(self, redraw=True, rescale=True):
    # causes data to be drawn on canvas
    self.handleData, self.handleErr, self.handleErrShady, self.handleBar, self.handleStack, self.handleStackNeg, self.handleText, self.handleViolin, self.handleScatter = self.parent.plotArea.plotData(self.value(),\
      dataobject = self, handleData = self.handleData, handleErr = self.handleErr, handleErrShady=self.handleErrShady, handleBar=self.handleBar,\
      handleStack=self.handleStack, handleStackNeg=self.handleStackNeg, handleText=self.handleText, handleViolin=self.handleViolin, handleScatter=self.handleScatter, redraw=False, rescale=rescale)
    if (self.handleData != None):
      self.handleData.set_visible(self.visibility)
    if (self.handleBar != None):
      for entry in self.handleBar.patches:
        entry.set_visible(self.visibility)
    if (self.handleStack != None):
      self.handleStack.set_visible(self.visibility)
    if (self.handleStackNeg != None):
      self.handleStackNeg.set_visible(self.visibility)
    if (self.handleErrShady != None):
      self.handleErrShady.set_visible(self.visibility)
    if (self.handleText != None):
      for entry in self.handleText:
        entry.set_visible(self.visibility)
    if(self.handleViolin != None):
      for key in self.handleViolin:
        if((key in self.Violinstyle) and ('visible' in self.Violinstyle[key])):
          modState = self.Violinstyle[key]['visible']
        else:
          modState = True
        if(type(self.handleViolin[key]) == type([])):
          for entry in self.handleViolin[key]:
            entry.set_visible(self.visibility and modState)
        else:
          self.handleViolin[key].set_visible(self.visibility and modState)
    if (self.handleScatter != None):
      self.handleScatter.set_visible(self.visibility)

    # causes data to be drawn on canvas
    self.handleData_div, self.handleErr_div, self.handleErrShady_div, self.handleBar_div, self.handleStack_div, self.handleStackNeg_div, self.handleText_div, self.handleViolin_div, self.handleScatter_div = self.parent.plotArea.plotData(self.value(),\
      dataobject = self, handleData = self.handleData_div, handleErr = self.handleErr_div, handleErrShady=self.handleErrShady_div, handleBar=self.handleBar_div,\
      handleStack=self.handleStack_div, handleStackNeg=self.handleStackNeg_div, handleText=self.handleText_div, handleViolin=self.handleViolin_div, handleScatter=self.handleScatter_div, redraw=False, rescale=rescale, splitX=True)
    # set visibility
    if (self.handleData_div != None):
      self.handleData_div.set_visible(self.visibility)
    if (self.handleBar_div != None):
      for entry in self.handleBar_div.patches:
        entry.set_visible(self.visibility)
    if (self.handleStack_div != None):
      self.handleStack_div.set_visible(self.visibility)
    if (self.handleStackNeg_div != None):
      self.handleStackNeg_div.set_visible(self.visibility)
    if (self.handleErrShady_div != None):
      self.handleErrShady_div.set_visible(self.visibility)
    if (self.handleText_div != None):
      for entry in self.handleText_div:
        entry.set_visible(self.visibility)
    if(self.handleViolin_div != None):
      for key in self.handleViolin_div:
        if((key in self.Violinstyle) and ('visible' in self.Violinstyle[key])):
          modState = self.Violinstyle[key]['visible']
        else:
          modState = True
        if(type(self.handleViolin_div[key]) == type([])):
          for entry in self.handleViolin_div[key]:
            entry.set_visible(self.visibility and modState)
        else:
          self.handleViolin_div[key].set_visible(self.visibility and modState)
    if (self.handleScatter_div != None):
      self.handleScatter_div.set_visible(self.visibility)

    # redraw? -- update legend in this go if required (this is needed to draw stack/bar entries for legend when line/marker invisible)
    value = self.parent.plotArea.legendVisible
    if(value):
      self.parent.plotArea.setLegend(value=value, redraw=False) 
      
    if(redraw and self.visibility):
      self.parent.plotArea.dataplotwidget.myRefresh()
      
  def drawMeResid(self, redraw=True, rescale=True):
    # causes residuals to be drawn on canvas
    self.handleResid, self.parent.plotArea.handleResidZero, self.handleResidBar, self.handleResidStack,\
    self.handleResidStackNeg, self.handleResidText, self.handleResidScatter = self.parent.plotArea.plotResid(\
      dataobject = self, handleResid = self.handleResid, handleResidZero = self.parent.plotArea.handleResidZero,\
      handleResidBar = self.handleResidBar, handleResidStack = self.handleResidStack, handleResidStackNeg = self.handleResidStackNeg,\
      handleResidText = self.handleResidText, handleResidScatter=self.handleResidScatter, redraw=False, rescale=rescale)
    # set visibility
    if (self.handleResid != None):
      self.handleResid.set_visible(self.visibilityResid)
    if (self.handleResidBar != None):
      for entry in self.handleResidBar.patches:
        entry.set_visible(self.visibilityResid)
    if (self.handleResidStack != None):
      self.handleResidStack.set_visible(self.visibilityResid)
    if (self.handleResidStackNeg != None):
      self.handleResidStackNeg.set_visible(self.visibilityResid)
    if (self.handleResidText != None):
      for entry in self.handleResidText:
        entry.set_visible(self.visibilityResid)
    if (self.handleResidScatter != None):
      self.handleResidScatter.set_visible(self.visibilityResid)

    # causes residuals to be drawn on canvas
    self.handleResid_div, self.parent.plotArea.handleResidZero_div, self.handleResidBar_div, self.handleResidStack_div,\
    self.handleResidStackNeg_div, self.handleResidText_div, self.handleResidScatter_div = self.parent.plotArea.plotResid(\
      dataobject = self, handleResid = self.handleResid_div, handleResidZero = self.parent.plotArea.handleResidZero_div,\
      handleResidBar = self.handleResidBar_div, handleResidStack = self.handleResidStack_div, handleResidStackNeg = self.handleResidStackNeg_div,\
      handleResidText = self.handleResidText_div, handleResidScatter=self.handleResidScatter_div, redraw=False, splitX=True, rescale=rescale)
    # set visibility
    if (self.handleResid_div != None):
      self.handleResid_div.set_visible(self.visibilityResid)
    if (self.handleResidBar_div != None):
      for entry in self.handleResidBar_div.patches:
        entry.set_visible(self.visibilityResid)
    if (self.handleResidStack_div != None):
      self.handleResidStack_div.set_visible(self.visibilityResid)
    if (self.handleResidStackNeg_div != None):
      self.handleResidStackNeg_div.set_visible(self.visibilityResid)
    if (self.handleResidText_div != None):
      for entry in self.handleResidText_div:
        entry.set_visible(self.visibilityResid)
    if (self.handleResidScatter_div != None):
      self.handleResidScatter_div.set_visible(self.visibilityResid)

    # redraw?
    if(redraw and self.visibilityResid):
      self.parent.plotArea.residplotwidget.myRefresh()

  def getStyle(self):
    # returns the style object
    return self.style
    
  def getTextStyle(self):
    # returns the textstyle object
    return self.Textstyle
    
  def getBarStyle(self):
    # returns the barstyle object
    return self.Barstyle
    
  def getScatterStyle(self):
    # returns the scatterstyle object
    return self.Scatterstyle
    
  def getStackStyle(self):
    # returns the stackstyle object
    return self.Stackstyle
    
  def getViolinStyle(self):
    # returns the violinstyle object
    return self.Violinstyle
    
  def getResidStyle(self):
    # returns the residuals style object
    return self.Residstyle
    
  def getResidTextStyle(self):
    # returns the resid textstyle object
    return self.ResidTextstyle
    
  def getResidBarStyle(self):
    # returns the resid barstyle object
    return self.ResidBarstyle
    
  def getResidStackStyle(self):
    # returns the resid stackstyle object
    return self.ResidStackstyle

  def getResidScatterStyle(self):
    # returns the resid scatterstyle object
    return self.ResidScatterstyle
    
  def setDataStyleSet(self, index=0):
    # called to update an entire style set for a newly generated data object
    if(index in self.parent.dataStyles):
      for entry in self.parent.dataStyles[index]:
        if(entry == 'dataSet'):
          # filter out style designations that are already present in style
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.style)}
          self.style.update(useDict)
        elif(entry == 'dataError'):
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.Errorstyle)}
          self.Errorstyle.update(useDict)
        elif(entry == 'dataErrorShady'):
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.ErrorShadystyle)}
          self.ErrorShadystyle.update(useDict)
        elif(entry == 'dataBar'):
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.Barstyle)}
          self.Barstyle.update(useDict)
        elif(entry == 'dataStack'):
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.Stackstyle)}
          self.Stackstyle.update(useDict)
        elif(entry == 'dataText'):
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.Textstyle)}
          self.Textstyle.update(useDict)
        elif(entry == 'dataViolin'):
          # beware that Violinstyle contains nested dictionary -- have to go step by step
          for key in self.parent.dataStyles[index][entry]:
            if(key in self.Violinstyle):
              if(isinstance(self.parent.dataStyles[index][entry][key], dict)):
                # filter out style designations that are not already present in Violinstyle (required for legacy reasons)
                useDict = {kei:value for kei, value in self.parent.dataStyles[index][entry][key].items() if (kei in self.Violinstyle[key])}
                self.Violinstyle[key].update(useDict)
                ###print(useDict, index)
              else:
                self.Violinstyle[key] = self.parent.dataStyles[index][entry][key]
        elif(entry == 'dataScatter'):
          useDict = {key:value for key, value in self.parent.dataStyles[index][entry].items() if (key in self.Scatterstyle)}
          self.Scatterstyle.update(useDict)

  def setResidStyleSet(self, index=0):
    # called to update an entire style set for a newly generated data object
    if(index in self.parent.residStyles):
      for entry in self.parent.residStyles[index]:
        if(entry == 'resid'):
          self.Residstyle.update(self.parent.residStyles[index][entry])
        elif(entry == 'residBar'):
          self.ResidBarstyle.update(self.parent.residStyles[index][entry])
        elif(entry == 'residStack'):
          self.ResidStackstyle.update(self.parent.residStyles[index][entry])
        elif(entry == 'residText'):
          self.ResidTextstyle.update(self.parent.residStyles[index][entry])
        elif(entry == 'residScatter'):
          self.ResidScatterstyle.update(self.parent.residStyles[index][entry])

  def setResidStyle(self, key, value, redraw=True):
    # changes the style value
    if(key in self.Residstyle):
      if(self.Residstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Residstyle[key] = value
      # cause plot to be updated
      updateFlag = False
      # special treatment for markerfacecolor
      if((key != 'markerfacecolor') or self.Residstyle['doFill']):
        if(self.handleResid != None):
          method = 'set_' + key
          if (hasattr(self.handleResid, method)):
            method2call = getattr(self.handleResid, method)
            if((key == 'marker') and (value in CUSTOM_MARKERS)):
              method2call(CUSTOM_MARKERS[value])
            elif((key == 'marker') and not (value in matplotlib.lines.Line2D.markers)):
              # this is to ensure compatibility with future versions that may introduce markers we don't know yet
              method2call('o')
            elif((key in ['linestyle', 'linewidth']) and (self.Residstyle['linestyle'] != 'solid') and (self.Residstyle['linewidth'] < .01)):
              # need to set linestyle to 'solid' to avoid crash
              self.Residstyle['linestyle'] = 'solid'
              self.handleResid.set_linestyle('solid')
              self.handleResid.set_linewidth(self.Residstyle['linewidth'])
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            else:
              method2call(value)
            updateFlag = True
            # special treatment for capstyle
            if((key == 'dash_capstyle') and (hasattr(self.handleResid, 'set_solid_capstyle'))):
              method2call = getattr(self.handleResid, 'set_solid_capstyle')
              method2call(value)
  
        if(self.handleResid_div != None):
          method = 'set_' + key
          if (hasattr(self.handleResid_div, method)):
            method2call = getattr(self.handleResid_div, method)
            if((key == 'marker') and (value in CUSTOM_MARKERS)):
              method2call(CUSTOM_MARKERS[value])
            elif((key == 'marker') and not (value in matplotlib.lines.Line2D.markers)):
              # this is to ensure compatibility with future versions that may introduce markers we don't know yet
              method2call('o')
            elif((key in ['linestyle', 'linewidth']) and (self.Residstyle['linestyle'] != 'solid') and (self.Residstyle['linewidth'] < .01)):
              # need to set linestyle to 'solid' to avoid crash
              self.Residstyle['linestyle'] = 'solid'
              self.handleResid_div.set_linestyle('solid')
              self.handleResid_div.set_linewidth(self.Residstyle['linewidth'])
              self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
            else:
              method2call(value)
            updateFlag = True
            # special treatment for capstyle
            if((key == 'dash_capstyle') and (hasattr(self.handleResid_div, 'set_solid_capstyle'))):
              method2call = getattr(self.handleResid_div, 'set_solid_capstyle')
              method2call(value)
      else:
        # prevent update of plot
        changed = False

      if(redraw and self.visibilityResid and updateFlag):
        self.parent.plotArea.residplotwidget.myRefresh()
      return (changed and self.visibilityResid)

  def setResidBarStyle(self, key, value, redraw=True, rescale=True):
    # changes the barstyle value
    if(key in self.ResidBarstyle):
      if(self.ResidBarstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.ResidBarstyle[key] = value
      # cause plot to be updated
      if(key in ['showBar', 'offset']):
        self.drawMeResid(redraw=redraw, rescale=rescale)
      elif(key in ['width']):
        if((self.handleResidBar != None) or (self.handleResidBar != None)):
          # have to trigger redraw to avoid recentering of bars
          self.drawMeResid(redraw=redraw)
      else:
        updateFlag = False
        # check for potential problems with zero linewidth
        if((key in ['linestyle', 'linewidth']) and (self.ResidBarstyle['linestyle'] != 'solid') and (self.ResidBarstyle['linewidth'] < .01)):
          # need to set linestyle to 'solid' to avoid crash
          self.ResidBarstyle['linestyle'] = 'solid'
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          if(key == 'linestyle'):
            value = 'solid'
          else:
            for handleBar in [self.handleResidBar, self.handleResidBar_div]:
              if(handleBar != None):
                for patch in handleBar.patches:
                  patch.set_linestyle('solid')

        # special treatment for markerfacecolor
        if((key != 'facecolor') or self.ResidBarstyle['doFill']):
          if(self.handleResidBar != None):
            if(key in ['hatch', 'hatchMultiply']):
              key = 'hatch'
              value = self.ResidBarstyle['hatch'] * self.ResidBarstyle['hatchMultiply']
            method = 'set_'+key
            for entry in self.handleResidBar.patches:
              if(key in ['facecolor', 'facecolorAlt']):
                if (hasattr(entry, 'set_facecolor')):
                  if(entry.get_height() >= 0):
                    useValue = self.ResidBarstyle['facecolor']
                  else:
                    useValue = self.ResidBarstyle['facecolorAlt']
                  entry.set_facecolor(useValue)
              elif (hasattr(entry, method)):
                method2call = getattr(entry, method)
                method2call(value)
            updateFlag =True
  
          if(self.handleResidBar_div != None):
            if(key in ['hatch', 'hatchMultiply']):
              key = 'hatch'
              value = self.ResidBarstyle['hatch'] * self.ResidBarstyle['hatchMultiply']
            method = 'set_' + key
            for entry in self.handleResidBar_div.patches:
              if(key in ['facecolor', 'facecolorAlt']):
                if (hasattr(entry, 'set_facecolor')):
                  if(entry.get_height() >= 0):
                    useValue = self.ResidBarstyle['facecolor']
                  else:
                    useValue = self.ResidBarstyle['facecolorAlt']
                  entry.set_facecolor(useValue)
              elif (hasattr(entry, method)):
                method2call = getattr(entry, method)
                method2call(value)
            updateFlag =True
        else:
          # prevent update of plot
          changed = False

        if(redraw and self.visibilityResid and updateFlag):
          self.parent.plotArea.residplotwidget.myRefresh()
      return (changed and self.visibilityResid)

  def setResidStackStyle(self, key, value, redraw=True, rescale=True):
    # changes the stackstyle value
    if(key in self.ResidStackstyle):
      if(self.ResidStackstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.ResidStackstyle[key] = value
      # cause plot to be updated
      if(key in ['showStack', 'bottom']):
        self.drawMeResid(redraw=redraw, rescale=rescale)
      else:
        updateFlag = False
        # check for potential problems with zero linewidth
        if((key in ['linestyle', 'linewidth']) and (self.ResidStackstyle['linestyle'] != 'solid') and (self.ResidStackstyle['linewidth'] < .01)):
          # need to set linestyle to 'solid' to avoid crash
          self.ResidStackstyle['linestyle'] = 'solid'
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          if(key == 'linestyle'):
            value = 'solid'
          else:
            for handleStack in [self.handleResidStack, self.handleResidStackNeg, self.handleResidStack_div, self.handleResidStackNeg_div]:
              if(hasattr(handleStack, 'set_linestyle')):
                handleStack.set_linestyle('solid')

        for handle in [self.handleResidStack, self.handleResidStackNeg, self.handleResidStack_div, self.handleResidStackNeg_div]:
          if(handle != None):
            updateFlag = True
            if(key in ['facecolor', 'facecolorAlt']):
              if (hasattr(handle, 'set_facecolor')):
                if(handle == self.handleResidStack):
                  useValue = self.ResidStackstyle['facecolor']
                else:
                  useValue = self.ResidStackstyle['facecolorAlt']
                handle.set_facecolor(useValue)
            else:
              if(key in ['hatch', 'hatchMultiply']):
                key = 'hatch'
                value = self.ResidStackstyle['hatch'] * self.ResidStackstyle['hatchMultiply']
              method = 'set_' + key
              if (hasattr(handle, method)):
                method2call = getattr(handle, method)
                method2call(value)
  
        if(redraw and self.visibilityResid and updateFlag):
          self.parent.plotArea.residplotwidget.myRefresh()
      return (changed and self.visibilityResid)

  def getResidLineStyle(self):
    # returns the residuals line style object
    return self.ResidLinestyle
    
  def setResidLineStyle(self, key, value, redraw=True):
    # changes the style value
    if(key in self.ResidLinestyle):
      if(self.ResidLinestyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.ResidLinestyle[key] = value
      # cause plot to be updated
      updateFlag = False
      if((key in ['linestyle', 'linewidth']) and (self.ResidLinestyle['linestyle'] != 'solid') and (self.ResidLinestyle['linewidth'] < .01)):
        # need to set linestyle to 'solid' to avoid crash
        self.ResidLinestyle['linestyle'] = 'solid'
        self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
        for handle in [self.parent.plotArea.handleResidZero, self.parent.plotArea.handleResidZero_div]:
          if(handle != None):
            handle.set_linestyle('solid')
            handle.set_linewidth(self.ResidLinestyle['linewidth'])
            if(handle.get_visible()):
              updateFlag = True
      else:
        for handle in [self.parent.plotArea.handleResidZero, self.parent.plotArea.handleResidZero_div]:
          if(handle != None):
            method = 'set_' + key
            if (hasattr(handle, method)):
              method2call = getattr(handle, method)
              method2call(value)
              # special treatment for capstyle
              if((key == 'dash_capstyle') and (hasattr(handle, 'set_solid_capstyle'))):
                method2call = getattr(handle, 'set_solid_capstyle')
                method2call(value)
            if(key == 'doGap'):
              if(hasattr(handle, 'set_gapcolor')):
                if(value):
                  handle.set_gapcolor(self.ResidLinestyle['gapcolor'])
                else:
                  handle.set_gapcolor('none')
            if(handle.get_visible()):
              updateFlag = True

      if(redraw and updateFlag):
        self.parent.plotArea.residplotwidget.myRefresh()
      return (changed and self.parent.plotArea.handleResidZero.get_visible())

  def setResidTextStyle(self, key, value, redraw=True, rescale=False):
    # changes the residuals textstyle value
    if(key in self.ResidTextstyle):
      if(self.ResidTextstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.ResidTextstyle[key] = value
      # cause plot to be updated
      if(key in ['showText', 'skip', 'padX', 'padY', 'type']):
        self.drawMeResid(redraw=redraw, rescale=rescale)
      else:
        updateFlag = False
        if(self.handleResidText != None):
          method = 'set_' + key
          if (hasattr(self.handleResidText[0], method)):
            ###safeFont = 'DejaVu Sans'
            updateFlag = True
            for item in self.handleResidText:
              method2call = getattr(item, method)
              method2call(value)
              # special treatment for font type to catch errors
              if(key == 'fontname'):
                _, _, descent = item._get_layout(self.parent.plotArea.matplot.canvas.renderer)
                if(not (descent > 0)):
                  self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                  value = SAFE_FONT
                  method2call(value)

        if(redraw and self.visibility and updateFlag):
          self.parent.plotArea.residplotwidget.myRefresh()
      return (changed and self.visibility)

  def toggleResidMarkerFill(self, value, redraw=True):
    # toggles resid marker fill
    if(self.Residstyle['doFill'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.Residstyle['doFill'] = value
    # adjust value
    if(value):
      value = self.Residstyle['markerfacecolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    if(self.handleResid != None):
      self.handleResid.set_markerfacecolor(value)
      updateFlag = True
    if(self.handleResid_div != None):
      self.handleResid_div.set_markerfacecolor(value)
      updateFlag = True

    if(redraw and self.visibilityResid and updateFlag):
      self.parent.plotArea.residplotwidget.myRefresh()
    return (changed and self.visibilityResid)

  def toggleMarkerFill(self, value, redraw=True):
    # toggles marker fill
    if(self.style['doFill'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.style['doFill'] = value
    # adjust value
    if(value):
      value = self.style['markerfacecolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    handles = [self.handleData, self.handleData_div]
    if(self.parent.plotArea.splitY):
      handles += [self.handlesBothAxes[i] for i in ['handleData', 'handleData_div'] if i in self.handlesBothAxes]
    for handleData in handles:
      if(handleData != None):
        handleData.set_markerfacecolor(value)
        updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return (changed and self.visibility)

  def toggleResidDoGap(self, value, redraw=True):
    # toggles resid line gap color
    if(self.Residstyle['doGap'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.Residstyle['doGap'] = value
    # adjust value
    if(value):
      value = self.Residstyle['gapcolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    if(self.handleResid != None):
      self.handleResid.set_gapcolor(value)
      updateFlag = True
    if(self.handleResid_div != None):
      self.handleResid_div.set_gapcolor(value)
      updateFlag = True

    if(redraw and self.visibilityResid and updateFlag):
      self.parent.plotArea.residplotwidget.myRefresh()
    return (changed and self.visibilityResid)

  def toggleDoGap(self, value, redraw=True):
    # toggles line gap color
    if(self.style['doGap'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.style['doGap'] = value
    # adjust value
    if(value):
      value = self.style['gapcolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    handles = [self.handleData, self.handleData_div]
    if(self.parent.plotArea.splitY):
      handles += [self.handlesBothAxes[i] for i in ['handleData', 'handleData_div'] if i in self.handlesBothAxes]
    for handleData in handles:
      if(handleData != None):
        handleData.set_gapcolor(value)
        updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return (changed and self.visibility)

  def toggleStackFill(self, value, redraw=True):
    # toggles stack fill
    if(self.Stackstyle['doFill'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.Stackstyle['doFill'] = value
    # cause plot to be updated
    updateFlag = False
    # toggle fill
    if(value):
      # call setStackStyle() to reactivate bar filling (have to do it like this b/c of positive/negative coloring)
      tempVal = self.Stackstyle['facecolor']
      self.Stackstyle['facecolor'] = 'tilt'
      self.setStackStyle('facecolor', tempVal, redraw=False, rescale=False)
      updateFlag = True
    else:
      handles = [self.handleStack, self.handleStack_div, self.handleStackNeg, self.handleStackNeg_div]
      if(self.parent.plotArea.splitY):
        handles += [self.handlesBothAxes[i] for i in ['handleStack', 'handleStack_div', 'handleStackNeg', 'handleStackNeg_div'] if i in self.handlesBothAxes]
      for handleStack in handles:
        if(handleStack != None):
          handleStack.set_facecolor('none')
          updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return (changed and self.visibility)

  def toggleResidStackFill(self, value, redraw=True):
    # toggles resid stack fill
    if(self.ResidStackstyle['doFill'] == value):
      redraw = False

    self.ResidStackstyle['doFill'] = value
    # cause plot to be updated
    updateFlag = False
    # toggle fill
    if(value):
      # call setResidStackStyle() to reactivate bar filling (have to do it like this b/c of positive/negative coloring)
      tempVal = self.ResidStackstyle['facecolor']
      self.ResidStackstyle['facecolor'] = 'tilt'
      self.setResidStackStyle('facecolor', tempVal, redraw=False, rescale=False)
      updateFlag = True
    else:
      for handleResidStack in (self.handleResidStack, self.handleResidStack_div, self.handleResidStackNeg, self.handleResidStackNeg_div):
        if(handleResidStack != None):
          handleResidStack.set_facecolor('none')
          updateFlag = True

    if(redraw and self.visibilityResid and updateFlag):
      self.parent.plotArea.residplotwidget.myRefresh()

  def toggleBarFill(self, value, redraw=True):
    # toggles bar fill
    if(self.Barstyle['doFill'] == value):
      redraw, changed = False, False
    else:
      changed = True
    self.Barstyle['doFill'] = value
    # cause plot to be updated
    updateFlag = False
    # toggle fill
    if(value):
      # call setBarStyle() to reactivate bar filling (have to do it like this b/c of positive/negative coloring)
      tempVal = self.Barstyle['facecolor']
      self.Barstyle['facecolor'] = 'tilt'
      self.setBarStyle('facecolor', tempVal, redraw=False, rescale=False)
      updateFlag = True
    else:
      handles = [self.handleBar, self.handleBar_div]
      if(self.parent.plotArea.splitY):
        handles += [self.handlesBothAxes[i] for i in ['handleBar', 'handleBar_div'] if i in self.handlesBothAxes]
      for handleBar in handles:
        if(handleBar != None):
          for entry in handleBar.patches:
            entry.set_facecolor('none')
          updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()
    return (changed and self.visibility)

  def toggleResidBarFill(self, value, redraw=True):
    # toggles resid bar fill
    if(self.ResidBarstyle['doFill'] == value):
      redraw = False

    self.ResidBarstyle['doFill'] = value
    # cause plot to be updated
    updateFlag = False
    # toggle fill
    if(value):
      # call setBarStyle() to reactivate bar filling (have to do it like this b/c of positive/negative coloring)
      tempVal = self.ResidBarstyle['facecolor']
      self.ResidBarstyle['facecolor'] = 'tilt'
      self.setResidBarStyle('facecolor', tempVal, redraw=False, rescale=False)
      updateFlag = True
    else:
      for handleResidBar in (self.handleResidBar, self.handleResidBar_div):
        if(handleResidBar != None):
          for entry in handleResidBar.patches:
            entry.set_facecolor('none')
          updateFlag = True

    if(redraw and self.visibilityResid and updateFlag):
      self.parent.plotArea.residplotwidget.myRefresh()

  def toggleFliersMarkerFill(self, value, redraw=True):
    # toggles fliers marker fill
    if(self.Violinstyle['fliers']['doFill'] == value):
      redraw = False

    self.Violinstyle['fliers']['doFill'] = value
    # adjust value
    if(value):
      value = self.Violinstyle['fliers']['markerfacecolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    handles = [self.handleViolin, self.handleViolin_div]
    if(self.parent.plotArea.splitY):
      handles += [self.handlesBothAxes[i] for i in ['handleViolin', 'handleViolin_div'] if i in self.handlesBothAxes]
    for handleViolin in handles:
      if(handleViolin != None):
        for entry in handleViolin['fliers']:
          entry.set_markerfacecolor(value)
        updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def setStyle(self, key, value, redraw=True):
    # changes the style value
    if(key in self.style):
      if(self.style[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.style[key] = value
      # cause plot to be updated
      updateFlag = False
      handles = [self.handleData, self.handleData_div]
      if(self.parent.plotArea.splitY):
        handles += [self.handlesBothAxes[i] for i in ['handleData', 'handleData_div'] if i in self.handlesBothAxes]
      # special treatment for markerfacecolor
      if((key != 'markerfacecolor') or self.style['doFill']):
        for handleData in handles:
          if(handleData != None):
            method = 'set_' + key
            if (hasattr(handleData, method)):
              method2call = getattr(handleData, method)
              if(key == 'marker'):
                if(value in CUSTOM_MARKERS):
                  method2call(CUSTOM_MARKERS[value])
                elif(not (value in matplotlib.lines.Line2D.markers)):
                  # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                  method2call('o')
                else:
                  method2call(value)
              elif((key in ['linestyle', 'linewidth']) and (self.style['linestyle'] != 'solid') and (self.style['linewidth'] < .01)):
                # need to set linestyle to 'solid' to avoid crash
                self.style['linestyle'] = 'solid'
                handleData.set_linestyle('solid')
                handleData.set_linewidth(self.style['linewidth'])
                self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
              else:
                method2call(value)
              updateFlag = True
      else:
        # prevent update of plot
        changed = False

      if(redraw and self.visibility and updateFlag):
        self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def setResidScatterStyle(self, key, value, redraw=True):
    # changes the scatter style value
    if(key in self.ResidScatterstyle):
      if(self.ResidScatterstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.ResidScatterstyle[key] = value
      # cause plot to be updated
      handles = [self.handleResidScatter, self.handleResidScatter_div]
      # special treatment for scatter markers
      if(key == 'marker'):
        self.drawMeResid(redraw=redraw, rescale=False)
        redraw = False
      else:
        for handleResidScatter in handles:
          if(handleResidScatter != None):
            if(key.startswith('sizes')):
              if(self.ResidScatterstyle['sizes__type'] in ['x', 'y']):
                sizesMultiply = self.__dict__[self.ResidScatterstyle['sizes__type']]
              elif(self.ResidScatterstyle['sizes__type'] == 'labels'):
                sizesMultiply = self.convertLabels2Numbers(labels2=False)
              elif(self.ResidScatterstyle['sizes__type'] == 'labels2'):
                sizesMultiply = self.convertLabels2Numbers(labels2=True)
              else:
                sizesMultiply = np.ones(len(self.x))
              
              handleResidScatter.set_sizes((self.ResidScatterstyle['sizes'] ** 2) * np.abs(sizesMultiply))
            elif(key.startswith('facecolor') or key.startswith('doFill')):
              if(not self.ResidScatterstyle['doFill']):
                # set transparent fill in this case
                facecolors = [[0.] * 4] * len(self.x)
              elif(self.ResidScatterstyle['facecolor__type'] == 'const'):
                facecolors = [self.ResidScatterstyle['facecolor']] * len(self.x)
              else:
                if(self.ResidScatterstyle['facecolor__type'] in ['x', 'y']):
                  colorValue = self.__dict__[self.ResidScatterstyle['facecolor__type']]
                elif(self.ResidScatterstyle['facecolor__type'] == 'labels'):
                  colorValue = self.convertLabels2Numbers(labels2=False)
                elif(self.ResidScatterstyle['facecolor__type'] == 'labels2'):
                  colorValue = self.convertLabels2Numbers(labels2=True)
                else:
                  colorValue = np.ones(len(self.x))
                # get color values
                facecolors = self.convertNumbers2ColorMap(colorValue=colorValue, cmap=self.ResidScatterstyle['facecolor__cmap'])
              handleResidScatter.set_facecolor(facecolors)
            elif(key.startswith('edgecolor')):
              if(self.ResidScatterstyle['edgecolor__type'] == 'const'):
                facecolors = [self.ResidScatterstyle['edgecolor']] * len(self.x)
              else:
                if(self.ResidScatterstyle['edgecolor__type'] in ['x', 'y']):
                  colorValue = self.__dict__[self.ResidScatterstyle['edgecolor__type']]
                elif(self.ResidScatterstyle['edgecolor__type'] == 'labels'):
                  colorValue = self.convertLabels2Numbers(labels2=False)
                elif(self.ResidScatterstyle['edgecolor__type'] == 'labels2'):
                  colorValue = self.convertLabels2Numbers(labels2=True)
                else:
                  colorValue = np.ones(len(self.x))
                # get color values
                facecolors = self.convertNumbers2ColorMap(colorValue=colorValue, cmap=self.ResidScatterstyle['edgecolor__cmap'])
              handleResidScatter.set_edgecolor(facecolors)
            else:
              method = 'set_' + key
              if(hasattr(handleResidScatter, method)):
                method2call = getattr(handleResidScatter, method)
                method2call([self.ResidScatterstyle[key]])

      if(redraw and self.visibility):
        self.parent.plotArea.residplotwidget.myRefresh()
      return (changed and self.visibility)

  def setScatterStyle(self, key, value, redraw=True):
    # changes the scatter style value
    if(key in self.Scatterstyle):
      if(self.Scatterstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Scatterstyle[key] = value
      # cause plot to be updated
      handles = [self.handleScatter, self.handleScatter_div]
      if(self.parent.plotArea.splitY):
        handles += [self.handlesBothAxes[i] for i in ['handleScatter', 'handleScatter_div'] if i in self.handlesBothAxes]
      # special treatment for scatter markers
      if(key == 'marker'):
        self.drawMe(redraw=redraw, rescale=False)
        redraw = False
      else:
        for handleScatter in handles:
          if(handleScatter != None):
            if(key.startswith('sizes')):
              if(self.Scatterstyle['sizes__type'] in ['x', 'y']):
                sizesMultiply = self.__dict__[self.Scatterstyle['sizes__type']]
              elif(self.Scatterstyle['sizes__type'] == 'labels'):
                sizesMultiply = self.convertLabels2Numbers(labels2=False)
              elif(self.Scatterstyle['sizes__type'] == 'labels2'):
                sizesMultiply = self.convertLabels2Numbers(labels2=True)
              else:
                sizesMultiply = np.ones(len(self.x))
              
              handleScatter.set_sizes((self.Scatterstyle['sizes'] ** 2) * np.abs(sizesMultiply))
            elif(key.startswith('facecolor') or key.startswith('doFill')):
              if(not self.Scatterstyle['doFill']):
                # set transparent fill in this case
                facecolors = [[0.] * 4] * len(self.x)
              elif(self.Scatterstyle['facecolor__type'] == 'const'):
                facecolors = [self.Scatterstyle['facecolor']] * len(self.x)
              else:
                if(self.Scatterstyle['facecolor__type'] in ['x', 'y']):
                  colorValue = self.__dict__[self.Scatterstyle['facecolor__type']]
                elif(self.Scatterstyle['facecolor__type'] == 'labels'):
                  colorValue = self.convertLabels2Numbers(labels2=False)
                elif(self.Scatterstyle['facecolor__type'] == 'labels2'):
                  colorValue = self.convertLabels2Numbers(labels2=True)
                else:
                  colorValue = np.ones(len(self.x))
                # get color values
                facecolors = self.convertNumbers2ColorMap(colorValue=colorValue, cmap=self.Scatterstyle['facecolor__cmap'])
              handleScatter.set_facecolor(facecolors)
            elif(key.startswith('edgecolor')):
              if(self.Scatterstyle['edgecolor__type'] == 'const'):
                facecolors = [self.Scatterstyle['edgecolor']] * len(self.x)
              else:
                if(self.Scatterstyle['edgecolor__type'] in ['x', 'y']):
                  colorValue = self.__dict__[self.Scatterstyle['edgecolor__type']]
                elif(self.Scatterstyle['edgecolor__type'] == 'labels'):
                  colorValue = self.convertLabels2Numbers(labels2=False)
                elif(self.Scatterstyle['edgecolor__type'] == 'labels2'):
                  colorValue = self.convertLabels2Numbers(labels2=True)
                else:
                  colorValue = np.ones(len(self.x))
                # get color values
                facecolors = self.convertNumbers2ColorMap(colorValue=colorValue, cmap=self.Scatterstyle['edgecolor__cmap'])
              handleScatter.set_edgecolor(facecolors)
            else:
              method = 'set_' + key
              if(hasattr(handleScatter, method)):
                method2call = getattr(handleScatter, method)
                method2call([self.Scatterstyle[key]])

      if(redraw and self.visibility):
        self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def convertNumbers2ColorMap(self, colorValue, cmap='jet'):
    # assigns color values based on value
    # select map
    try:
      cmap = matplotlib.cm.__dict__[cmap]
    except:
      self.parent.statusbar.showMessage('Warning, no color map named ' + cmap + ' found, switching to jet.', self.parent.STATUS_TIME, color='blue')
      cmap = matplotlib.cm.jet
    
    # prepare colorValues
    minC, maxC = np.min(colorValue), np.max(colorValue)
    diff = maxC - minC
    if(diff):
      cvalues = [(i - minC) / diff for i in colorValue]
    else:
      cvalues = [0.5] * len(colorValue)
    
    return [cmap(i) for i in cvalues]

  def convertLabels2Numbers(self, labels2=False):
    # helper functions used by scatter plot to convert labels to numerical values
    def convertolitsch(s):
      try:
        return float(s)
      except:
        try:
          import unicodedata
          return unicodedata.numeric(s)
        except:
          return 1.

    if(labels2):
      labels = [convertolitsch(s) for s in self.labels2]
    else:
      labels = [convertolitsch(s) for s in self.labels]
    if(len(labels) < len(self.x)):
      labels.extend([1.] * (-len(labels) + len(self.x)))
      self.parent.statusbar.showMessage('Where no label entry available, value set to default.', self.parent.STATUS_TIME, color='blue')
    
    return np.array(labels)

  def getErrorStyle(self):
    # returns the style object
    return self.Errorstyle
    
  def getErrorShadyStyle(self):
    # returns the style object
    return self.ErrorShadystyle

  def setErrorShadyStyle(self, key, value, redraw=True):
    # changes the errorstyle value
    if(key in self.ErrorShadystyle):
      if(self.ErrorShadystyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.ErrorShadystyle[key] = value
      # cause plot to be updated
      if((key == 'visible') and (value)):
        # if error shading is turned on, we need to issue redraw to generate shading object
        self.drawMe(redraw=redraw, rescale=False)
      else:
        updateFlag = False
        handles = [self.handleErrShady, self.handleErrShady_div]
        if(self.parent.plotArea.splitY):
          handles += [self.handlesBothAxes[i] for i in ['handleErrShady', 'handleErrShady_div'] if i in self.handlesBothAxes]
        for handleErrShady in handles:
          if(handleErrShady != None):
            method = 'set_' + key
            if (hasattr(handleErrShady, method)):
              method2call = getattr(handleErrShady, method)
              method2call(value)
              updateFlag = True

        if(redraw and self.visibility and updateFlag):
          self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)
    
  def setErrorStyle(self, key, value, redraw=True):
    # changes the errorstyle value
    if(key in self.Errorstyle):
      if(self.Errorstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Errorstyle[key] = value
      # cause plot to be updated
      updateFlag, redrawFlag = False, False
      handles = [self.handleErr, self.handleErr_div]
      if(self.parent.plotArea.splitY):
        handles += [self.handlesBothAxes[i] for i in ['handleErr', 'handleErr_div'] if i in self.handlesBothAxes]
      for handleErr in handles:
        if(handleErr != None):
          if(key in ['marker', 'markerX']):
            # deal w/ x-errors
            if(len(self.xerr)):
              # we have x errors, those should be the first two entries in self.handleErr[1]
              offset = 2
              if(key == 'markerX'):
                for entry in handleErr[1][:min(offset, len(handleErr[1]))]:
                  if(value in CUSTOM_MARKERS):
                    entry.set_marker(CUSTOM_MARKERS[value])
                  elif(not (value in matplotlib.lines.Line2D.markers)):
                    # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                    entry.set_marker('o')
                  else:
                    entry.set_marker(value)
            else:
              offset = 0
            # deal w/ y-errors
            if(len(self.yerr)):
              # we have y errors, those should be the first two entries in self.handleErr[1]
              if(key == 'marker'):
                for entry in handleErr[1][offset:]:
                  if(value in CUSTOM_MARKERS):
                    entry.set_marker(CUSTOM_MARKERS[value])
                  elif(not (value in matplotlib.lines.Line2D.markers)):
                    # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                    entry.set_marker('o')
                  else:
                    entry.set_marker(value)
          elif(key in ['errorInFront']):
            self.setZOrderError(state=value, redraw=redraw)
          elif(key in ['direction']):
            if(not redrawFlag):
              # need to handle this by redrawing curve
              self.drawMe(redraw=redraw, rescale=False)
              redraw, redrawFlag = False, True
          else:
            # special treatment for markerfacecolor
            if((key != 'markerfacecolor') or self.Errorstyle['doFill']):
              # extra check for set_visible() -- this prevents error display when entire data set not displayed
              if(key == 'visible'):
                value = value and self.visibility
              method = 'set_' + key
              for entry in handleErr[1]:
                # remember the organization of the error handles
                if (hasattr(entry, method)):
                  method2call = getattr(entry, method)
                  method2call(value)
              for entry in handleErr[2]:
                if (hasattr(entry, method)):
                  method2call = getattr(entry, method)
                  method2call(value)
            else:
              # prevent update of plot
              changed = False

          # don't connect the error bars by line
          for entry in handleErr[1]:
            entry.set_linestyle('None')
          # hide caps when error display is only in one direction
          if(self.Errorstyle['direction'] != 'both'):
            if(self.xerr.size):
              offset = 2
              if(self.Errorstyle['direction'] == 'positive'):
                for entry in handleErr[1][:min(offset - 1, len(handleErr[1]))]:
                  entry.set_visible(False)
              elif(self.Errorstyle['direction'] == 'negative'):
                for entry in handleErr[1][1:min(offset, len(handleErr[1]))]:
                  entry.set_visible(False)
            else:
              offset = 0
            if(self.yerr.size):
              if(self.Errorstyle['direction'] == 'positive'):
                for entry in handleErr[1][offset:-1]:
                  entry.set_visible(False)
              elif(self.Errorstyle['direction'] == 'negative'):
                for entry in handleErr[1][offset + 1:]:
                  entry.set_visible(False)
        
        # to be safe, always update (at some point revisit this)
        updateFlag = True
            
      if(redraw and self.visibility and updateFlag):
        self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def toggleErrorMarkerFill(self, value, redraw=True):
    # toggles marker fill
    if(self.Errorstyle['doFill'] == value):
      redraw = False

    self.Errorstyle['doFill'] = value
    # adjust value
    if(value):
      value = self.Errorstyle['markerfacecolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    handles = [self.handleErr, self.handleErr_div]
    if(self.parent.plotArea.splitY):
      handles += [self.handlesBothAxes[i] for i in ['handleErr', 'handleErr_div'] if i in self.handlesBothAxes]
    for handleErr in handles:
      if(handleErr != None):
        for entry in handleErr[1]:
          entry.set_markerfacecolor(value)
        updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def setBarStyle(self, key, value, redraw=True, rescale=True):
    # changes the barstyle value
    if(key in self.Barstyle):
      if(self.Barstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Barstyle[key] = value
      # cause plot to be updated
      if(key in ['showBar', 'offset', 'bottom']):
        self.drawMe(redraw=redraw, rescale=rescale)
      else:
        updateFlag, redrawFlag = False, False
        handles = [self.handleBar, self.handleBar_div]
        if(self.parent.plotArea.splitY):
          handles += [self.handlesBothAxes[i] for i in ['handleBar', 'handleBar_div'] if i in self.handlesBothAxes]
          
        # check for potential problems with zero linewidth
        if((key in ['linestyle', 'linewidth']) and (self.Barstyle['linestyle'] != 'solid') and (self.Barstyle['linewidth'] < .01)):
          # need to set linestyle to 'solid' to avoid crash
          self.Barstyle['linestyle'] = 'solid'
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          if(key == 'linestyle'):
            value = 'solid'
          else:
            for handleBar in handles:
              for patch in handleBar.patches:
                patch.set_linestyle('solid')

        # special treatment for markerfacecolor
        if((key != 'facecolor') or self.Barstyle['doFill']):
          for handleBar in handles:
            if(handleBar != None):
              if(key in ['width']):
                if(not redrawFlag):
                  # have to trigger redraw to avoid recentering of bars
                  self.drawMe(redraw=redraw, rescale=False)
                  redraw, redrawFlag = False, True
              else:
                if(key in ['hatch', 'hatchMultiply']):
                  key = 'hatch'
                  value = self.Barstyle['hatch'] * self.Barstyle['hatchMultiply']
                method = 'set_' + key
                for entry in handleBar.patches:
                  if(key in ['facecolor', 'facecolorAlt']):
                    if (hasattr(entry, 'set_facecolor')):
                      if(entry.get_height() >= 0):
                        useValue = self.Barstyle['facecolor']
                      else:
                        useValue = self.Barstyle['facecolorAlt']
                      entry.set_facecolor(useValue)
                  elif (hasattr(entry, method)):
                    method2call = getattr(entry, method)
                    method2call(value)
              updateFlag = True
        else:
          # prevent update of plot
          changed = False

        if(redraw and self.visibility and updateFlag):
          self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def setStackStyle(self, key, value, redraw=True, rescale=True):
    # changes the stackstyle value
    if(key in self.Stackstyle):
      if(self.Stackstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Stackstyle[key] = value
      # cause plot to be updated
      if(key in ['showStack', 'bottom']):
        self.drawMe(redraw=redraw, rescale=rescale)
      else:
        updateFlag = False
        handles = [self.handleStack, self.handleStackNeg, self.handleStack_div, self.handleStackNeg_div]
        handlesPos = [self.handleStack, self.handleStack_div]
        if(self.parent.plotArea.splitY):
          handles += [self.handlesBothAxes[i] for i in ['handleStack', 'handleStackNeg', 'handleStackNeg_div', 'handleStack_div'] if i in self.handlesBothAxes]
          handlesPos += [self.handlesBothAxes[i] for i in ['handleStack', 'handleStack_div'] if i in self.handlesBothAxes]

        # check for potential problems with zero linewidth
        if((key in ['linestyle', 'linewidth']) and (self.Stackstyle['linestyle'] != 'solid') and (self.Stackstyle['linewidth'] < .01)):
          # need to set linestyle to 'solid' to avoid crash
          self.Stackstyle['linestyle'] = 'solid'
          self.parent.statusbar.showMessage('Zero line width encountered, set line style to solid.', self.parent.STATUS_TIME, color='blue')
          if(key == 'linestyle'):
            value = 'solid'
          else:
            for handleStack in handles:
              if(hasattr(handleStack, 'set_linestyle')):
                handleStack.set_linestyle('solid')

        # special treatment for markerfacecolor
        if((key != 'facecolor') or self.Stackstyle['doFill']):
          for handle in handles:
            if(handle != None):
              updateFlag = True
              if(key in ['facecolor', 'facecolorAlt']):
                if (hasattr(handle, 'set_facecolor')):
                  if(handle in handlesPos):
                    useValue = self.Stackstyle['facecolor']
                  else:
                    useValue = self.Stackstyle['facecolorAlt']
                  handle.set_facecolor(useValue)
              else:
                if(key in ['hatch', 'hatchMultiply']):
                  key = 'hatch'
                  value = self.Stackstyle['hatch'] * self.Stackstyle['hatchMultiply']
                method = 'set_' + key
                if (hasattr(handle, method)):
                  method2call = getattr(handle, method)
                  method2call(value)
        else:
          # prevent update of plot
          changed = False
  
        if(redraw and self.visibility and updateFlag):
          self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def setTextStyle(self, key, value, redraw=True, rescale=False):
    # changes the textstyle value
    if(key in self.Textstyle):
      if(self.Textstyle[key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Textstyle[key] = value
      # cause plot to be updated
      if(key in ['showText', 'skip', 'padX', 'padY', 'type']):
        self.drawMe(redraw=redraw, rescale=rescale)
      else:
        updateFlag = False
        handles = [self.handleText, self.handleText_div]
        if(self.parent.plotArea.splitY):
          handles += [self.handlesBothAxes[i] for i in ['handleText', 'handleText_div'] if i in self.handlesBothAxes]
        for handleText in handles:
          if(handleText != None):
            method = 'set_' + key
            if (hasattr(handleText[0], method)):
              ###safeFont = 'DejaVu Sans'
              updateFlag = True
              for item in handleText:
                method2call = getattr(item, method)
                method2call(value)
                # special treatment for font type to catch errors
                if(key == 'fontname'):
                  _, _, descent = item._get_layout(self.parent.plotArea.matplot.canvas.renderer)
                  if(not (descent > 0)):
                    self.parent.statusbar.showMessage('Experiencing problems setting font ' + value + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
                    value = SAFE_FONT
                    method2call(value)

        if(redraw and self.visibility and updateFlag):
          self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def toggleViolinBodyFill(self, value, redraw=True):
    # toggles violin body fill
    if(self.Violinstyle['bodies']['doFill'] == value):
      redraw = False

    self.Violinstyle['bodies']['doFill'] = value
    # adjust value
    if(value):
      value = self.Violinstyle['bodies']['facecolor']
    else:
      value = 'none'
    # cause plot to be updated
    updateFlag = False
    handles = [self.handleViolin, self.handleViolin_div]
    if(self.parent.plotArea.splitY):
      handles += [self.handlesBothAxes[i] for i in ['handleViolin', 'handleViolin_div'] if i in self.handlesBothAxes]
    for handleViolin in handles:
      # toggle fill
      if(handleViolin != None):
        for entry in handleViolin['bodies']:
          entry.set_facecolor(value)
        updateFlag = True

    if(redraw and self.visibility and updateFlag):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def setViolinStyle(self, subtarget, key, value, redraw=True, rescale=True):
    # changes the violinstyle value
    if((subtarget != None) and (subtarget in self.Violinstyle) and (key in self.Violinstyle[subtarget])):
      if(self.Violinstyle[subtarget][key] == value):
        redraw, changed = False, False
      else:
        changed = True
      self.Violinstyle[subtarget][key] = value
      # cause plot to be updated
      if(key in ['width', 'type']):
        self.drawMe(redraw=redraw, rescale=rescale)
      else:
        updateFlag = False
        handles = [self.handleViolin, self.handleViolin_div]
        if(self.parent.plotArea.splitY):
          handles += [self.handlesBothAxes[i] for i in ['handleViolin', 'handleViolin_div'] if i in self.handlesBothAxes]
        # special treatment for markerfacecolor
        if((key != 'markerfacecolor') or self.Violinstyle['fliers']['doFill']):
          for handleViolin in handles:
            if(handleViolin != None):
              if(key == 'boxFill'):
                self.drawMe(redraw=redraw, rescale=False)
                redraw = False
              elif((subtarget == 'boxes') and (key == 'facecolor')):
                # need to redraw here to deal with monkey business related to how filled/unfilled boxes are drawn
                if(self.Violinstyle['boxes']['boxFill']):
                  self.drawMe(redraw=redraw, rescale=False)
                redraw = False
              else:
                if(key in ['hatch', 'hatchMultiply']):
                  key = 'hatch'
                  value = self.Violinstyle[subtarget]['hatch'] * self.Violinstyle[subtarget]['hatchMultiply']
                if((subtarget == 'boxes') and (key == 'edgecolor') and (not self.Violinstyle['boxes']['boxFill'])):
                  # this is ugly -- boxes with no fill respect color instead of edgecolor
                  key = 'color'
                method = 'set_' + key
                if(subtarget in handleViolin):
                  if(type(handleViolin[subtarget]) == type([])):
                    # for time being slave cmins to cmaxes
                    if((subtarget in ['cmaxes', 'cmins']) and (key != 'visible')):
                      targetList = handleViolin['cmaxes'] + handleViolin['cmins']
                    elif((subtarget in ['cmeans', 'cmedians']) and (key != 'visible')):
                      targetList = handleViolin['cmeans'] + handleViolin['cmedians']
                    elif((subtarget in ['means', 'medians']) and (key != 'visible')):
                      targetList = handleViolin['means'] + handleViolin['medians']
                    else:
                      targetList = handleViolin[subtarget]
                    for item in targetList:
                      if(hasattr(item, method)):
                        method2call = getattr(item, method)
                        if((key == 'marker') and (value in CUSTOM_MARKERS)):
                          method2call(CUSTOM_MARKERS[value])
                        elif((key == 'marker') and not (value in matplotlib.lines.Line2D.markers)):
                          # this is to ensure compatibility with future versions that may introduce markers we don't know yet
                          method2call('o')
                        else:
                          method2call(value)
                      elif((key == 'dash_capstyle') and hasattr(item, 'set_capstyle')):
                        # need this if box is plotted as patch
                        item.set_capstyle(value)
                      # alpha channel of color is somehow ignored -- need to manually set alpha for the entire object though :(
                      if(('color' in key) and (type(value) == type([]))):
                        item.set_alpha(value[-1])
                  else:
                    if((subtarget in ['cmaxes', 'cmins']) and (key != 'visible')):
                      targetList = ['cmaxes', 'cmins']
                    elif((subtarget in ['cmeans', 'cmedians']) and (key != 'visible')):
                      targetList = ['cmeans', 'cmedians']
                    elif((subtarget in ['means', 'medians']) and (key != 'visible')):
                      targetList = ['means', 'medians']
                    else:
                      targetList = [subtarget]
                      
                    for target in targetList:
                      if(hasattr(handleViolin[target], method)):
                        method2call = getattr(handleViolin[target], method)
                        method2call(value)
                      elif((key == 'dash_capstyle') and hasattr(handleViolin[target], 'set_capstyle')):
                        # need this if box is plotted as patch
                        handleViolin[target].set_capstyle(value)
              updateFlag = True
        else:
          # prevent update of plot
          changed = False

        if(redraw and self.visibility and updateFlag):
          self.parent.plotArea.dataplotwidget.myRefresh()
      return (changed and self.visibility)

  def setData(self, data=[], roles=[], labels=[], labels2=[]):
    # use this function to change the data value
    # check whether roles have been provided
    if(len(roles)):
      if(('x' in roles) and ('y' in roles)):
        self.x = data[:, roles.index('x')]
        self.y = data[:, roles.index('y')]
        if ('xerr' in roles):
          self.xerr = data[:, roles.index('xerr')]
        else:
          self.xerr = np.array([])
        if ('yerr' in roles):
          self.yerr = data[:, roles.index('yerr')]
        else:
          self.yerr = np.array([])
    else:
      if(len(data)):
        self.xerr = np.array([]); self.yerr = np.array([])
        array_dim = data.shape
        if (len(array_dim) > 1 and array_dim[1] > 1):
          # okay found valid data, now assign values
          self.x = data[:, 0]
          if (array_dim[1] == 2):
            self.y = data[:, 1]
          elif (array_dim[1] == 3):
            self.y = data[:, 1]
            self.yerr = data[:, 2]
          else:
            self.xerr = data[:, 1]
            self.y = data[:, 2]
            self.yerr = data[:, 3]
    
    # assign labels
    self.labels = labels
    self.labels2 = labels2
            
    # clear fval and resid (need to do this as x values and dimensions probably will have changed)
    self.fval = np.array([])
    self.resid = np.array([])
  
  def value(self):
    # use this function to read out value of data object
    data = {}
    if (len(self.x)):
      data['x'] = self.x
    if (len(self.y)):
      data['y'] = self.y
    if (len(self.xerr)):
      data['xerr'] = self.xerr
    if (len(self.yerr)):
      data['yerr'] = self.yerr
    if (len(self.fval)):
      data['fval'] = self.fval
    if (len(self.resid)):
      data['resid'] = self.resid
      
    return data

  def duplicateForSplit(self, xval=[], yval=[], xerr=[], yerr=[]):
    # duplicates current data set and draws it on split y axis
    # delete all previous items (if any)
    self.removeFromSplit()
    
    # assign x and y values
    if(not len(xval)):
      xval, yval, xerr, yerr = self.x, self.y, self.xerr, self.yerr
    xval, yval, xerr, yerr = np.array(xval), np.array(yval), np.array(xerr), np.array(yerr)

    if(self.onSecondAxes):
      axisobjectBase = 'ax'
    else:
      axisobjectBase = 'ax2'
      
    # intialize dictionary with None
    items = 'handleData,handleErr,handleErrShady,handleBar,handleStack,handleStackNeg,handleText,handleViolin,handleScatter'.split(',')
    for item in items:
      self.handlesBothAxes[item] = None
      self.handlesBothAxes[item + '_div'] = None
      
    # copy plot objects
    for extension in ['', '_div']:
      if(extension == ''):
        # first treat main x axes
        handleData, handleErr, handleErrShady, handleBar, handleStack, handleStackNeg, handleText, handleViolin, handleScatter =\
          self.handleData, self.handleErr, self.handleErrShady, self.handleBar, self.handleStack, self.handleStackNeg, self.handleText, self.handleViolin, self.handleScatter
      else:
        # then treat spit x axes
        handleData, handleErr, handleErrShady, handleBar, handleStack, handleStackNeg, handleText, handleViolin, handleScatter =\
          self.handleData_div, self.handleErr_div, self.handleErrShady_div, self.handleBar_div, self.handleStack_div, self.handleStackNeg_div, self.handleText_div, self.handleViolin_div, self.handleScatter_div
      axisobject = self.parent.plotArea.__dict__[axisobjectBase + extension]
        
      if(handleData != None):
        handle = 'handleData' + extension
        self.handlesBothAxes[handle], = axisobject.plot(xval, yval, 'ko')
      
      if(handleErr != None):
        handle = 'handleErr' + extension
        if(self.Barstyle['showBar']):
          useOffset = self.Barstyle['offset']
        else:
          useOffset = 0
          
        if(len(xerr)):
          if(len(yerr)):
            self.handlesBothAxes[handle] = axisobject.errorbar(xval + useOffset, yval, xerr=xerr, yerr=yerr, zorder=self.zorder + self.relativeZOrderError + self.parent.zOffset, capsize=1)
          else:
            self.handlesBothAxes[handle] = axisobject.errorbar(xval + useOffset, yval, xerr=xerr, zorder=self.zorder + self.relativeZOrderError + self.parent.zOffset, capsize=1)
        elif(len(yerr)):
          self.handlesBothAxes[handle] = axisobject.errorbar(xval + useOffset, yval, yerr=yerr, zorder=self.zorder + self.relativeZOrderError + self.parent.zOffset, capsize=1)
          
        if(self.handlesBothAxes[handle] != None):
          # don't draw the error curve
          self.handlesBothAxes[handle][0].set_linestyle('None')
          self.handlesBothAxes[handle][0].set_marker('None')

      if(handleErrShady != None):
        handle = 'handleErrShady' + extension
        if(self.Barstyle['showBar']):
          useOffset = self.Barstyle['offset']
        else:
          useOffset = 0
        useYerrPos, useYerrNeg = 0.0 * yerr, 0.0 * yerr
        
        if(self.Errorstyle['direction'] in ['both', 'positive']):
          useYerrPos = yerr
        if(self.Errorstyle['direction'] in ['both', 'negative']):
          useYerrNeg = yerr
        self.handlesBothAxes[handle] = axisobject.fill_between(xval + useOffset, yval - useYerrNeg, yval + useYerrPos, zorder=self.zorder + self.relativeZOrderError + self.parent.zOffset - 0.05)
  
      if(handleBar != None):
        handle = 'handleBar' + extension
        self.handlesBothAxes[handle] = axisobject.bar(xval + self.Barstyle['offset'], yval, align='center', width=self.Barstyle['width'])
        
      if((handleStack != None) or (handleStackNeg != None)):
        handlePos, handleNeg = 'handleStack' + extension, 'handleStackNeg' + extension
        posVals = (yval >= 0)
        posCount = [i for i in posVals if i]
        negCount = [i for i in posVals if (not i)]
        if(len(posCount)):
          self.handlesBothAxes[handlePos], = axisobject.stackplot(xval, yval, zorder=self.zorder + self.relativeZOrderBar + self.parent.zOffset, where=(yval >= 0), interpolate=True)
        if(len(negCount)):
          self.handlesBothAxes[handleNeg], = axisobject.stackplot(xval, yval, zorder=self.zorder + self.relativeZOrderBar + self.parent.zOffset, where=(yval <= 0), interpolate=True)

      if((handleText != None) and len(handleText)):
        handle = 'handleText' + extension
        self.handlesBothAxes[handle] = []
        for entry in handleText:
          position, text = entry.get_position(), entry.get_text()
          self.handlesBothAxes[handle].append(axisobject.text(position[0], position[1], text, horizontalalignment='center',
                              verticalalignment='bottom', zorder=self.zorder + self.relativeZOrderText + self.parent.zOffset))
          self.handlesBothAxes[handle][-1].set_clip_on(True)

      if((handleViolin != None) and len(handleViolin)):
        handle = 'handleViolin' + extension
        # get data organized into vectors
        xvector, yvector = self.collateViolinData()
        if(len(xvector) * len(yvector)):
          if(self.Violinstyle['mode'] == 1):
            if(('bodies' in self.Violinstyle) and ('width' in self.Violinstyle['bodies'])):
              useWidth = self.Violinstyle['bodies']['width']
            else:
              useWidth = 0.5
            self.handlesBothAxes[handle] = axisobject.violinplot(yvector, xvector, vert=True, showmeans=True, showextrema=True, showmedians=True, widths=useWidth)
          else:
            if(('boxes' in self.Violinstyle) and ('width' in self.Violinstyle['boxes'])):
              useWidth = self.Violinstyle['boxes']['width']
            else:
              useWidth = 0.5
            if(('boxes' in self.Violinstyle) and ('boxFill' in self.Violinstyle['boxes'])):
              useFill = self.Violinstyle['boxes']['boxFill']
            else:
              useFill = False
            # check matplotlib version
            version = matplotlib.__version__.split('.')
            if((len(version) > 1) and ((int(version[0]) > 3) or ((int(version[0]) == 3) and (int(version[1]))))):
              self.handlesBothAxes[handle] = axisobject.boxplot(yvector, positions=xvector, manage_ticks=False, showmeans=True, showfliers=True, sym=None, widths=useWidth, patch_artist=useFill, flierprops={'marker': 'o'})
            else:
              self.handlesBothAxes[handle] = axisobject.boxplot(yvector, positions=xvector, manage_xticks=False, showmeans=True, showfliers=True, sym=None, widths=useWidth, patch_artist=useFill, flierprops={'marker': 'o'})
      
      if(handleScatter != None):
        handle = 'handleScatter' + extension
        self.handlesBothAxes[handle] = axisobject.scatter(xval, yval, marker=self.Scatterstyle['marker'])
      
      # apply all styles
      self.styleMe(handleData=self.handlesBothAxes['handleData' + extension], handleErr=self.handlesBothAxes['handleErr' + extension],\
                   handleErrShady=self.handlesBothAxes['handleErrShady' + extension], handleBar=self.handlesBothAxes['handleBar' + extension],\
                   handleStack=self.handlesBothAxes['handleStack' + extension], handleStackNeg=self.handlesBothAxes['handleStackNeg' + extension],\
                   handleText=self.handlesBothAxes['handleText' + extension], handleViolin=self.handlesBothAxes['handleViolin' + extension],\
                   handleScatter=self.handlesBothAxes['handleScatter' + extension])
   
  def removeFromSplit(self):
    # removes duplicated data set from split y axes
    for handle in ['handleText', 'handleText_div']:
      if(handle in self.handlesBothAxes):
        if((self.handlesBothAxes[handle] != None) and len(self.handlesBothAxes[handle])):
          for entry in self.handlesBothAxes[handle]:
            entry.remove()
          self.handlesBothAxes.pop(handle)

    for handle in ['handleViolin', 'handleViolin_div']:
      if(handle in self.handlesBothAxes):
        if((self.handlesBothAxes[handle] != None) and len(self.handlesBothAxes[handle])):
          for entry in self.handlesBothAxes[handle]:
            if(type(self.handlesBothAxes[handle][entry]) == type([])):
              for entry2 in self.handlesBothAxes[handle][entry]:
                if(hasattr(entry2, 'remove')):
                  entry2.remove()
            elif(hasattr(self.handlesBothAxes[handle][entry], 'remove')):
              self.handlesBothAxes[handle][entry].remove()
          self.handlesBothAxes.pop(handle)

    for handle in ['handleErr', 'handleErr_div']:
      if(handle in self.handlesBothAxes):
        if((self.handlesBothAxes[handle] != None) and len(self.handlesBothAxes[handle])):
          if(hasattr(self.handlesBothAxes[handle][0], 'remove')):
            self.handlesBothAxes[handle][0].remove()
          if(type(self.handlesBothAxes[handle][1]) in [type([]), type(())]):
            for entry in self.handlesBothAxes[handle][1]:
              if(hasattr(entry, 'remove')):
                entry.remove()
          if(type(self.handlesBothAxes[handle][2]) in [type([]), type(())]):
            for entry in self.handlesBothAxes[handle][2]:
              if(hasattr(entry, 'remove')):
                entry.remove()
        self.handlesBothAxes.pop(handle)
   
    for handle in self.handlesBothAxes:
      if(hasattr(self.handlesBothAxes[handle], 'remove')):
        self.handlesBothAxes[handle].remove()
    self.handlesBothAxes = {}

class scatterStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False):
    super(scatterStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode
    
    # valid line styles
    self.markerstyles = []
    self.markerstyles.extend(matplotlib.lines.Line2D.markers)
    # weed out duplicate blank items
    blankItems = [i for i in self.markerstyles if i in ['', ' ', 'None', None]]
    while (len(blankItems) - 1):
      killItem = blankItems[-1]
      self.markerstyles = [i for i in self.markerstyles if i != killItem]
      blankItems = blankItems[:-1]
    self.orderedstyles = ['None', None, ' ', '', '.', ',', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', '*', 'h', 'H', '+',\
                          'x', 'D', 'd', '|', '_', 'P', 'X', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    self.orderedstyles = [i for i in self.orderedstyles if (i in self.markerstyles)]
    # disable advanced styles for scatter as this causes crash
    ###if(self.parent.parent.parent.graphicsarea.advancedExport['advancedGraphics']):
    ###  self.orderedstyles += [i for i in CUSTOM_MARKERS]

    self.variableTypes = ['const', 'x', 'y', 'labels', 'labels2']
    self.colormaps = sorted(plt.colormaps(), key=str.casefold)
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      if(self.residMode):
        getScatterStyle = self.target.getResidScatterStyle()
      else:
        getScatterStyle = self.target.getScatterStyle()
      # have to make a copy to not directly alter the original object
      for entry in getScatterStyle:
        self.style[entry] = getScatterStyle[entry]
    else:
      self.style = {'showScatter': False, 'facecolor': [.5, .6, .7, 1.], 'facecolor__type': 'y', 'facecolor__cmap': 'jet', 'doFill': True,
                    'edgecolor': [.2, .2, .2, 1.], 'edgecolor__type': 'const', 'edgecolor__cmap': 'jet', 'linewidth': .5,
                    'sizes': 10, 'sizes__type': 'const', 'marker': 'o'}
    
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    # heading
    self.displayGroup = QWidgetMac()
    self.vLayout.addWidget(self.displayGroup)
    self.hLayout0 = QtWidgets.QHBoxLayout(self.displayGroup)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.scatterStyleLabel = QtWidgets.QLabel()
    self.scatterStyleLabel.setText('Scatter')
    font = self.scatterStyleLabel.font()
    font.setBold(True)
    self.scatterStyleLabel.setFont(font)
    self.scatterStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.scatterStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.scatterStyleLabel)
    # display scatter?
    self.displayLabel = QPushButtonCheckable()
    self.displayLabel.setText('Show?')
    self.displayLabel.setToolTip('Show scatter graphics of object')
    self.displayLabel.setChecked(self.style['showScatter'])
    self.displayLabel.clicked.connect(partial(self.setDisplay, self.target))
    self.displayLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.displayLabel)

    # scatter size
    self.scatterSizeGroup = QWidgetMac()
    self.vLayout.addWidget(self.scatterSizeGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.scatterSizeGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.scatterSizeLabel = QtWidgets.QLabel('Size')
    self.scatterSizeLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.scatterSizeLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.scatterSizeLabel)
    # marker size spin box
    self.scatterSizeSpin = QDoubleSpinClick()
    self.hLayout.addWidget(self.scatterSizeSpin)
    self.scatterSizeSpin.setMinimum(0.0)
    self.scatterSizeSpin.setMaximum(100.0)
    self.scatterSizeSpin.setValue(self.style['sizes'])
    self.scatterSizeSpin.setToolTip('Scatter size of object')
    self.scatterSizeSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterSizeSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterSizeSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'sizes', self.scatterSizeSpin, False))
    # variable scatter size
    varyBy = QtWidgets.QLabel('vary by')
    varyBy.setMaximumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
    varyBy.setMinimumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(varyBy)
    self.scatterTypeBox = QComboBoxMac()
    self.scatterTypeBox.setToolTip('Variable scatter size of object')
    self.scatterTypeBox.addItems(self.variableTypes)
    self.scatterTypeBox.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterTypeBox.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['sizes__type'] in self.variableTypes):
      currindex = self.variableTypes.index(self.style['sizes__type'])
      self.scatterTypeBox.setCurrentIndex(currindex)
    else:
      self.scatterTypeBox.setCurrentIndex(0)
    self.scatterTypeBox.activated.connect(partial(self.selectStyle, self.target, 'sizes__type', self.scatterTypeBox))
    self.hLayout.addWidget(self.scatterTypeBox)

    # scatter fill
    self.scatterFaceColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.scatterFaceColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.scatterFaceColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.scatterFaceColorLabel = QPushButtonCheckable()
    self.scatterFaceColorLabel.setText('Face')
    self.scatterFaceColorLabel.setToolTip('Show scatter fill of object')
    self.scatterFaceColorLabel.setChecked(self.style['doFill'])
    self.scatterFaceColorLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.scatterFaceColorLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.scatterFaceColorLabel.clicked.connect(partial(self.toggleScatterFill, self.target, self.scatterFaceColorLabel))
    self.hLayout2.addWidget(self.scatterFaceColorLabel)
    # scatter facecolor
    self.scatterFaceColorButton = QPushButtonColor()
    self.scatterFaceColorButton.setToolTip('Scatter fill color of object')
    self.scatterFaceColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['facecolor'][0:3]]
    self.scatterFaceColorButton.setMyColor(*colorvalue)
    self.scatterFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.scatterFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.scatterFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.scatterFaceColorButton.clicked.connect(partial(self.setColor, target=self.target, key='facecolor'))
    self.hLayout2.addWidget(self.scatterFaceColorButton)

    # variable scatter color
    self.scatterFaceColorGroup2 = QWidgetMac()
    self.vLayout.addWidget(self.scatterFaceColorGroup2)
    self.hLayout25 = QtWidgets.QHBoxLayout(self.scatterFaceColorGroup2)
    self.hLayout25.setContentsMargins(0, 0, 0, 0)
    self.hLayout25.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout25.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    spacer = QtWidgets.QLabel('')
    spacer.setMaximumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout25.addWidget(spacer)
    #
    varyBy = QtWidgets.QLabel('vary by')
    varyBy.setMaximumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
    varyBy.setMinimumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout25.addWidget(varyBy)
    self.scatterVariableColorBox = QComboBoxMac()
    self.scatterVariableColorBox.setToolTip('Variable scatter fill color of object')
    self.scatterVariableColorBox.addItems(self.variableTypes)
    self.scatterVariableColorBox.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterVariableColorBox.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['facecolor__type'] in self.variableTypes):
      currindex = self.variableTypes.index(self.style['facecolor__type'])
      self.scatterVariableColorBox.setCurrentIndex(currindex)
    else:
      self.scatterVariableColorBox.setCurrentIndex(0)
    self.scatterVariableColorBox.activated.connect(partial(self.selectStyle, self.target, 'facecolor__type', self.scatterVariableColorBox))
    self.hLayout25.addWidget(self.scatterVariableColorBox)
    # variable scatter color map
    self.scatterVariableColorMapBox = CmapButton()
    self.scatterVariableColorMapBox.setToolTip('Variable scatter fill color map of object')
    self.scatterVariableColorMapBox.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.scatterVariableColorMapBox.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['facecolor__cmap'] in self.colormaps):
      self.scatterVariableColorMapBox.setCmap(self.style['facecolor__cmap'])
    else:
      self.scatterVariableColorMapBox.setCmap(self.colormaps[0])
    self.scatterVariableColorMapBox.setParent(self)
    self.scatterVariableColorMapBox.setKey('facecolor__cmap')
    self.scatterVariableColorMapBox.setCmapList(self.colormaps)
    self.scatterVariableColorMapBox.clicked.connect(partial(self.scatterCallback, self.scatterVariableColorMapBox, False, 'facecolor__cmap'))
    self.hLayout25.addWidget(self.scatterVariableColorMapBox)

    # scatter edge
    self.scatterEdgeGroup = QWidgetMac()
    self.vLayout.addWidget(self.scatterEdgeGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.scatterEdgeGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.scatterEdgeLabel = QtWidgets.QLabel('Edge')
    self.scatterEdgeLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.scatterEdgeLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.scatterEdgeLabel)
    # scatter size spin box
    self.scatterEdgeWidthSpin = QDoubleSpinClick()
    self.hLayout3.addWidget(self.scatterEdgeWidthSpin)
    self.scatterEdgeWidthSpin.setMinimum(0.0)
    self.scatterEdgeWidthSpin.setMaximum(100.0)
    self.scatterEdgeWidthSpin.setValue(self.style['linewidth'])
    self.scatterEdgeWidthSpin.setToolTip('Scatter edge width of object')
    self.scatterEdgeWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterEdgeWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterEdgeWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'linewidth', self.scatterEdgeWidthSpin, False))
    # scatter edgecolor
    self.scatterEdgeColorButton = QPushButtonColor()
    self.scatterEdgeColorButton.setToolTip('Marker edge color of object')
    self.scatterEdgeColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['edgecolor'][0:3]]
    self.scatterEdgeColorButton.setMyColor(*colorvalue)
    self.scatterEdgeColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.scatterEdgeColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.scatterEdgeColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.scatterEdgeColorButton.clicked.connect(partial(self.setColor, target=self.target, key='edgecolor'))
    self.hLayout3.addWidget(self.scatterEdgeColorButton)
    
    # variable scatter edge color
    self.scatterEdgeGroup2 = QWidgetMac()
    self.vLayout.addWidget(self.scatterEdgeGroup2)
    self.hLayout35 = QtWidgets.QHBoxLayout(self.scatterEdgeGroup2)
    self.hLayout35.setContentsMargins(0, 0, 0, 0)
    self.hLayout35.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout35.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    spacer = QtWidgets.QLabel('')
    spacer.setMaximumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout35.addWidget(spacer)
    #
    varyBy = QtWidgets.QLabel('vary by')
    varyBy.setMaximumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
    varyBy.setMinimumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout35.addWidget(varyBy)
    self.scatterVariableEdgeBox = QComboBoxMac()
    self.scatterVariableEdgeBox.setToolTip('Variable scatter edge color of object')
    self.scatterVariableEdgeBox.addItems(self.variableTypes)
    self.scatterVariableEdgeBox.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.scatterVariableEdgeBox.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['edgecolor__type'] in self.variableTypes):
      currindex = self.variableTypes.index(self.style['edgecolor__type'])
      self.scatterVariableEdgeBox.setCurrentIndex(currindex)
    else:
      self.scatterVariableEdgeBox.setCurrentIndex(0)
    self.scatterVariableEdgeBox.activated.connect(partial(self.selectStyle, self.target, 'edgecolor__type', self.scatterVariableEdgeBox))
    self.hLayout35.addWidget(self.scatterVariableEdgeBox)
    # variable scatter edge color map
    self.scatterVariableEdgeMapBox = CmapButton()
    self.scatterVariableEdgeMapBox.setToolTip('Variable scatter edge color map of object')
    self.scatterVariableEdgeMapBox.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.scatterVariableEdgeMapBox.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['edgecolor__cmap'] in self.colormaps):
      self.scatterVariableEdgeMapBox.setCmap(self.style['edgecolor__cmap'])
    else:
      self.scatterVariableEdgeMapBox.setCmap(self.colormaps[0])
    self.scatterVariableEdgeMapBox.setParent(self)
    self.scatterVariableEdgeMapBox.setKey('edgecolor__cmap')
    self.scatterVariableEdgeMapBox.setCmapList(self.colormaps)
    self.scatterVariableEdgeMapBox.clicked.connect(partial(self.scatterCallback, self.scatterVariableEdgeMapBox, False, 'edgecolor__cmap'))
    self.hLayout35.addWidget(self.scatterVariableEdgeMapBox)

    # scatter style
    self.scatterStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.scatterStyleGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.scatterStyleGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.scatterStyleLabel = QtWidgets.QLabel('Style')
    self.scatterStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.scatterStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.scatterStyleLabel)
    self.comboStyle = MarkerButton()
    self.comboStyle.setToolTip('Scatter symbol of object')
    self.comboStyle.clicked.connect(partial(self.scatterCallback, self.comboStyle, True, 'marker'))
    if(self.style['marker'] in self.orderedstyles):
      self.comboStyle.setSymbol(self.style['marker'])
    else:
      self.comboStyle.setSymbol(self.orderedstyles[0])
    self.comboStyle.setSymbolList(self.orderedstyles)
    self.comboStyle.setParent(self)
    self.comboStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.comboStyle)    

  def scatterCallback(self, callButton=None, flag=True, key=''):
    # opens menu to access scatter symbol or cmap dialog
    if(flag):
      self.menu = MarkerMenu(self)
    else:
      self.menu = CmapMenu(parent=self, key=key)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # bottom align position of QMenu
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)

  def setMarker(self, symbol='', alternate=False):
    # sets scatter symbol to selected type
    # argument alternate is of no consequence as scatter only uses conventional markers
    # leave lest problems with occur w/ MarkerMenu()
    if((symbol in self.orderedstyles) and (symbol != self.style['marker'])):
      self.style['marker'] = symbol
      self.comboStyle.setSymbol(symbol)

      if(self.residMode):
        self.target.setResidScatterStyle('marker', symbol, redraw=True)
      else:
        changed = self.target.setScatterStyle('marker', symbol, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def setCmap(self, cmap='', key=''):
    # sets scatter cmap to selected one
    if((cmap in self.colormaps) and (key in self.style) and (cmap != self.style[key])):
      self.style[key] = cmap
      if(key == 'edgecolor__cmap'):
        self.scatterVariableEdgeMapBox.setCmap(cmap)
      elif(key == 'facecolor__cmap'):
        self.scatterVariableColorMapBox.setCmap(cmap)

      if(self.residMode):
        self.target.setResidScatterStyle(key, cmap, redraw=True)
      else:
        changed = self.target.setScatterStyle(key, cmap, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def setColor(self, target = None, key = None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in target.style):
        prevColor = [int(255. * i) for i in target.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'facecolor'):
          self.scatterFaceColorButton.setMyColor(*value)
        elif(key == 'edgecolor'):
          self.scatterEdgeColorButton.setMyColor(*value)
        value = [i / 255. for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidScatterStyle(key, value, redraw=True)
        else:
          changed = target.setScatterStyle(key, value, redraw=False)
          # update legend if needed
          self.updateLegend(redraw=changed)

      # bring back the QMenu
      self.parent.togglePersist(False)

  def toggleScatterFill(self, target=None, entryfield=None):
    # toggles whether to fill scatter or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['doFill'] = value
      if(self.residMode):
        target.setResidScatterStyle('doFill', value, redraw=True)
      else:
        changed = target.setScatterStyle('doFill', value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def selectStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      index = entryfield.currentIndex()
      if(key == 'marker'):
        value = self.orderedstyles[index]
      else:
        value = self.variableTypes[index]
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidScatterStyle(key, value, redraw=True)
      else:
        changed = target.setScatterStyle(key, value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def alterStyle(self, target=None, key=None, entryfield=None, integer=False):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        if(integer):
          value = int(entryfield.value())
        else:
          value = float(entryfield.value())
        originalvalue = value
      except:
        if(integer):
          value = 0
          originalvalue = 1
        else:
          value = 0.0
          originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidScatterStyle(key, value, redraw=True)
      else:
        changed = target.setScatterStyle(key, value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def setDisplay(self, target=None):
    # toggled display of scatter graphics
    if(target != None):
      self.style['showScatter'] = self.displayLabel.isChecked()
      if(self.residMode):
        target.toggleResidScatter(self.style['showScatter'])
      else:
        target.toggleScatter(self.style['showScatter'])

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

# define subclassed button for cmap button
class CmapButton(QPushButtonMac):
  def __init__(self, *args, **kwargs):
    super(QPushButtonMac, self).__init__(*args, **kwargs)
    self.myCmap = ''
    self.myCmapList = []
    self.setText('')
    self.parent = None
    self.key = ''
    
  def setCmap(self, cmap=''):
    self.myCmap = cmap
    # update button label
    self.update()

  def setCmapList(self, cmapList=[]):
    if(type(cmapList) == type([])):
      self.myCmapList = cmapList

  def setParent(self, parent=None):
    self.parent = parent
    
  def setKey(self, key=''):
    self.key = key

  def paintEvent(self, event):
    # regular draw event
    QPushButtonMac.paintEvent(self, event)
    
    # draw little triangle
    PSIZE = 4
    s = self.size()
    buttonWidth, buttonHeight = s.width(), s.height()
    offsetX, offsetY = int(buttonWidth - int(PSIZE + 4)), int(buttonHeight / 2 - int(PSIZE / 2 - 2))
    polygon = QtGui.QPolygon([QtCore.QPoint(int(offsetX), int(offsetY)), QtCore.QPoint(int(offsetX + int(PSIZE)), int(offsetY)),\
                              QtCore.QPoint(int(offsetX + int(PSIZE / 2)), int(offsetY + int(PSIZE)))])
    qp = QtGui.QPainter()
    if(qp is not None):
      qp.begin(self)
      qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
      qp.setPen(QtCore.Qt.PenStyle.NoPen)
      # draw dropdown triangle
      qp.setBrush(QtGui.QColor(*UI_TEXT_COLOR))
      qp.drawPolygon(polygon)
      
      # draw color button with preview of gradient
      BDIM = buttonHeight - 2
      try:
        cmap = matplotlib.cm.__dict__[self.myCmap]
        grad2 = QtGui.QLinearGradient(0, 0, BDIM, 0)
        for i in range(11):
          grad2.setColorAt(0.1 * i, QtGui.QColor(*[int(255 * i) for i in cmap(0.1 * i)]))
        qp.setBrush(QtGui.QBrush(grad2))
        qp.drawRoundedRect(1, 1, BDIM, BDIM, 3, 3)
      except:
        pass
  
      # overlay gradient
      pen = QtGui.QPen()
      pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
      pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
      pen.setColor(QtGui.QColor(171, 171, 171, 255))
      pen.setWidth(1)
      pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
      qp.setPen(pen)
  
      grad = QtGui.QLinearGradient(0, 0, 0, BDIM)
      grad.setColorAt(0, QtGui.QColor(255, 255, 255, 150))
      grad.setColorAt(0.2, QtGui.QColor(255, 255, 255, 0))
      grad.setColorAt(0.8, QtGui.QColor(0, 0, 0, 0))
      grad.setColorAt(1, QtGui.QColor(0, 0, 0, 80))
      qp.setBrush(QtGui.QBrush(grad))
  
      qp.drawRoundedRect(1, 1, BDIM, BDIM, 3, 3)
      
      # write text (do this via painter such as have left alignment w/o breaking Fusion style)
      pen = QtGui.QPen()
      pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
      pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
      pen.setColor(QtGui.QColor(*UI_TEXT_COLOR))
      pen.setWidth(1)
      pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
      qp.setPen(pen)
      textHeight = self.fontMetrics().height()
      qp.drawText(4 + BDIM, textHeight, str(self.myCmap))
      # fini
      qp.end()
    
  def wheelEvent(self, event):
    # handles wheel events to change cmap (mimics qcombobox style)
    if(self.myCmap in self.myCmapList):
      current = self.myCmapList.index(self.myCmap)
      if((event.angleDelta().y() < 0) and (current < len(self.myCmapList) - 1) and (self.parent != None)):
        self.parent.setCmap(self.myCmapList[current + 1], key=self.key)
      elif((event.angleDelta().y() > 0) and (current > 0) and (self.parent != None)):
        self.parent.setCmap(self.myCmapList[current - 1], key=self.key)
        
  def keyPressEvent(self, event):
    # handles up and down arrow
    if(event.key() in [QtCore.Qt.Key.Key_Up, QtCore.Qt.Key.Key_Down, QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Home,\
       QtCore.Qt.Key.Key_End, QtCore.Qt.Key.Key_PageDown, QtCore.Qt.Key.Key_PageUp]):
      if(event.key() in [QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter]):
        self.parent.scatterCallback(self, flag=False)
      elif(event.key() in [QtCore.Qt.Key.Key_Home, QtCore.Qt.Key.Key_PageUp]):
        self.parent.setMarker(self.myCmapList[0])
      elif(event.key() in [QtCore.Qt.Key.Key_End, QtCore.Qt.Key.Key_PageDown]):
        self.parent.setCmap(self.myCmapList[-1], key=self.key)
      elif(self.myCmap in self.myCmapList):
        current = self.myCmapList.index(self.myCmap)
        if((event.key() == QtCore.Qt.Key.Key_Down) and (current < len(self.myCmapList) - 1) and (self.parent != None)):
          self.parent.setCmap(self.myCmapList[current + 1], key=self.key)
        elif((event.key() == QtCore.Qt.Key.Key_Up) and (current > 0) and (self.parent != None)):
          self.parent.setCmap(self.myCmapList[current - 1], key=self.key)
      else:
        event.ignore()
    else:
      # activate normal event handling
      QPushButtonMac.keyPressEvent(self, event)

# define item delegate for coloring of specific cells
class CmapDelegate(QtWidgets.QStyledItemDelegate):
  def __init__(self):
    super(CmapDelegate, self).__init__()

  def paint(self, painter, option, index):
    item = index.data(QtCore.Qt.ItemDataRole.DisplayRole)
    if(item in plt.colormaps()):
      painter.save()
      s, x, y = option.rect.size(), option.rect.x(), option.rect.y()
      height, width = s.height(), s.width()
      painter.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
      painter.setPen(QtCore.Qt.PenStyle.NoPen)
      
      # check if object is under mouse
      if(option.state & QtWidgets.QStyle.StateFlag.State_MouseOver):
        painter.setBrush(QtGui.QBrush(QtGui.QColor(0, 120, 215)))
        painter.drawRoundedRect(option.rect, 2, 2)

      # check if object has focus
      if(option.state & QtWidgets.QStyle.StateFlag.State_HasFocus):
        painter.setBrush(QtGui.QBrush(QtGui.QColor(0, 120, 215)))
        painter.drawRoundedRect(option.rect, 2, 2)
#        pen = QtGui.QPen()
#        pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
#        pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
#        pen.setColor(QtGui.QColor(171, 171, 171, 255))
#        pen.setWidth(2)
#        pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
#        painter.setPen(pen)
#        painter.setBrush(QtCore.Qt.BrushStyle.NoBrush)
#        painter.drawRoundedRect(option.rect, 1, 1)
#        painter.setPen(QtCore.Qt.PenStyle.NoPen)
        
      # draw color button with preview of gradient
      BDIM = height - 2
      try:
        cmap = matplotlib.cm.__dict__[item]
        grad2 = QtGui.QLinearGradient(x + 2, y, x + 2 + BDIM, y)
        for i in range(11):
          grad2.setColorAt(0.1 * i, QtGui.QColor(*[int(255 * i) for i in cmap(0.1 * i)]))
        painter.setBrush(QtGui.QBrush(grad2))
        painter.drawRoundedRect(x + 2, y + 1, BDIM, BDIM, 3, 3)
      except:
        pass
  
      # overlay gradient
      pen = QtGui.QPen()
      pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
      pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
      pen.setColor(QtGui.QColor(171, 171, 171, 255))
      pen.setWidth(1)
      pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
      painter.setPen(pen)
  
      grad = QtGui.QLinearGradient(x, y, x, y + BDIM)
      grad.setColorAt(0, QtGui.QColor(255, 255, 255, 150))
      grad.setColorAt(0.2, QtGui.QColor(255, 255, 255, 0))
      grad.setColorAt(0.8, QtGui.QColor(0, 0, 0, 0))
      grad.setColorAt(1, QtGui.QColor(0, 0, 0, 80))
      painter.setBrush(QtGui.QBrush(grad))
  
      painter.drawRoundedRect(x + 2, y + 1, BDIM, BDIM, 3, 3)
      painter.restore()
    else:
      # regular draw event
      QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)

# define cmap dialog
class CmapMenu(KuhMenu):
  def __init__(self, *args, **kwargs):
    for item in ['parent', 'key']:
      if(item in kwargs):
        self.__dict__[item] = kwargs[item]
        del kwargs[item]
      else:
        self.__dict__[item] = None
    super(CmapMenu, self).__init__(*args, **kwargs)
    self.data = []

    # float validator
    self.validFloat = MyValidFloat()
    self.validInt = MyValidInt()

    # set up GUI
    self.buildRessource()

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    
    # argh -- we will need a new table model for this
    if(self.parent != None):
      items = self.parent.colormaps
      nitems = len(items)
      if(nitems):
        # determine dimensions of table
        ncolumns = int(np.ceil(nitems ** 0.5))
        nrows = int(np.ceil(nitems / ncolumns))
        # set up data
        self.data = [['tilt' for i in range(ncolumns)] for i in range(nrows)]
        for i in range(nitems):
          self.data[i % nrows][int(i / nrows)] = items[i]
        # determine maximum length of entries in each column
        maxColumnLength = [max([len(str(self.data[i][j])) if (self.data[i][j] != 'tilt') else 0 for i in range(nrows)]) for j in range(ncolumns)]
        self.model = StandardItemModel(nrows, ncolumns, self)
        self.model.populateModel(self.data)
        # introduce tooltips for cmaps
        if(myapp.toolTipsShow):
          for row in range(nrows):
            for col in range(ncolumns):
              if(self.data[row][col] != 'tilt'):
                self.model.item(row, col).setToolTip(self.data[row][col])
        self.model.layoutChanged.emit()
        # set up view
        self.tableView = QtWidgets.QTableView()
        self.tableView.setModel(self.model)
        self.tableView.setFixedWidth(int(BASE_SIZE + 2))
        self.tableView.horizontalHeader().hide()
        self.tableView.verticalHeader().hide()
        self.tableView.setShowGrid(False)
        self.tableView.setStyleSheet("QTableView::item:hover {color: white; background-color: #0078d7; border-style: solid; border-radius: 4px;}\n\
                                      QTableView::item {background-color: transparent; text-align: center;}\n\
                                      QTableView::item:focus {background-color: transparent; border-width: 2px; border-color: #444444; border-style: solid; border-radius: 4px;}\n\
                                      QTableView::item:hover:focus {color: white; background-color: #0078d7; border-width: 2px; border-color: #444444; border-style: solid; border-radius: 4px;}\n\
                                      QTableView {border-style: solid; border-radius: 4px;}\n\
                                      QTableView {background-color: rgba" + UI_ALTERNATE_BASE_STRING + ";}")
        self.tableView.clicked.connect(self.clickHandler)
        self.tableView.activated.connect(self.activateHandler)
        self.tableView.setSelectionMode(self.tableView.SelectionMode.NoSelection)
        self.tableView.setEditTriggers(self.tableView.EditTrigger.NoEditTriggers)
        # set delegate
        self.tableView.setItemDelegate(CmapDelegate())
        # set focus to target item
        if((self.key in self.parent.style) and (self.parent.style[self.key] in items)):
          selectedIndex = items.index(self.parent.style[self.key])
          selectItem = self.model.index(selectedIndex % nrows, int(selectedIndex / nrows))
          self.tableView.selectionModel().setCurrentIndex(selectItem, QtCore.QItemSelectionModel.SelectionFlag.Current)
        
        self.vLayout.addWidget(self.tableView)
        self.tableView.setFocus()
        
        # resize QTableView
        [self.tableView.horizontalHeader().resizeSection(hheader, int(BASE_SIZE + 2)) for hheader in range(ncolumns)]
        [self.tableView.verticalHeader().resizeSection(vheader, int(BASE_SIZE + 2)) for vheader in range(nrows)]
        
        # now correctly size the QMenu -- seriously effing bugger this
        width, height = self.tableView.viewportSizeHint().width(), self.tableView.viewportSizeHint().height()
        self.tableView.setMinimumWidth(width)
        self.tableView.setMinimumHeight(height)
        self.adjustSize()
      
  def clickHandler(self, index):
    # callback for click on table
    column, row = index.column(), index.row()
    if(self.data[row][column] != 'tilt'):
      self.parent.setCmap(self.data[row][column], self.key)
      self.close()

  def activateHandler(self, index):
    # callback for enter press in table
    self.clickHandler(index)

class markerStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False):
    super(markerStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode
    
    # valid line styles
    self.markerstyles = []
    self.markerstyles.extend(matplotlib.lines.Line2D.markers)
    # weed out duplicate blank items
    blankItems = [i for i in self.markerstyles if i in ['', ' ', 'None', None]]
    while (len(blankItems) - 1):
      killItem = blankItems[-1]
      self.markerstyles = [i for i in self.markerstyles if i != killItem]
      blankItems = blankItems[:-1]
    self.orderedstyles = ['None', None, ' ', '', '.', ',', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', '*', 'h', 'H', '+',\
                          'x', 'D', 'd', '|', '_', 'P', 'X', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    self.orderedstyles = [i for i in self.orderedstyles if (i in self.markerstyles)]
    if(self.parent.parent.parent.graphicsarea.advancedExport['advancedGraphics']):
      self.orderedstyles += [i for i in CUSTOM_MARKERS]

    self.fillstyles = [i for i in matplotlib.lines.Line2D.fillStyles if not i in ['', 'none', 'None', None]]
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      if(self.residMode):
        getStyle = self.target.getResidStyle()
      else:
        getStyle = self.target.getStyle()
      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        self.style[entry] = getStyle[entry]
    else:
      self.style['markerfacecolor'] = [1.0, 1.0, 1.0, 1.0]
      self.style['markerfacecoloralt'] = [0.0, 1.0, 1.0, 1.0]
      self.style['markeredgecolor'] = [0.0, 0.0, 0.0, 1.0]
      self.style['markeredgewidth'] = 1.0
      self.style['markersize'] = 8.0
      self.style['marker'] = 'o'
      self.style['fillstyle'] = 'full'
      self.style['markevery'] = 1
      self.style['doFill'] = True
    
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    # heading
    self.markerStyleLabel = QtWidgets.QLabel()
    self.markerStyleLabel.setText('Marker')
    font = self.markerStyleLabel.font()
    font.setBold(True)
    self.markerStyleLabel.setFont(font)
    self.vLayout.addWidget(self.markerStyleLabel)    
    
    # marker size
    self.markerSizeGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerSizeGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.markerSizeGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerSizeLabel = QtWidgets.QLabel('Size')
    self.markerSizeLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.markerSizeLabel)
    # marker size spin box
    self.markerSizeSpin = QDoubleSpinClick()
    self.hLayout.addWidget(self.markerSizeSpin)
    self.markerSizeSpin.setMinimum(0.0)
    self.markerSizeSpin.setMaximum(100.0)
    self.markerSizeSpin.setValue(self.style['markersize'])
    self.markerSizeSpin.setToolTip('Marker size of object')
    self.markerSizeSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'markersize', self.markerSizeSpin, False))
    
    # marker facecolor
    self.markerFaceColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerFaceColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.markerFaceColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerFaceColorLabel = QPushButtonCheckable()
    self.markerFaceColorLabel.setText('Face')
    self.markerFaceColorLabel.setToolTip('Show marker fill of object')
    self.markerFaceColorLabel.setChecked(self.style['doFill'])
    self.markerFaceColorLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerFaceColorLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerFaceColorLabel.clicked.connect(partial(self.toggleMarkerFill, self.target, self.markerFaceColorLabel))
    self.hLayout2.addWidget(self.markerFaceColorLabel)
    
    self.markerFaceColorButton = QPushButtonColor()
    self.markerFaceColorButton.setToolTip('Marker fill color of object')
    self.markerFaceColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['markerfacecolor'][0:3]]
    self.markerFaceColorButton.setMyColor(*colorvalue)
    self.markerFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerFaceColorButton.clicked.connect(partial(self.setColor, target=self.target, key='markerfacecolor'))
    self.hLayout2.addWidget(self.markerFaceColorButton)
      
    # marker edgecolor
    self.markerEdgeColorLabel = QtWidgets.QLabel('Edge')
    self.markerEdgeColorLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeColorLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.markerEdgeColorLabel)

    self.markerEdgeColorButton = QPushButtonColor()
    self.markerEdgeColorButton.setToolTip('Marker edge color of object')
    self.markerEdgeColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['markeredgecolor'][0:3]]
    self.markerEdgeColorButton.setMyColor(*colorvalue)
    self.markerEdgeColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerEdgeColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerEdgeColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerEdgeColorButton.clicked.connect(partial(self.setColor, target=self.target, key='markeredgecolor'))
    self.hLayout2.addWidget(self.markerEdgeColorButton)
      
    # marker edge width
    self.markerEdgeWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerEdgeWidthGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.markerEdgeWidthGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerEdgeWidthLabel = QtWidgets.QLabel('Edgewidth')
    self.markerEdgeWidthLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.markerEdgeWidthLabel)
    # marker edge width spin box
    self.markerEdgeWidthSpin = QDoubleSpinClick()
    self.hLayout4.addWidget(self.markerEdgeWidthSpin)
    self.markerEdgeWidthSpin.setMinimum(0.0)
    self.markerEdgeWidthSpin.setMaximum(100.0)
    self.markerEdgeWidthSpin.setValue(self.style['markeredgewidth'])
    self.markerEdgeWidthSpin.setToolTip('Marker edge width of object')
    self.markerEdgeWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'markeredgewidth', self.markerEdgeWidthSpin, False))
    
    # marker style
    self.markerStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerStyleGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.markerStyleGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerStyleLabel = QtWidgets.QLabel('Style')
    self.markerStyleLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerStyleLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout5.addWidget(self.markerStyleLabel)
    self.comboStyle = MarkerButton()
    self.comboStyle.setToolTip('Marker symbol of object')
    self.comboStyle.clicked.connect(partial(self.markerCallback, self.comboStyle))
    if(self.style['marker'] in self.orderedstyles):
      self.comboStyle.setSymbol(self.style['marker'])
    else:
      self.comboStyle.setSymbol(self.orderedstyles[0])
    self.comboStyle.setSymbolList(self.orderedstyles)
    self.comboStyle.setParent(self)
    self.comboStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout5.addWidget(self.comboStyle)    

    # marker fill style
    self.markerFillStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerFillStyleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.markerFillStyleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.markerFillStyleLabel = QtWidgets.QLabel('Fillstyle')
    self.markerFillStyleLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerFillStyleLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.markerFillStyleLabel)
    self.comboFillStyle = QComboBoxMac()
    self.comboFillStyle.setToolTip('Marker fill style of object')
    for entry in self.fillstyles:
      self.comboFillStyle.addItem(str(entry))
    if(self.style['fillstyle'] in self.fillstyles):
      currindex = self.fillstyles.index(self.style['fillstyle'])
    else:
      currindex = 0
    self.comboFillStyle.setCurrentIndex(currindex)
    self.comboFillStyle.activated.connect(partial(self.selectStyle, self.target, 'fillstyle', self.comboFillStyle))
    self.comboFillStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboFillStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.comboFillStyle)    

    self.markerAltColorButton = QPushButtonColor()
    self.markerAltColorButton.setToolTip('Marker alternate fill color of object')
    self.markerAltColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['markerfacecoloralt'][0:3]]
    self.markerAltColorButton.setMyColor(*colorvalue)
    self.markerAltColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerAltColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerAltColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerAltColorButton.clicked.connect(partial(self.setColor, target=self.target, key='markerfacecoloralt'))
    self.hLayout3.addWidget(self.markerAltColorButton)

    # markevery group
    self.markerEveryGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerEveryGroup)
    self.hLayout6 = QtWidgets.QHBoxLayout(self.markerEveryGroup)
    self.hLayout6.setContentsMargins(0, 0, 0, 0)
    self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerEveryLabel = QtWidgets.QLabel('Mark every')
    self.markerEveryLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEveryLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.markerEveryLabel)
    # marker size spin box
    self.markerEverySpin = QSpinClick()
    self.hLayout6.addWidget(self.markerEverySpin)
    self.markerEverySpin.setMinimum(1)
    self.markerEverySpin.setMaximum(1000)
    self.markerEverySpin.setValue(self.style['markevery'])
    self.markerEverySpin.setToolTip('Put marker on every nth point of object')
    self.markerEverySpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEverySpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEverySpin.editingFinished.connect(partial(self.alterStyle, self.target, 'markevery', self.markerEverySpin, True))

  def setColor(self, target = None, key = None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in target.style):
        prevColor = [int(255 * i) for i in target.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'markerfacecolor'):
          self.markerFaceColorButton.setMyColor(*value)
        elif(key == 'markeredgecolor'):
          self.markerEdgeColorButton.setMyColor(*value)
        elif(key == 'markerfacecoloralt'):
          self.markerAltColorButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidStyle(key, value, redraw=True)
        else:
          changed = target.setStyle(key, value, redraw=False)
          # update legend if needed
          self.updateLegend(redraw=changed)

      # bring back the QMenu
      self.parent.togglePersist(False)

  def toggleMarkerFill(self, target = None, entryfield = None):
    # toggles whether to fill markers or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['doFill'] = value
      if(self.residMode):
        target.toggleResidMarkerFill(value, redraw=True)
      else:
        changed = target.toggleMarkerFill(value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)
    
  def selectStyle(self, target = None, key = None, entryfield = None):
    if((target != None) and (key != None)):
      index = entryfield.currentIndex()
      if(key == 'marker'):
        value = self.orderedstyles[index]
      else:
        value = self.fillstyles[index]
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStyle(key, value, redraw=True)
      else:
        changed = target.setStyle(key, value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def alterStyle(self, target=None, key=None, entryfield=None, integer=False):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        if(integer):
          value = int(entryfield.value())
        else:
          value = float(entryfield.value())
        originalvalue = value
      except:
        if(integer):
          value = 0
          originalvalue = 1
        else:
          value = 0.0
          originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStyle(key, value, redraw=True)
      else:
        changed = target.setStyle(key, value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

  def markerCallback(self, callButton=None):
    # opens menu to access advanced configuration options
    self.menu = MarkerMenu(self)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # bottom align position of QMenu
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)

  def setMarker(self, symbol='', alternate=False):
    # sets marker symbol to selected type
    if((symbol in self.orderedstyles) and (symbol != self.style['marker'])):
      self.style['marker'] = symbol
      self.comboStyle.setSymbol(symbol)

      if(self.residMode):
        self.target.setResidStyle('marker', symbol, redraw=True)
      else:
        changed = self.target.setStyle('marker', symbol, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

# define subclassed button for marker button
class MarkerButton(QPushButtonMac):
  def __init__(self, *args, **kwargs):
    super(QPushButtonMac, self).__init__(*args, **kwargs)
    self.mySymbol = ''
    self.mySymbolList = []
    self.setText('')
    self.parent = None
    self.alternate = False
    
  def setSymbol(self, symbol=''):
    self.mySymbol = symbol
    # update button label
    self.update()

  def setSymbolList(self, symbolList=[]):
    if(type(symbolList) == type([])):
      self.mySymbolList = symbolList

  def setParent(self, parent=None):
    self.parent = parent
    
  def setAlternate(self, alternate=False):
    self.alternate = alternate
    
  def paintEvent(self, event):
    # regular draw event
    QPushButtonMac.paintEvent(self, event)
    
    # draw little triangle
    PSIZE = 4
    s = self.size()
    buttonWidth, buttonHeight = s.width(), s.height()
    offsetX, offsetY = int(buttonWidth - int(PSIZE + 4)), int(buttonHeight / 2 - int(PSIZE / 2 - 2))
    polygon = QtGui.QPolygon([QtCore.QPoint(int(offsetX), int(offsetY)), QtCore.QPoint(int(offsetX + int(PSIZE)), int(offsetY)),\
                              QtCore.QPoint(int(offsetX + int(PSIZE / 2)), int(offsetY + int(PSIZE)))])
    qp = QtGui.QPainter()
    if(qp is not None):
      qp.begin(self)
      qp.setRenderHint(QtGui.QPainter.RenderHint.Antialiasing, True)
      qp.setPen(QtCore.Qt.PenStyle.NoPen)
      # draw dropdown triangle
      qp.setBrush(QtGui.QColor(*UI_TEXT_COLOR))
      qp.drawPolygon(polygon)
      # write text (do this via painter such as have left alignment w/o breaking Fusion style)
      pen = QtGui.QPen()
      pen.setStyle(QtCore.Qt.PenStyle.SolidLine)
      pen.setJoinStyle(QtCore.Qt.PenJoinStyle.RoundJoin)
      pen.setColor(QtGui.QColor(*UI_TEXT_COLOR))
      pen.setWidth(1)
      pen.setCapStyle(QtCore.Qt.PenCapStyle.SquareCap)
      qp.setPen(pen)
      textHeight = self.fontMetrics().height()
      qp.drawText(2, textHeight, str(self.mySymbol))
      # fini
      qp.end()
    
  def wheelEvent(self, event):
    # handles wheel events to change marker symbol (mimics qcombobox style)
    if(self.mySymbol in self.mySymbolList):
      current = self.mySymbolList.index(self.mySymbol)
      if((event.angleDelta().y() < 0) and (current < len(self.mySymbolList) - 1) and (self.parent != None)):
        self.parent.setMarker(self.mySymbolList[current + 1], self.alternate)
      elif((event.angleDelta().y() > 0) and (current > 0) and (self.parent != None)):
        self.parent.setMarker(self.mySymbolList[current - 1], self.alternate)
        
  def keyPressEvent(self, event):
    # handles up and down arrow
    if(event.key() in [QtCore.Qt.Key.Key_Up, QtCore.Qt.Key.Key_Down, QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter, QtCore.Qt.Key.Key_Home,\
       QtCore.Qt.Key.Key_End, QtCore.Qt.Key.Key_PageDown, QtCore.Qt.Key.Key_PageUp]):
      if(event.key() in [QtCore.Qt.Key.Key_Return, QtCore.Qt.Key.Key_Enter]):
        self.parent.markerCallback(self)
      elif(event.key() in [QtCore.Qt.Key.Key_Home, QtCore.Qt.Key.Key_PageUp]):
        self.parent.setMarker(self.mySymbolList[0], self.alternate)
      elif(event.key() in [QtCore.Qt.Key.Key_End, QtCore.Qt.Key.Key_PageDown]):
        self.parent.setMarker(self.mySymbolList[-1], self.alternate)
      elif(self.mySymbol in self.mySymbolList):
        current = self.mySymbolList.index(self.mySymbol)
        if((event.key() == QtCore.Qt.Key.Key_Down) and (current < len(self.mySymbolList) - 1) and (self.parent != None)):
          self.parent.setMarker(self.mySymbolList[current + 1], self.alternate)
        elif((event.key() == QtCore.Qt.Key.Key_Up) and (current > 0) and (self.parent != None)):
          self.parent.setMarker(self.mySymbolList[current - 1], self.alternate)
      else:
        event.ignore()
    else:
      # activate normal event handling
      QPushButtonMac.keyPressEvent(self, event)

# define marker dialog
class MarkerMenu(KuhMenu):
  def __init__(self, parent = None):
    super(MarkerMenu, self).__init__()
    self.parent = parent
    self.data = []
    self.alternate = False
      
    # float validator
    self.validFloat = MyValidFloat()
    self.validInt = MyValidInt()

    # set up GUI
    self.buildRessource()
    
  def setAlternate(self, alternate=False):
    self.alternate = alternate

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    
    # argh -- we will need a new table model for this
    if(self.parent != None):
      items = self.parent.orderedstyles
      nitems = len(items)
      if(nitems):
        # determine dimensions of table
        ncolumns = int(np.ceil(nitems ** 0.5))
        nrows = int(np.ceil(nitems / ncolumns))
        # set up data
        self.data = [['tilt' for i in range(ncolumns)] for i in range(nrows)]
        for i in range(nitems):
          self.data[i % nrows][int(i / nrows)] = items[i]
        # determine maximum length of entries in each column
        maxColumnLength = [max([len(str(self.data[i][j])) if (self.data[i][j] != 'tilt') else 0 for i in range(nrows)]) for j in range(ncolumns)]
        self.model = StandardItemModel(nrows, ncolumns, self)
        self.model.populateModel(self.data)
        self.model.layoutChanged.emit()
        # set up view
        self.tableView = QtWidgets.QTableView()
        self.tableView.setModel(self.model)
        self.tableView.resizeColumnsToContents()
        self.tableView.horizontalHeader().hide()
        self.tableView.verticalHeader().hide()
        self.tableView.setShowGrid(False)
        self.tableView.setStyleSheet("QTableView::item:hover {color: white; background-color: #0078d7; border-style: solid; border-radius: 4px;}\n\
                                      QTableView::item {background-color: transparent; text-align: center;}\n\
                                      QTableView::item:focus {background-color: transparent; border-width: 2px; border-color: #444444; border-style: solid; border-radius: 4px;}\n\
                                      QTableView::item:hover:focus {color: white; background-color: #0078d7; border-width: 2px; border-color: #444444; border-style: solid; border-radius: 4px;}\n\
                                      QTableView {border-style: solid; border-radius: 4px;}\n\
                                      QTableView {background-color: rgba" + UI_ALTERNATE_BASE_STRING + ";}")
        self.tableView.clicked.connect(self.clickHandler)
        self.tableView.activated.connect(self.activateHandler)
        self.tableView.setSelectionMode(self.tableView.SelectionMode.NoSelection)
        self.tableView.setEditTriggers(self.tableView.EditTrigger.NoEditTriggers)
        # set delegate
        self.tableView.setItemDelegate(MarkerDelegate())
        # set focus to target item
        if(self.parent.style['marker'] in items):
          selectedIndex = items.index(self.parent.style['marker'])
          selectItem = self.model.index(selectedIndex % nrows, int(selectedIndex / nrows))
          self.tableView.selectionModel().setCurrentIndex(selectItem, QtCore.QItemSelectionModel.SelectionFlag.Current)
        
        self.vLayout.addWidget(self.tableView)
        self.tableView.setFocus()
        
        # resize QTableView -- resizeColumnsToContents fails horribly
        [self.tableView.horizontalHeader().resizeSection(hheader, int((max(maxColumnLength[hheader], 3)  + 0.5) * 8 * SCALEFONT)) for hheader in range(ncolumns)]
        [self.tableView.verticalHeader().resizeSection(vheader, int(BASE_SIZE + 2)) for vheader in range(nrows)]
        
        # now correctly size the QMenu -- seriously effing bugger this
        width, height = self.tableView.viewportSizeHint().width(), self.tableView.viewportSizeHint().height()
        self.tableView.setMinimumWidth(width)
        self.tableView.setMinimumHeight(height)
        self.adjustSize()
      
  def clickHandler(self, index):
    # callback for click on table
    column, row = index.column(), index.row()
    if(self.data[row][column] != 'tilt'):
      self.parent.setMarker(self.data[row][column], self.alternate)
      self.close()

  def activateHandler(self, index):
    # callback for enter press in table
    self.clickHandler(index)

# define item delegate for coloring of specific cells
class MarkerDelegate(QtWidgets.QStyledItemDelegate):
  def __init__(self):
    super(MarkerDelegate, self).__init__()

  def paint(self, painter, option, index):
    item = index.data(QtCore.Qt.ItemDataRole.DisplayRole)
    if(item in CUSTOM_MARKERS):
      if(DARK_MODE):
        brushColor = QtGui.QColor('#555555')
      else:
        brushColor = QtGui.QColor('#aaaaaa')
      painter.save()
      painter.setBrush(QtGui.QBrush(brushColor))
      painter.setOpacity(1.0)
      painter.setPen(QtCore.Qt.PenStyle.NoPen)
      r = QtCore.QRect(option.rect)
      painter.drawRect(r)
      #painter.drawRoundedRect(r, 4, 4)
      painter.restore()
    # regular draw event
    QtWidgets.QStyledItemDelegate.paint(self, painter, option, index)

# custom table model for multi-column pull-down menu
class StandardItemModel(QtGui.QStandardItemModel):
  def __init__(self, *args, **kwargs):
    super(StandardItemModel, self).__init__(*args, **kwargs)

  def populateModel(self, data):
    if((type(data) == type([])) and len(data)):
      nrows, ncolumns = self.rowCount(), self.columnCount()
      nitems = min(len(data) * len(data[0]), nrows * ncolumns)
      [self.setItem(i % nrows, int(i / nrows), QtGui.QStandardItem(str(data[i % nrows][int(i / nrows)]))) for i in range(nitems) if (data[i % nrows][int(i / nrows)] != 'tilt')]
      [self.setItem(i % nrows, int(i / nrows), QtGui.QStandardItem('')) for i in range(nitems) if (data[i % nrows][int(i / nrows)] == 'tilt')]
      [self.item(i % nrows, int(i / nrows)).setEnabled(False) for i in range(nitems) if (data[i % nrows][int(i / nrows)] == 'tilt')]
      
# the linestyle menu
class lineStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False, residZero=False):
    super(lineStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode
    self.residZero = residZero
    
    # valid line styles
    self.linestyles = ['None', '-', '--', '-.', ':']
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.dashstyles = ['butt', 'round', 'projecting']
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      if(self.residMode):
        getStyle = self.target.getResidStyle()
      else:
        if(self.residZero):
          getStyle = self.parent.parent.parent.data[self.parent.parent.parent.activeData].getResidLineStyle()
        else:
          getStyle = self.target.getStyle()
      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        self.style[entry] = getStyle[entry]
    
    # set default values
    defaultStyle = {'linewidth': 1.0, 'color': [0, 0, 0, 1], 'linestyle': 'solid', 'dash_capstyle': 'butt', 'gapcolor': [0, 0, 0.8, 1], 'doGap': False}
    for entry in defaultStyle:
      if(not (entry in self.style)):
        self.style[entry] = defaultStyle[entry]

    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.lineStyleLabel = QtWidgets.QLabel()
    self.lineStyleLabel.setText('Line')
    font = self.lineStyleLabel.font()
    font.setBold(True)
    self.lineStyleLabel.setFont(font)
    self.vLayout.addWidget(self.lineStyleLabel)    
    
    # line width
    self.lineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineWidthGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.lineWidthGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineWidthLabel = QtWidgets.QLabel('Width')
    self.lineWidthLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.lineWidthLabel)
    # line width spin box
    self.lineWidthSpin = QDoubleSpinClick()
    self.hLayout.addWidget(self.lineWidthSpin)
    self.lineWidthSpin.setMinimum(0.0)
    self.lineWidthSpin.setMaximum(100.0)
    self.lineWidthSpin.setValue(self.style['linewidth'])
    self.lineWidthSpin.setToolTip('Line width of object')
    self.lineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'linewidth', self.lineWidthSpin))

    # line color
    self.lineColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.lineColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineColorLabel = QtWidgets.QLabel('Color')
    self.lineColorLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.lineColorLabel)
      
    self.lineColorButton = QPushButtonColor()
    self.lineColorButton.setToolTip('Line color of object')
    self.lineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['color'][0:3]]
    self.lineColorButton.setMyColor(*colorvalue)
    self.lineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.lineColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'color'))
    self.hLayout2.addWidget(self.lineColorButton)
      
    # gap color
    self.lineColorGroup2 = QWidgetMac()
    self.vLayout.addWidget(self.lineColorGroup2)
    self.hLayout22 = QtWidgets.QHBoxLayout(self.lineColorGroup2)
    self.hLayout22.setContentsMargins(0, 0, 0, 0)
    self.hLayout22.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout22.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.lineColorLabel2 = QPushButtonCheckable()
    self.lineColorLabel2.setText('Gap')
    self.lineColorLabel2.setToolTip('Show line gap color of object')
    self.lineColorLabel2.setChecked(self.style['doGap'])
    self.lineColorLabel2.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorLabel2.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorLabel2.clicked.connect(partial(self.toggleDoGap, self.target, self.lineColorLabel2))
    self.hLayout22.addWidget(self.lineColorLabel2)
      
    self.lineColorButton2 = QPushButtonColor()
    self.lineColorButton2.setToolTip('Line gap color of object')
    self.lineColorButton2.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['gapcolor'][0:3]]
    self.lineColorButton2.setMyColor(*colorvalue)
    self.lineColorButton2.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton2.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton2.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.lineColorButton2.clicked.connect(partial(self.setColor, target=self.target, key='gapcolor'))
    self.hLayout22.addWidget(self.lineColorButton2)
      
    # line style
    self.lineStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineStyleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.lineStyleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineStyleLabel = QtWidgets.QLabel('Style')
    self.lineStyleLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.lineStyleLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.lineStyleLabel)
    self.comboStyle = QComboBoxMac()
    self.comboStyle.setToolTip('Line style of object')
    for entry in self.linestyles:
      self.comboStyle.addItem(entry)
    if(self.style['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['linestyle'])
    else:
      currindex = 0
    self.comboStyle.setCurrentIndex(currindex)
    self.comboStyle.activated.connect(partial(self.selectStyle, self.target, 'linestyle', self.comboStyle))
    self.comboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.comboStyle)
    
    # cap style
    self.lineDashStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineDashStyleGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.lineDashStyleGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineDashStyleLabel = QtWidgets.QLabel('Cap')
    self.lineDashStyleLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.lineDashStyleLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.lineDashStyleLabel)
    self.comboDashStyle = QComboBoxMac()
    self.comboDashStyle.setToolTip('Cap style of object')
    for entry in self.dashstyles:
      self.comboDashStyle.addItem(entry)
    if(self.style['dash_capstyle'] in self.dashstyles):
      currindex = self.dashstyles.index(self.style['dash_capstyle'])
    else:
      currindex = 0
    self.comboDashStyle.setCurrentIndex(currindex)
    self.comboDashStyle.activated.connect(partial(self.selectStyle, self.target, 'dash_capstyle', self.comboDashStyle))
    self.comboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.comboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.comboDashStyle)

  def setColor(self, target=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'color'):
          self.lineColorButton.setMyColor(*value)
        # process color and update plot
        value = [i / 255.0 for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidStyle(key, value, redraw=True)
        elif(self.residZero):
          self.parent.parent.parent.data[self.parent.parent.parent.activeData].setResidLineStyle(key, value, redraw=True)
        else:
          changed = target.setStyle(key, value, redraw=False)
          # update legend if needed
          self.updateLegend(redraw=changed)

      # bring back the QMenu
      self.parent.togglePersist(False)
    
  def selectStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      value = str(entryfield.currentText())
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStyle(key, value, redraw=True)
      elif(self.residZero):
        self.parent.parent.parent.data[self.parent.parent.parent.activeData].setResidLineStyle(key, value, redraw=True)
      else:
        changed = target.setStyle(key, value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)
      
  def alterStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStyle(key, value, redraw=True)
      elif(self.residZero):
        self.parent.parent.parent.data[self.parent.parent.parent.activeData].setResidLineStyle(key, value, redraw=True)
      else:
        changed = target.setStyle(key, value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def toggleDoGap(self, target = None, entryfield = None):
    # toggles whether to show gap color of line or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['doGap'] = value
      if(self.residMode):
        target.toggleResidDoGap(value, redraw=True)
      elif(self.residZero):
        self.parent.parent.parent.data[self.parent.parent.parent.activeData].setResidLineStyle('doGap', value, redraw=True)
      else:
        changed = target.toggleDoGap(value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

class barStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False):
    super(barStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode
    
    # float validator
    self.validFloat = MyValidFloat()

    # valid line styles
    self.linestyles = ['None', '-', '--', '-.', ':']
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.linestyles = ['solid', 'dashed', 'dashdot', 'dotted']
    
    # valid hatch styles
    self.hatchstyles = ['None', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*'] # unfortunately none is not interpreted correctly
    self.hatchstyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    self.hatchMultiplystyles = [str(i) for i in range(1, 11)]
    self.dashstyles = ['butt', 'round', 'projecting']
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      if(self.residMode):
        getStyle = self.target.getResidBarStyle()
      else:
        getStyle = self.target.getBarStyle()
      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        self.style[entry] = getStyle[entry]
    else:
      self.style['linewidth'] = 0.5
      self.style['linestyle'] = 'solid'
      self.style['facecolor'] = [0.8, 0.0, 0.0, 0.5]
      self.style['facecolorAlt'] = [0.3, 0.2, 0.6, 1.0]
      self.style['edgecolor'] = [0.3, 0.3, 0.3, 1.0]
      self.style['width'] = 0.1
      self.style['hatch'] = 'None'
      self.style['hatchMultiply'] = 1
      self.style['hatch_linewidth'] = 1.
      self.style['showBar'] = False
      self.style['offset'] = 0
      self.style['doFill'] = True
      self.style['bottom'] = 0
      self.style['horizontal'] = False
    
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    # heading
    self.displayGroup = QWidgetMac()
    self.vLayout.addWidget(self.displayGroup)
    self.hLayout0 = QtWidgets.QHBoxLayout(self.displayGroup)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.barStyleLabel = QtWidgets.QLabel()
    self.barStyleLabel.setText('Bar')
    font = self.barStyleLabel.font()
    font.setBold(True)
    self.barStyleLabel.setFont(font)
    self.barStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.barStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.barStyleLabel)    
    
    # display bar?
    self.displayLabel = QPushButtonCheckable()
    self.displayLabel.setText('Show?')
    self.displayLabel.setToolTip('Show bar graphics of object')
    self.displayLabel.setChecked(self.style['showBar'])
    self.displayLabel.clicked.connect(partial(self.setDisplay, self.target))
    self.displayLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.displayLabel)
    
    # line width
    self.lineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineWidthGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.lineWidthGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineWidthLabel = QtWidgets.QLabel('Linewidth')
    self.lineWidthLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.lineWidthLabel)
    # line width spin box
    self.lineWidthSpin = QDoubleSpinClick()
    self.hLayout.addWidget(self.lineWidthSpin)
    self.lineWidthSpin.setMinimum(0.0)
    self.lineWidthSpin.setMaximum(100.0)
    self.lineWidthSpin.setValue(self.style['linewidth'])
    self.lineWidthSpin.setToolTip('Bar line width of object')
    self.lineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'linewidth', self.lineWidthSpin))
    
    # line color
    self.lineColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.lineColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineColorLabel = QtWidgets.QLabel('Linecolor')
    self.lineColorLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.lineColorLabel)
      
    self.lineColorButton = QPushButtonColor()
    self.lineColorButton.setToolTip('Bar line color of object')
    self.lineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['edgecolor'][0:3]]
    self.lineColorButton.setMyColor(*colorvalue)
    self.lineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.lineColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'edgecolor'))
    self.hLayout2.addWidget(self.lineColorButton)
      
    # line style
    self.lineStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineStyleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.lineStyleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineStyleLabel = QtWidgets.QLabel('Linestyle')
    self.lineStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.lineStyleLabel)
    self.comboStyle = QComboBoxMac()
    self.comboStyle.setToolTip('Bar line style of object')
    for entry in self.linestyles:
      self.comboStyle.addItem(entry)
    if(self.style['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['linestyle'])
    else:
      currindex = 0
    self.comboStyle.setCurrentIndex(currindex)
    self.comboStyle.activated.connect(partial(self.selectStyle, self.target, 'linestyle', self.comboStyle))
    self.comboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.comboStyle)

    # cap style
    self.lineDashStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineDashStyleGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.lineDashStyleGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineDashStyleLabel = QtWidgets.QLabel('Linecap')
    self.lineDashStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineDashStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.lineDashStyleLabel)
    self.comboDashStyle = QComboBoxMac()
    self.comboDashStyle.setToolTip('Bar cap style of object')
    for entry in self.dashstyles:
      self.comboDashStyle.addItem(entry)
    if(self.style['capstyle'] in self.dashstyles):
      currindex = self.dashstyles.index(self.style['capstyle'])
    else:
      currindex = 0
    self.comboDashStyle.setCurrentIndex(currindex)
    self.comboDashStyle.activated.connect(partial(self.selectStyle, self.target, 'capstyle', self.comboDashStyle))
    self.comboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.comboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.comboDashStyle)

    # fill color
    self.fillColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.fillColorGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.fillColorGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.fillColorLabel = QPushButtonCheckable()
    self.fillColorLabel.setText('Fillcolor')
    self.fillColorLabel.setToolTip('Show bar fill of object')
    self.fillColorLabel.setChecked(self.style['doFill'])
    self.fillColorLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.fillColorLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.fillColorLabel.clicked.connect(partial(self.toggleBarFill, self.target, self.fillColorLabel))
    self.hLayout5.addWidget(self.fillColorLabel)
      
    self.fillColorButton = QPushButtonColor()
    self.fillColorButton.setToolTip('Bar fill color of object')
    self.fillColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['facecolor'][0:3]]
    self.fillColorButton.setMyColor(*colorvalue)
    self.fillColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.fillColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'facecolor'))
    self.hLayout5.addWidget(self.fillColorButton)
      
    self.fillColorButtonAlt = QPushButtonColor()
    self.fillColorButtonAlt.setToolTip('Bar alternate fill color of object')
    self.fillColorButtonAlt.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['facecolorAlt'][0:3]]
    self.fillColorButtonAlt.setMyColor(*colorvalue)
    self.fillColorButtonAlt.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButtonAlt.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButtonAlt.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.fillColorButtonAlt.clicked.connect(partial(self.setColor, target = self.target, key = 'facecolorAlt'))
    self.hLayout5.addWidget(self.fillColorButtonAlt)
      
    # hatch style
    self.hatchStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.hatchStyleGroup)
    self.hLayout6 = QtWidgets.QHBoxLayout(self.hatchStyleGroup)
    self.hLayout6.setContentsMargins(0, 0, 0, 0)
    self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hatchStyleLabel = QtWidgets.QLabel('Hatch')
    self.hatchStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hatchStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.hatchStyleLabel)
    self.comboHatchStyle = QComboBoxMac()
    self.comboHatchStyle.setToolTip('Bar hatch style of object')
    for entry in self.hatchstyles:
      self.comboHatchStyle.addItem(entry)
    if(self.style['hatch'] in self.hatchstyles):
      currindex = self.hatchstyles.index(self.style['hatch'])
    else:
      currindex = 0
    self.comboHatchStyle.setCurrentIndex(currindex)
    self.comboHatchStyle.activated.connect(partial(self.selectStyle, self.target, 'hatch', self.comboHatchStyle))
    self.comboHatchStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.comboHatchStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.comboHatchStyle)
    self.comboHatchMultiplyStyle = QComboBoxMac()
    self.comboHatchMultiplyStyle.setToolTip('Bar hatch repeat of object')
    for entry in self.hatchMultiplystyles:
      self.comboHatchMultiplyStyle.addItem(str(entry))
    if(str(self.style['hatchMultiply']) in self.hatchMultiplystyles):
      currindex = self.hatchMultiplystyles.index(str(self.style['hatchMultiply']))
    else:
      currindex = 0
    self.comboHatchMultiplyStyle.setCurrentIndex(currindex)
    self.comboHatchMultiplyStyle.activated.connect(partial(self.selectStyle, self.target, 'hatchMultiply', self.comboHatchMultiplyStyle))
    self.comboHatchMultiplyStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.comboHatchMultiplyStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.comboHatchMultiplyStyle)

    # hatch second row
    self.hatchLineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.hatchLineWidthGroup)
    self.hLayout_h = QtWidgets.QHBoxLayout(self.hatchLineWidthGroup)
    self.hLayout_h.setContentsMargins(0, 0, 0, 0)
    self.hLayout_h.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout_h.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hatchLineWidthLabel = QtWidgets.QLabel('Hatch line')
    self.hatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout_h.addWidget(self.hatchLineWidthLabel)
    # hatch line width spin box
    self.hatchLineWidthSpin = QDoubleSpinClick()
    self.hLayout_h.addWidget(self.hatchLineWidthSpin)
    self.hatchLineWidthSpin.setMinimum(0.0)
    self.hatchLineWidthSpin.setMaximum(100.0)
    self.hatchLineWidthSpin.setValue(self.style['hatch_linewidth'])
    self.hatchLineWidthSpin.setToolTip('Bar hatch line width of object')
    self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'hatch_linewidth', self.hatchLineWidthSpin))
    
    # bar width
    self.barWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.barWidthGroup)
    self.hLayout7 = QtWidgets.QHBoxLayout(self.barWidthGroup)
    self.hLayout7.setContentsMargins(0, 0, 0, 0)
    self.hLayout7.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout7.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.barWidthLabel = QtWidgets.QLabel('Barwidth')
    self.barWidthLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.barWidthLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout7.addWidget(self.barWidthLabel)
    self.barWidthEntry = QLineEditClick()
    self.barWidthEntry.setToolTip('Bar width of object')
    self.barWidthEntry.setText(str(self.style['width']))
    self.barWidthEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.barWidthEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.barWidthEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'width', self.barWidthEntry, 0.0, 1000.0))
    self.barWidthEntry.setValidator(self.validFloat)
    self.hLayout7.addWidget(self.barWidthEntry)
    
    # bar offset
    self.barOffsetGroup = QWidgetMac()
    self.vLayout.addWidget(self.barOffsetGroup)
    self.hLayout8 = QtWidgets.QHBoxLayout(self.barOffsetGroup)
    self.hLayout8.setContentsMargins(0, 0, 0, 0)
    self.hLayout8.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout8.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.barOffsetLabel = QtWidgets.QLabel('Offset')
    self.barOffsetLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.barOffsetLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout8.addWidget(self.barOffsetLabel)
    self.barOffsetEntry = QLineEditClick()
    self.barOffsetEntry.setToolTip('Bar x offset of object')
    self.barOffsetEntry.setText(str(self.style['offset']))
    self.barOffsetEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.barOffsetEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.barOffsetEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'offset', self.barOffsetEntry, -1000.0, 1000.0))
    self.barOffsetEntry.setValidator(self.validFloat)
    self.hLayout8.addWidget(self.barOffsetEntry)
    
    # bar bottom
    self.barBottomGroup = QWidgetMac()
    self.vLayout.addWidget(self.barBottomGroup)
    self.hLayout9 = QtWidgets.QHBoxLayout(self.barBottomGroup)
    self.hLayout9.setContentsMargins(0, 0, 0, 0)
    self.hLayout9.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout9.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.barBottomLabel = QtWidgets.QLabel('Bottom')
    self.barBottomLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.barBottomLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout9.addWidget(self.barBottomLabel)
    self.barBottomEntry = QLineEditClick()
    self.barBottomEntry.setToolTip('Bar x bottom of object')
    self.barBottomEntry.setText(str(self.style['bottom']))
    self.barBottomEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.barBottomEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.barBottomEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'bottom', self.barBottomEntry, -np.inf, np.inf))
    self.barBottomEntry.setValidator(self.validFloat)
    self.hLayout9.addWidget(self.barBottomEntry)

    # bar horizontal?
    self.barHorizontalGroup = QWidgetMac()
    self.vLayout.addWidget(self.barHorizontalGroup)
    self.hLayout10 = QtWidgets.QHBoxLayout(self.barHorizontalGroup)
    self.hLayout10.setContentsMargins(0, 0, 0, 0)
    self.hLayout10.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout10.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.barHorizontalLabel = QPushButtonCheckable()
    self.barHorizontalLabel.setText('horizontal')
    self.barHorizontalLabel.setToolTip('Show horizontal rather than vertical bars')
    self.barHorizontalLabel.setChecked(self.style['horizontal'])
    self.barHorizontalLabel.clicked.connect(partial(self.setHorizontal, self.target))
    self.barHorizontalLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.barHorizontalLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout10.addWidget(self.barHorizontalLabel)

  def setHorizontal(self, target=None):
    # toggles b/w horizontal and vertical orientation of bar chart
    if(target != None):
      self.style['horizontal'] = self.barHorizontalLabel.isChecked()
      if(self.residMode):
        target.toggleResidBarHorizontal(self.style['horizontal'])
      else:
        target.toggleBarHorizontal(self.style['horizontal'])
  
  def setDisplay(self, target=None):
    # toggled display of bar graphics
    if(target != None):
      self.style['showBar'] = self.displayLabel.isChecked()
      if(self.residMode):
        target.toggleResidBar(self.style['showBar'])
      else:
        target.toggleBar(self.style['showBar'])

  def setColor(self, target=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'edgecolor'):
          self.lineColorButton.setMyColor(*value)
        elif(key == 'facecolor'):
          self.fillColorButton.setMyColor(*value)
        elif(key == 'facecolorAlt'):
          self.fillColorButtonAlt.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidBarStyle(key, value, redraw=True)
        else:
          target.setBarStyle(key, value, redraw=True)
        # update legend if needed - not needed as bars do not feature in legend
        #self.updateLegend()

      # bring back the QMenu
      self.parent.togglePersist(False)
    
  def selectStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      #value = str(self.comboStyle.currentText())
      value = str(entryfield.currentText())
      if(key in self.style):
        if(key == 'hatchMultiply'):
          value = int(value)
        self.style[key] = value
      if(self.residMode):
        target.setResidBarStyle(key, value, redraw=True)
      else:
        target.setBarStyle(key, value, redraw=True)
      # update legend if needed - not needed as bars do not feature in legend
      #self.updateLegend()

  def toggleBarFill(self, target = None, entryfield = None):
    # toggles whether to fill bars or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['doFill'] = value
      if(self.residMode):
        target.toggleResidBarFill(value, redraw=True)
      else:
        changed = target.toggleBarFill(value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)

  def alterStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidBarStyle(key, value, redraw=True)
      else:
        target.setBarStyle(key, value, redraw=True)
      # update legend if needed - not needed as bars do not feature in legend
      #self.updateLegend()
      
  def changeStyle(self, target=None, key=None, entryfield=None, minval=0, maxval=1):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidBarStyle(key, value, redraw=True)
      else:
        target.setBarStyle(key, value, redraw=True)
      # update legend if needed - not needed as bars do not feature in legend
      #self.updateLegend()

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

class stackStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False):
    super(stackStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode
    
    # float validator
    self.validFloat = MyValidFloat()

    # valid line styles
    self.linestyles = ['None', '-', '--', '-.', ':']
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.linestyles = ['solid', 'dashed', 'dashdot', 'dotted']
    
    # valid hatch styles
    self.hatchstyles = ['None', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*'] # unfortunately none is not interpreted correctly
    self.hatchstyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    self.hatchMultiplystyles = [str(i) for i in range(1, 11)]
    self.dashstyles = ['butt', 'round', 'projecting']
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      if(self.residMode):
        getStyle = self.target.getResidStackStyle()
      else:
        getStyle = self.target.getStackStyle()
      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        self.style[entry] = getStyle[entry]
    else:
      self.style['linewidth'] = 0.5
      self.style['linestyle'] = 'solid'
      self.style['facecolor'] = [0.8, 0.0, 0.0, 0.5]
      self.style['facecolorAlt'] = [0.3, 0.2, 0.6, 1.0]
      self.style['edgecolor'] = [0.3, 0.3, 0.3, 1.0]
      self.style['hatch'] = 'None'
      self.style['hatchMultiply'] = 1
      self.style['hatch_linewidth'] = 1.
      self.style['showStack'] = False
      self.style['doFill'] = True
      self.style['bottom'] = 0.
    
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    # heading
    self.displayGroup = QWidgetMac()
    self.vLayout.addWidget(self.displayGroup)
    self.hLayout0 = QtWidgets.QHBoxLayout(self.displayGroup)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.stackStyleLabel = QtWidgets.QLabel()
    self.stackStyleLabel.setText('Stack')
    font = self.stackStyleLabel.font()
    font.setBold(True)
    self.stackStyleLabel.setFont(font)
    self.stackStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.stackStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.stackStyleLabel)    
    
    # display stack?
    self.displayLabel = QPushButtonCheckable()
    self.displayLabel.setText('Show?')
    self.displayLabel.setToolTip('Show stack graphics of object')
    self.displayLabel.setChecked(self.style['showStack'])
    self.displayLabel.clicked.connect(partial(self.setDisplay, self.target))
    self.displayLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.displayLabel)
    
    # line width
    self.lineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineWidthGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.lineWidthGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineWidthLabel = QtWidgets.QLabel('Linewidth')
    self.lineWidthLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.lineWidthLabel)
    # line width spin box
    self.lineWidthSpin = QDoubleSpinClick()
    self.hLayout.addWidget(self.lineWidthSpin)
    self.lineWidthSpin.setMinimum(0.0)
    self.lineWidthSpin.setMaximum(100.0)
    self.lineWidthSpin.setValue(self.style['linewidth'])
    self.lineWidthSpin.setToolTip('Stack line width of object')
    self.lineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'linewidth', self.lineWidthSpin))
    
    # line color
    self.lineColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.lineColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineColorLabel = QtWidgets.QLabel('Linecolor')
    self.lineColorLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.lineColorLabel)
      
    self.lineColorButton = QPushButtonColor()
    self.lineColorButton.setToolTip('Stack line color of object')
    self.lineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['edgecolor'][0:3]]
    self.lineColorButton.setMyColor(*colorvalue)
    self.lineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.lineColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'edgecolor'))
    self.hLayout2.addWidget(self.lineColorButton)
      
    # line style
    self.lineStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineStyleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.lineStyleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineStyleLabel = QtWidgets.QLabel('Linestyle')
    self.lineStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.lineStyleLabel)
    self.comboStyle = QComboBoxMac()
    self.comboStyle.setToolTip('Stack line style of object')
    for entry in self.linestyles:
      self.comboStyle.addItem(entry)
    if(self.style['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['linestyle'])
    else:
      currindex = 0
    self.comboStyle.setCurrentIndex(currindex)
    self.comboStyle.activated.connect(partial(self.selectStyle, self.target, 'linestyle', self.comboStyle))
    self.comboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.comboStyle)

    # fill color
    self.fillColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.fillColorGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.fillColorGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.fillColorLabel = QPushButtonCheckable()
    self.fillColorLabel.setText('Fillcolor')
    self.fillColorLabel.setToolTip('Show stack fill of object')
    self.fillColorLabel.setChecked(self.style['doFill'])
    self.fillColorLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.fillColorLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.fillColorLabel.clicked.connect(partial(self.toggleStackFill, self.target, self.fillColorLabel))
    self.hLayout5.addWidget(self.fillColorLabel)
      
    self.fillColorButton = QPushButtonColor()
    self.fillColorButton.setToolTip('Stack fill color of object')
    self.fillColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['facecolor'][0:3]]
    self.fillColorButton.setMyColor(*colorvalue)
    self.fillColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.fillColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'facecolor'))
    self.hLayout5.addWidget(self.fillColorButton)
      
    self.fillColorButtonAlt = QPushButtonColor()
    self.fillColorButtonAlt.setToolTip('Stack alternate fill color of object')
    self.fillColorButtonAlt.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['facecolorAlt'][0:3]]
    self.fillColorButtonAlt.setMyColor(*colorvalue)
    self.fillColorButtonAlt.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButtonAlt.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorButtonAlt.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.fillColorButtonAlt.clicked.connect(partial(self.setColor, target = self.target, key = 'facecolorAlt'))
    self.hLayout5.addWidget(self.fillColorButtonAlt)
      
    # hatch style
    self.hatchStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.hatchStyleGroup)
    self.hLayout6 = QtWidgets.QHBoxLayout(self.hatchStyleGroup)
    self.hLayout6.setContentsMargins(0, 0, 0, 0)
    self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hatchStyleLabel = QtWidgets.QLabel('Hatch')
    self.hatchStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hatchStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.hatchStyleLabel)
    self.comboHatchStyle = QComboBoxMac()
    self.comboHatchStyle.setToolTip('Stack hatch style of object')
    for entry in self.hatchstyles:
      self.comboHatchStyle.addItem(entry)
    if(self.style['hatch'] in self.hatchstyles):
      currindex = self.hatchstyles.index(self.style['hatch'])
    else:
      currindex = 0
    self.comboHatchStyle.setCurrentIndex(currindex)
    self.comboHatchStyle.activated.connect(partial(self.selectStyle, self.target, 'hatch', self.comboHatchStyle))
    self.comboHatchStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.comboHatchStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.comboHatchStyle)
    self.comboHatchMultiplyStyle = QComboBoxMac()
    self.comboHatchMultiplyStyle.setToolTip('Stack hatch repeat of object')
    for entry in self.hatchMultiplystyles:
      self.comboHatchMultiplyStyle.addItem(str(entry))
    if(str(self.style['hatchMultiply']) in self.hatchMultiplystyles):
      currindex = self.hatchMultiplystyles.index(str(self.style['hatchMultiply']))
    else:
      currindex = 0
    self.comboHatchMultiplyStyle.setCurrentIndex(currindex)
    self.comboHatchMultiplyStyle.activated.connect(partial(self.selectStyle, self.target, 'hatchMultiply', self.comboHatchMultiplyStyle))
    self.comboHatchMultiplyStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.comboHatchMultiplyStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.comboHatchMultiplyStyle)

    # hatch second row
    self.hatchLineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.hatchLineWidthGroup)
    self.hLayout_h = QtWidgets.QHBoxLayout(self.hatchLineWidthGroup)
    self.hLayout_h.setContentsMargins(0, 0, 0, 0)
    self.hLayout_h.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout_h.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hatchLineWidthLabel = QtWidgets.QLabel('Hatch line')
    self.hatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout_h.addWidget(self.hatchLineWidthLabel)
    # hatch line width spin box
    self.hatchLineWidthSpin = QDoubleSpinClick()
    self.hLayout_h.addWidget(self.hatchLineWidthSpin)
    self.hatchLineWidthSpin.setMinimum(0.0)
    self.hatchLineWidthSpin.setMaximum(100.0)
    self.hatchLineWidthSpin.setValue(self.style['hatch_linewidth'])
    self.hatchLineWidthSpin.setToolTip('Stack hatch line width of object')
    self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'hatch_linewidth', self.hatchLineWidthSpin))
    
    # bar bottom
    self.stackBottomGroup = QWidgetMac()
    self.vLayout.addWidget(self.stackBottomGroup)
    self.hLayout9 = QtWidgets.QHBoxLayout(self.stackBottomGroup)
    self.hLayout9.setContentsMargins(0, 0, 0, 0)
    self.hLayout9.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout9.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.stackBottomLabel = QtWidgets.QLabel('Baseline')
    self.stackBottomLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.stackBottomLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout9.addWidget(self.stackBottomLabel)
    self.stackBottomEntry = QLineEditClick()
    self.stackBottomEntry.setToolTip('Stack x baseline of object')
    self.stackBottomEntry.setText(str(self.style['bottom']))
    self.stackBottomEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.stackBottomEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.stackBottomEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'bottom', self.stackBottomEntry, -np.inf, np.inf))
    self.stackBottomEntry.setValidator(self.validFloat)
    self.hLayout9.addWidget(self.stackBottomEntry)
    
  def setDisplay(self, target=None):
    # toggled display of bar graphics
    if(target != None):
      self.style['showStack'] = self.displayLabel.isChecked()
      if(self.residMode):
        target.toggleResidStack(self.style['showStack'])
      else:
        target.toggleStack(self.style['showStack'])

  def setColor(self, target=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'edgecolor'):
          self.lineColorButton.setMyColor(*value)
        elif(key == 'facecolor'):
          self.fillColorButton.setMyColor(*value)
        elif(key == 'facecolorAlt'):
          self.fillColorButtonAlt.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidStackStyle(key, value, redraw=True)
        else:
          target.setStackStyle(key, value, redraw=True)
        # update legend if needed - not needed as bars do not feature in legend
        #self.updateLegend()

      # bring back the QMenu
      self.parent.togglePersist(False)
    
  def selectStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      #value = str(self.comboStyle.currentText())
      value = str(entryfield.currentText())
      if(key in self.style):
        if(key == 'hatchMultiply'):
          value = int(value)
        self.style[key] = value
      if(self.residMode):
        target.setResidStackStyle(key, value, redraw=True)
      else:
        target.setStackStyle(key, value, redraw=True)
      # update legend if needed - not needed as bars do not feature in legend
      #self.updateLegend()

  def alterStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStackStyle(key, value, redraw=True)
      else:
        target.setStackStyle(key, value, redraw=True)
      # update legend if needed - not needed as bars do not feature in legend
      #self.updateLegend()

  def toggleStackFill(self, target = None, entryfield = None):
    # toggles whether to fill stacks or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['doFill'] = value
      if(self.residMode):
        target.toggleResidStackFill(value, redraw=True)
      else:
        changed = target.toggleStackFill(value, redraw=False)
        # update legend if needed
        self.updateLegend(redraw=changed)
      
  def changeStyle(self, target=None, key=None, entryfield=None, minval=0, maxval=1):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStackStyle(key, value, redraw=True)
      else:
        target.setStackStyle(key, value, redraw=True)
      # update legend if needed - not needed as bars do not feature in legend
      #self.updateLegend()

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

class textStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False):
    super(textStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode
    
    # float validator
    self.validFloat = MyValidFloat()

    # set up initial values
    self.style = {}
    if (self.target != None):
      if(self.residMode):
        getStyle = self.target.getResidTextStyle()
      else:
        getStyle = self.target.getTextStyle()
      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        self.style[entry] = getStyle[entry]
    else:
      self.style['showText'] = False
      self.style['skip'] = 0
      self.style['color'] = [0.2, 0.2, 0.2, 1.0]
      self.style['fontsize'] = 12
      self.style['fontweight'] = 'normal'
      self.style['fontstyle'] = 'normal'
      self.style['fontname'] = 'Dejavu Sans'
      self.style['rotation'] = 0.0
      self.style['padX'] = 0.0
      self.style['padY'] = 0.0
      self.style['type'] = 'y'
      self.style['linespacing'] = 1.
    
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    # heading
    self.displayGroup = QWidgetMac()
    self.vLayout.addWidget(self.displayGroup)
    self.hLayout0 = QtWidgets.QHBoxLayout(self.displayGroup)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textStyleLabel = QtWidgets.QLabel()
    self.textStyleLabel.setText('Text')
    font = self.textStyleLabel.font()
    font.setBold(True)
    self.textStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textStyleLabel.setFont(font)
    self.hLayout0.addWidget(self.textStyleLabel)    
    
    # display text?
    self.displayLabel = QPushButtonCheckable()
    self.displayLabel.setText('Show?')
    self.displayLabel.setToolTip('Show text labels of object')
    self.displayLabel.setChecked(self.style['showText'])
    self.displayLabel.clicked.connect(partial(self.setDisplay, self.target))
    self.displayLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.displayLabel)
    
    # text skip
    self.textSkipGroup = QWidgetMac()
    self.vLayout.addWidget(self.textSkipGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.textSkipGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textSkipLabel = QtWidgets.QLabel('Skip')
    self.textSkipLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textSkipLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.textSkipLabel)
    # text skip spin box
    self.textSkipSpin = QSpinClick()
    self.hLayout.addWidget(self.textSkipSpin)
    self.textSkipSpin.setMinimum(0)
    self.textSkipSpin.setMaximum(10000)
    self.textSkipSpin.setValue(self.style['skip'])
    self.textSkipSpin.setToolTip('Put text labels on object skipping every n data points')
    self.textSkipSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textSkipSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textSkipSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'skip', self.textSkipSpin, True))

    # text pad
    self.textPadGroup = QWidgetMac()
    self.vLayout.addWidget(self.textPadGroup)
    self.hLayout1 = QtWidgets.QHBoxLayout(self.textPadGroup)
    self.hLayout1.setContentsMargins(0, 0, 0, 0)
    self.hLayout1.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textXLabel = QtWidgets.QLabel(u'\N{GREEK CAPITAL LETTER DELTA}x')
    self.textXLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textXLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout1.addWidget(self.textXLabel)
    self.textXEntry = QLineEditClick()
    self.textXEntry.setToolTip('Text label x offset of object')
    self.textXEntry.setText(str(self.style['padX']))
    self.textXEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textXEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textXEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'padX', self.textXEntry, None, None, False))
    self.textXEntry.setValidator(self.validFloat)
    self.hLayout1.addWidget(self.textXEntry)

    self.textYLabel = QtWidgets.QLabel(u'\N{GREEK CAPITAL LETTER DELTA}y')
    self.textYLabel.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.textYLabel.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout1.addWidget(self.textYLabel)
    self.textYEntry = QLineEditClick()
    self.textYEntry.setToolTip('Text label y offset of object')
    self.textYEntry.setText(str(self.style['padY']))
    self.textYEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textYEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textYEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'padY', self.textYEntry, None, None, False))
    self.textYEntry.setValidator(self.validFloat)
    self.hLayout1.addWidget(self.textYEntry)

    # text font settings
    self.textFontGroup = QWidgetMac()
    self.vLayout.addWidget(self.textFontGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.textFontGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textFontLabel = QtWidgets.QLabel('Font')
    self.textFontLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textFontLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.textFontLabel)
      
    self.textColorButton = QPushButtonColor()
    self.textColorButton.setToolTip('Text label color of object')
    self.textColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['color'][0:3]]
    self.textColorButton.setMyColor(*colorvalue)
    self.textColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.textColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.textColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.textColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'color'))
    self.hLayout2.addWidget(self.textColorButton)

    self.textSizeSpin = QDoubleSpinClick()
    self.hLayout2.addWidget(self.textSizeSpin)
    self.textSizeSpin.setMinimum(0.0)
    self.textSizeSpin.setMaximum(100.0)
    self.textSizeSpin.setValue(self.style['fontsize'])
    self.textSizeSpin.setToolTip('Text label font size of object')
    self.textSizeSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textSizeSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textSizeSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'fontsize', self.textSizeSpin, False))
    self.textSizeSpin.setDecimals(1)
      
    self.textBoldButton = QPushButtonCheckable()
    self.textBoldButton.setText('B')
    self.textBoldButton.setToolTip('Text label font weight of object')
    self.textBoldButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.textBoldButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    if(self.style['fontweight'] == 'bold'):
      self.textBoldButton.setChecked(True)
    useFont = self.textBoldButton.font()
    useFont.setBold(True)
    self.textBoldButton.setFont(useFont)
    self.textBoldButton.clicked.connect(partial(self.changeFontStyle, self.target, 'fontweight', self.textBoldButton))
    self.hLayout2.addWidget(self.textBoldButton)
    
    self.textItalicButton = QPushButtonCheckable()
    self.textItalicButton.setText('I')
    self.textItalicButton.setToolTip('Text label font style of object')
    self.textItalicButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.textItalicButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    if(self.style['fontstyle'] == 'italic'):
      self.textItalicButton.setChecked(True)
    useFont = self.textItalicButton.font()
    useFont.setItalic(True)
    self.textItalicButton.setFont(useFont)
    self.textItalicButton.clicked.connect(partial(self.changeFontStyle, self.target, 'fontstyle', self.textItalicButton))
    self.hLayout2.addWidget(self.textItalicButton)
      
    self.textFontGroup2 = QWidgetMac()
    self.vLayout.addWidget(self.textFontGroup2)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.textFontGroup2)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    spacer = QtWidgets.QLabel('')
    spacer.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(spacer)

    self.textFontBox = FontoBox()
    self.textFontBox.setToolTip('Text label font of object')
    self.textFontBox.setMaximumSize(QtCore.QSize(int(140 * SCALEFONT), int(BASE_SIZE)))
    self.textFontBox.setMinimumSize(QtCore.QSize(int(140 * SCALEFONT), int(BASE_SIZE)))
    self.textFontBox.addItems(items=self.parent.parent.parent.fontNames)
    self.textFontBox.setMe(item=self.style['fontname'])
    self.textFontBox.activated.connect(partial(self.changeFont, self.target))
    self.hLayout3.addWidget(self.textFontBox)
    
    # text linespacing
    self.textLinespacingGroup = QWidgetMac()
    self.vLayout.addWidget(self.textLinespacingGroup)
    self.hLayout35 = QtWidgets.QHBoxLayout(self.textLinespacingGroup)
    self.hLayout35.setContentsMargins(0, 0, 0, 0)
    self.hLayout35.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout35.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textLinespacingLabel = QtWidgets.QLabel('Linespacing')
    self.textLinespacingLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textLinespacingLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout35.addWidget(self.textLinespacingLabel)
    # text linespacing dialog
    self.textLinespacing = QLineEditClick()
    self.hLayout35.addWidget(self.textLinespacing)
    self.textLinespacing.setText(str(self.style['linespacing']))
    self.textLinespacing.setToolTip('Text linespacing of object')
    self.textLinespacing.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textLinespacing.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textLinespacing.editingFinished.connect(partial(self.changeStyle, self.target, 'linespacing', self.textLinespacing, 0., 10., False))
    
    # text rotation
    self.textAngleGroup = QWidgetMac()
    self.vLayout.addWidget(self.textAngleGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.textAngleGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textAngleLabel = QtWidgets.QLabel('Angle')
    self.textAngleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textAngleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.textAngleLabel)
    # text angle spin
    self.textAngleSpin = QDoubleSpinClick()
    self.hLayout4.addWidget(self.textAngleSpin)
    self.textAngleSpin.setMinimum(0.0)
    self.textAngleSpin.setMaximum(360.0)
    self.textAngleSpin.setValue(self.style['rotation'])
    self.textAngleSpin.setToolTip('Text label angle of object')
    self.textAngleSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textAngleSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textAngleSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'rotation', self.textAngleSpin, False))
    self.textAngleSpin.setSingleStep(10)
    self.textAngleSpin.setWrapping(True)
    self.textAngleSpin.setAdaptive(False)
    
    # text type
    self.textTypeGroup = QWidgetMac()
    self.vLayout.addWidget(self.textTypeGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.textTypeGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.textTypeLabel = QtWidgets.QLabel('Type')
    self.textTypeLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.textTypeLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout5.addWidget(self.textTypeLabel)

    self.textTypes = ['x', 'y', 'labels']
    self.textTypeBox = QComboBoxMac()
    self.textTypeBox.setToolTip('Text label type of object')
    self.textTypeBox.addItems(self.textTypes)
    self.textTypeBox.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.textTypeBox.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['type'] in self.textTypes):
      currindex = self.textTypes.index(self.style['type'])
      self.textTypeBox.setCurrentIndex(currindex)
    else:
      self.textTypeBox.setCurrentIndex(0)
    self.textTypeBox.activated.connect(partial(self.selectStyle, self.target, 'type', self.textTypeBox))
    self.hLayout5.addWidget(self.textTypeBox)

  def setDisplay(self, target=None):
    # toggled display of text graphics
    if(target != None):
      self.style['showText'] = self.displayLabel.isChecked()
      if(self.residMode):
        target.toggleResidText(self.style['showText'])
      else:
        target.toggleText(self.style['showText'])

  def setColor(self, target=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'color'):
          self.textColorButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidTextStyle(key, value, redraw=True)
        else:
          target.setTextStyle(key, value, redraw=True)
        # update legend if needed - not needed as bars do not feature in legend
        #self.updateLegend()

      # bring back the QMenu
      self.parent.togglePersist(False)

  def selectStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      value = str(entryfield.currentText())
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidTextStyle(key, value, redraw=True)
      else:
        target.setTextStyle(key, value, redraw=True)
        # update legend if needed - not needed as errors do not feature in legend
        #self.updateLegend(redraw=True)

  def alterStyle(self, target=None, key=None, entryfield=None, integer=False):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        if(integer):
          value = int(entryfield.value())
        else:
          value = float(entryfield.value())
        originalvalue = value
      except:
        if(integer):
          value = 0
          originalvalue = 1
        else:
          value = 0.0
          originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidTextStyle(key, value, redraw=True)
      else:
        target.setTextStyle(key, value, redraw=True)
      # update legend if needed - not needed as text does not feature in legend
      #self.updateLegend()
    
  def changeStyle(self, target=None, key=None, entryfield=None, minval=0, maxval=1, integer=False):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        if(integer):
          value = int(entryfield.text())
        else:
          value = float(entryfield.text())
        originalvalue = value
      except:
        if(integer):
          value = 0
          originalvalue = 1
        else:
          value = 0.0
          originalvalue = 1.0
      if(maxval != None):
        if(integer):
          value = min(value, maxval)
        else:
          value = np.min((value, maxval))
      if(minval != None):
        if(integer):
          value = max(value, minval)
        else:
          value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidTextStyle(key, value, redraw=True)
      else:
        target.setTextStyle(key, value, redraw=True)
      # update legend if needed - not needed as text does not feature in legend
      #self.updateLegend()

  def changeFontStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key in ['fontweight', 'fontstyle']) and (entryfield != None)):
      value = 'normal'
      isChecked = entryfield.isChecked()
      if(key == 'fontweight'):
        if(isChecked):
          value = 'bold'
      else:
        if(isChecked):
          value = 'italic'
      if(self.residMode):
        target.setResidTextStyle(key, value, redraw=True)
      else:
        target.setTextStyle(key, value, redraw=True)
    
  def changeFont(self, target=None):
    if(target != None):
      useFont = str(self.textFontBox.currentText())
      if(self.residMode):
        target.setResidTextStyle('fontname', useFont, redraw=True)
      else:
        target.setTextStyle('fontname', useFont, redraw=True)

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

class errorStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None, residMode=False):
    super(errorStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    self.residMode = residMode

    # valid line styles
    self.linestyles = ['None', '-', '--', '-.', ':']
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.dashstyles = ['butt', 'round', 'projecting']
    self.markerstyles = []
    self.markerstyles.extend(matplotlib.lines.Line2D.markers)
    # weed out duplicate blank items
    blankItems = [i for i in self.markerstyles if i in ['', ' ', 'None', None]]
    while (len(blankItems) - 1):
      killItem = blankItems[-1]
      self.markerstyles = [i for i in self.markerstyles if i != killItem]
      blankItems = blankItems[:-1]
    self.orderedstyles = ['.', ',', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', '*', 'h', 'H', '+',\
                          'x', 'D', 'd', '|', '_', 'P', 'X', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 'None', None, ' ', '']
    self.orderedstyles = [i for i in self.orderedstyles if (i in self.markerstyles)]
    if(self.parent.parent.parent.graphicsarea.advancedExport['advancedGraphics']):
      self.orderedstyles += [i for i in CUSTOM_MARKERS]
      
    self.fillstyles = [i for i in matplotlib.lines.Line2D.fillStyles if not i in ['', 'none', 'None', None]]

    # set up initial values
    self.style, self.shadystyle = {}, {}
    if (self.target != None):
      if(self.residMode):
        getStyle = self.target.getResidStyle()
      else:
        getStyle = self.target.getErrorStyle()
        getShadystyle = self.target.getErrorShadyStyle()
        for entry in getShadystyle:
          self.shadystyle[entry] = getShadystyle[entry]
      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        self.style[entry] = getStyle[entry]
    else:
      self.style['color'] = [0.0, 0.0, 0.0, 1.0]
      self.style['linewidth'] = 1.0
      self.style['marker'] = '_'
      self.style['markerX'] = '|'
      self.style['markeredgewidth'] = 1.0
      self.style['markersize'] = 10.0
      self.style['fillstyle'] = 'full'
      self.style['errorInFront'] = False
      self.style['markerfacecolor'] = [1.0, 1.0, 1.0, 1.0]
      self.style['markerfacecoloralt'] = [0.0, 1.0, 1.0, 1.0]
      self.style['markeredgecolor'] = [0.0, 0.0, 0.0, 1.0]
      self.style['fillstyle'] = 'full'
      self.style['doFill'] = True
      
      self.shadystyle = {}
      self.shadystyle['visible'] = True
      self.shadystyle['facecolor'] = [0.8, 0.2, 0.2, 0.5]
      self.shadystyle['edgecolor'] = [0.8, 0.2, 0.2, 1.0]
      self.shadystyle['linestyle'] = 'None'
      self.shadystyle['linewidth'] = 1.0
      
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.hLayout_top = QtWidgets.QHBoxLayout(self)
    self.hLayout_top.setContentsMargins(0, 0, 0, 0)
    self.hLayout_top.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.topGroup1 = QWidgetMac()
    self.hLayout_top.addWidget(self.topGroup1)
    
    self.vLayout = QtWidgets.QVBoxLayout(self.topGroup1)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    # heading
    self.displayGroup = QWidgetMac()
    self.vLayout.addWidget(self.displayGroup)
    self.hLayout0 = QtWidgets.QHBoxLayout(self.displayGroup)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.errorStyleLabel = QtWidgets.QLabel()
    self.errorStyleLabel.setText('Error')
    font = self.errorStyleLabel.font()
    font.setBold(True)
    self.errorStyleLabel.setFont(font)
    self.errorStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.errorStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.errorStyleLabel)    
    
    # display error?
    self.displayLabel = QPushButtonCheckable()
    self.displayLabel.setText('Show?')
    self.displayLabel.setToolTip('Show error bar of object')
    self.displayLabel.setChecked(self.style['visible'])
    self.displayLabel.clicked.connect(partial(self.setDisplay, self.target))
    self.displayLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.displayLabel)
    
    # show bars in which direction?
    self.directionstyles = ['negative', 'both', 'positive']
    self.directionGroup = QWidgetMac()
    self.vLayout.addWidget(self.directionGroup)
    self.hLayout05 = QtWidgets.QHBoxLayout(self.directionGroup)
    self.hLayout05.setContentsMargins(0, 0, 0, 0)
    self.hLayout05.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout05.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.directionLabel = QtWidgets.QLabel('Direction')
    self.directionLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.directionLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout05.addWidget(self.directionLabel)
    self.comboDirection = QComboBoxMac()
    self.comboDirection.setToolTip('Error bar direction of object')
    for entry in self.directionstyles:
      self.comboDirection.addItem(entry)
    if(self.style['direction'] in self.directionstyles):
      currindex = self.directionstyles.index(self.style['direction'])
    else:
      currindex = 0
    self.comboDirection.setCurrentIndex(currindex)
    self.comboDirection.activated.connect(partial(self.selectStyle, self.target, 'direction', self.comboDirection))
    self.comboDirection.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboDirection.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout05.addWidget(self.comboDirection)

    # line width
    self.lineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineWidthGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.lineWidthGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineWidthLabel = QtWidgets.QLabel('Linewidth')
    self.lineWidthLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.lineWidthLabel)
    # line width spin box
    self.lineWidthSpin = QDoubleSpinClick()
    self.hLayout.addWidget(self.lineWidthSpin)
    self.lineWidthSpin.setMinimum(0.0)
    self.lineWidthSpin.setMaximum(100.0)
    self.lineWidthSpin.setValue(self.style['linewidth'])
    self.lineWidthSpin.setToolTip('Error bar line width of object')
    self.lineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'linewidth', self.lineWidthSpin))
    
    # line color
    self.lineColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.lineColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineColorLabel = QtWidgets.QLabel('Linecolor')
    self.lineColorLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.lineColorLabel)
    
    self.lineColorButton = QPushButtonColor()
    self.lineColorButton.setToolTip('Error bar line color of object')
    self.lineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['color'][0:3]]
    self.lineColorButton.setMyColor(*colorvalue)
    self.lineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.lineColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'color'))
    self.hLayout2.addWidget(self.lineColorButton)

    # line style
    self.lineStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.lineStyleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.lineStyleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineStyleLabel = QtWidgets.QLabel('Linestyle')
    self.lineStyleLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineStyleLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.lineStyleLabel)
    self.comboStyle = QComboBoxMac()
    self.comboStyle.setToolTip('Error bar line style of object')
    for entry in self.linestyles:
      self.comboStyle.addItem(entry)
    if(self.style['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['linestyle'])
    else:
      currindex = 0
    self.comboStyle.setCurrentIndex(currindex)
    self.comboStyle.activated.connect(partial(self.selectStyle, self.target, 'linestyle', self.comboStyle))
    self.comboStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.comboStyle)
    
    # marker in front?
    self.markerZOrderGroup = QWidgetMac()
    self.vLayout.addWidget(self.markerZOrderGroup)
    self.hLayout7 = QtWidgets.QHBoxLayout(self.markerZOrderGroup)
    self.hLayout7.setContentsMargins(0, 0, 0, 0)
    self.hLayout7.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout7.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerZOrderLabel = QPushButtonCheckable()
    self.markerZOrderLabel.setText('In front?')
    self.markerZOrderLabel.setToolTip('Show error bar in front of marker')
    self.markerZOrderLabel.setChecked(self.style['errorInFront'])
    self.markerZOrderLabel.clicked.connect(partial(self.setZOrder, self.target))
    self.markerZOrderLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.markerZOrderLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout7.addWidget(self.markerZOrderLabel)
    
    # set up configuration of cap style
    divider = VLine()
    self.hLayout_top.addWidget(divider)
    self.topGroup3 = QWidgetMac()
    self.hLayout_top.addWidget(self.topGroup3)
    
    self.vLayout3 = QtWidgets.QVBoxLayout(self.topGroup3)
    self.vLayout3.setContentsMargins(0, 0, 0, 0)
    self.vLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout3.setSpacing(2)

    # heading
    self.errorStyleLabel3 = QtWidgets.QLabel()
    self.errorStyleLabel3.setText('Cap')
    font = self.errorStyleLabel3.font()
    font.setBold(True)
    self.errorStyleLabel3.setFont(font)
    self.vLayout3.addWidget(self.errorStyleLabel3)    

    # marker size
    self.markerSizeGroup = QWidgetMac()
    self.vLayout3.addWidget(self.markerSizeGroup)
    self.hLayout6 = QtWidgets.QHBoxLayout(self.markerSizeGroup)
    self.hLayout6.setContentsMargins(0, 0, 0, 0)
    self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerSizeLabel = QtWidgets.QLabel('Size')
    self.markerSizeLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.markerSizeLabel)
    # marker size spin box
    self.markerSizeSpin = QDoubleSpinClick()
    self.hLayout6.addWidget(self.markerSizeSpin)
    self.markerSizeSpin.setMinimum(0.0)
    self.markerSizeSpin.setMaximum(100.0)
    self.markerSizeSpin.setValue(self.style['markersize'])
    self.markerSizeSpin.setToolTip('Error cap size of object')
    self.markerSizeSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'markersize', self.markerSizeSpin))

    # marker facecolor
    self.markerFaceColorGroup = QWidgetMac()
    self.vLayout3.addWidget(self.markerFaceColorGroup)
    self.hLayout30 = QtWidgets.QHBoxLayout(self.markerFaceColorGroup)
    self.hLayout30.setContentsMargins(0, 0, 0, 0)
    self.hLayout30.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout30.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerFaceColorLabel = QPushButtonCheckable()
    self.markerFaceColorLabel.setText('Face')
    self.markerFaceColorLabel.setToolTip('Show error cap fill of object')
    self.markerFaceColorLabel.setChecked(self.style['doFill'])
    self.markerFaceColorLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerFaceColorLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerFaceColorLabel.clicked.connect(partial(self.toggleMarkerFill, self.target, self.markerFaceColorLabel))
    self.hLayout30.addWidget(self.markerFaceColorLabel)
    
    self.markerFaceColorButton = QPushButtonColor()
    self.markerFaceColorButton.setToolTip('Error cap fill color of object')
    self.markerFaceColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['markerfacecolor'][0:3]]
    self.markerFaceColorButton.setMyColor(*colorvalue)
    self.markerFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerFaceColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'markerfacecolor'))
    self.hLayout30.addWidget(self.markerFaceColorButton)
      
    # marker edgecolor
    self.markerEdgeColorLabel = QtWidgets.QLabel('Edge')
    self.markerEdgeColorLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeColorLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout30.addWidget(self.markerEdgeColorLabel)

    self.markerEdgeColorButton = QPushButtonColor()
    self.markerEdgeColorButton.setToolTip('Error cap edge color of object')
    self.markerEdgeColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['markeredgecolor'][0:3]]
    self.markerEdgeColorButton.setMyColor(*colorvalue)
    self.markerEdgeColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerEdgeColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerEdgeColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerEdgeColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'markeredgecolor'))
    self.hLayout30.addWidget(self.markerEdgeColorButton)

    # marker edge width
    self.markerEdgeWidthGroup = QWidgetMac()
    self.vLayout3.addWidget(self.markerEdgeWidthGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.markerEdgeWidthGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerEdgeWidthLabel = QtWidgets.QLabel('Edgewidth')
    self.markerEdgeWidthLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout5.addWidget(self.markerEdgeWidthLabel)
    # marker edge width spin box
    self.markerEdgeWidthSpin = QDoubleSpinClick()
    self.hLayout5.addWidget(self.markerEdgeWidthSpin)
    self.markerEdgeWidthSpin.setMinimum(0.0)
    self.markerEdgeWidthSpin.setMaximum(100.0)
    self.markerEdgeWidthSpin.setValue(self.style['markeredgewidth'])
    self.markerEdgeWidthSpin.setToolTip('Error cap edge width of object')
    self.markerEdgeWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'markeredgewidth', self.markerEdgeWidthSpin))

    # marker style
    self.markerTypeGroup = QWidgetMac()
    self.vLayout3.addWidget(self.markerTypeGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.markerTypeGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerTypeLabel = QtWidgets.QLabel('Style')
    self.markerTypeLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerTypeLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.markerTypeLabel)
    self.markerType = MarkerButton()
    self.markerType.setToolTip('Error cap vertical symbol of object')
    self.markerType.clicked.connect(partial(self.markerCallback, self.markerType))
    if(self.style['marker'] in self.orderedstyles):
      self.markerType.setSymbol(self.style['marker'])
    else:
      self.markerType.setSymbol(self.orderedstyles[0])
    self.markerType.setSymbolList(self.orderedstyles)
    self.markerType.setParent(self)
    self.markerType.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerType.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.markerType)
    
    # marker styleX
    self.markerTypeXGroup = QWidgetMac()
    self.vLayout3.addWidget(self.markerTypeXGroup)
    self.hLayout41 = QtWidgets.QHBoxLayout(self.markerTypeXGroup)
    self.hLayout41.setContentsMargins(0, 0, 0, 0)
    self.hLayout41.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout41.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.markerTypeXLabel = QtWidgets.QLabel('StyleX')
    self.markerTypeXLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerTypeXLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout41.addWidget(self.markerTypeXLabel)
    self.markerTypeX = MarkerButton()
    self.markerTypeX.setToolTip('Error cap horizontal symbol of object')
    self.markerTypeX.clicked.connect(partial(self.markerCallback, self.markerTypeX))
    # need to exclude certain styles that make matplotlib crash -- this is quite ugly
    excludeStyles = [0, 5, 6, 7, 9, 10, 11]
    self.okayStyles = [i for i in self.orderedstyles if (not i in excludeStyles)]
    if(self.style['markerX'] in self.okayStyles):
      self.markerTypeX.setSymbol(self.style['markerX'])
    else:
      self.markerTypeX.setSymbol(self.okayStyles[0])
    self.markerTypeX.setSymbolList(self.okayStyles)
    self.markerTypeX.setParent(self)
    self.markerTypeX.setAlternate(True)
    self.markerTypeX.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerTypeX.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout41.addWidget(self.markerTypeX)
    
    # marker fill style
    self.markerFillStyleGroup = QWidgetMac()
    self.vLayout3.addWidget(self.markerFillStyleGroup)
    self.hLayout31 = QtWidgets.QHBoxLayout(self.markerFillStyleGroup)
    self.hLayout31.setContentsMargins(0, 0, 0, 0)
    self.hLayout31.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout31.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.markerFillStyleLabel = QtWidgets.QLabel('Fillstyle')
    self.markerFillStyleLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerFillStyleLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout31.addWidget(self.markerFillStyleLabel)
    self.comboFillStyle = QComboBoxMac()
    self.comboFillStyle.setToolTip('Error cap fill style of object')
    for entry in self.fillstyles:
      self.comboFillStyle.addItem(str(entry))
    if(self.style['fillstyle'] in self.fillstyles):
      currindex = self.fillstyles.index(self.style['fillstyle'])
    else:
      currindex = 0
    self.comboFillStyle.setCurrentIndex(currindex)
    self.comboFillStyle.activated.connect(partial(self.selectStyle, self.target, 'fillstyle', self.comboFillStyle))
    self.comboFillStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboFillStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout31.addWidget(self.comboFillStyle)    

    self.markerAltColorButton = QPushButtonColor()
    self.markerAltColorButton.setToolTip('Error cap alternate fill color of object')
    self.markerAltColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['markerfacecoloralt'][0:3]]
    self.markerAltColorButton.setMyColor(*colorvalue)
    self.markerAltColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerAltColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerAltColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerAltColorButton.clicked.connect(partial(self.setColor, target = self.target, key = 'markerfacecoloralt'))
    self.hLayout31.addWidget(self.markerAltColorButton)

    # set up configuration of error shading
    divider = VLine()
    self.hLayout_top.addWidget(divider)
    self.topGroup2 = QWidgetMac()
    self.hLayout_top.addWidget(self.topGroup2)
    
    self.vLayout2 = QtWidgets.QVBoxLayout(self.topGroup2)
    self.vLayout2.setContentsMargins(0, 0, 0, 0)
    self.vLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout2.setSpacing(2)

    # heading
    self.displayShadyGroup = QWidgetMac()
    self.vLayout2.addWidget(self.displayShadyGroup)
    self.hLayout20 = QtWidgets.QHBoxLayout(self.displayShadyGroup)
    self.hLayout20.setContentsMargins(0, 0, 0, 0)
    self.hLayout20.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout20.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.errorStyleLabel2 = QtWidgets.QLabel()
    self.errorStyleLabel2.setText('Shading')
    font = self.errorStyleLabel2.font()
    font.setBold(True)
    self.errorStyleLabel2.setFont(font)
    self.errorStyleLabel2.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.errorStyleLabel2.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout20.addWidget(self.errorStyleLabel2)    

    self.displayShadyCheck = QPushButtonCheckable()
    self.displayShadyCheck.setText('Show?')
    self.displayShadyCheck.setToolTip('Show error shading of object')
    self.displayShadyCheck.setChecked(self.shadystyle['visible'])
    self.displayShadyCheck.clicked.connect(partial(self.setShadyDisplay, self.target))
    self.displayShadyCheck.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayShadyCheck.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout20.addWidget(self.displayShadyCheck)

    # line width
    self.lineWidthShadyGroup = QWidgetMac()
    self.vLayout2.addWidget(self.lineWidthShadyGroup)
    self.hLayout21 = QtWidgets.QHBoxLayout(self.lineWidthShadyGroup)
    self.hLayout21.setContentsMargins(0, 0, 0, 0)
    self.hLayout21.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout21.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineWidthShadyLabel = QtWidgets.QLabel('Linewidth')
    self.lineWidthShadyLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthShadyLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout21.addWidget(self.lineWidthShadyLabel)
    # shady line width spin box
    self.lineWidthShadySpin = QDoubleSpinClick()
    self.hLayout21.addWidget(self.lineWidthShadySpin)
    self.lineWidthShadySpin.setMinimum(0.0)
    self.lineWidthShadySpin.setMaximum(100.0)
    self.lineWidthShadySpin.setValue(self.shadystyle['linewidth'])
    self.lineWidthShadySpin.setToolTip('Error shading line width of object')
    self.lineWidthShadySpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthShadySpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.lineWidthShadySpin.editingFinished.connect(partial(self.alterShadyStyle, self.target, 'linewidth', self.lineWidthShadySpin))
    
    # line color
    self.lineColorShadyGroup = QWidgetMac()
    self.vLayout2.addWidget(self.lineColorShadyGroup)
    self.hLayout22 = QtWidgets.QHBoxLayout(self.lineColorShadyGroup)
    self.hLayout22.setContentsMargins(0, 0, 0, 0)
    self.hLayout22.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout22.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineColorShadyLabel = QtWidgets.QLabel('Linecolor')
    self.lineColorShadyLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineColorShadyLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout22.addWidget(self.lineColorShadyLabel)
    
    self.lineColorShadyButton = QPushButtonColor()
    self.lineColorShadyButton.setToolTip('Error shading line color of object')
    self.lineColorShadyButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.shadystyle['edgecolor'][0:3]]
    self.lineColorShadyButton.setMyColor(*colorvalue)
    self.lineColorShadyButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorShadyButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.lineColorShadyButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.lineColorShadyButton.clicked.connect(partial(self.setShadyColor, target = self.target, key = 'edgecolor'))
    self.hLayout22.addWidget(self.lineColorShadyButton)

    # line style
    self.shadylinestyles = [i for i in self.linestyles if (i != 'None')]
    self.lineStyleShadyGroup = QWidgetMac()
    self.vLayout2.addWidget(self.lineStyleShadyGroup)
    self.hLayout23 = QtWidgets.QHBoxLayout(self.lineStyleShadyGroup)
    self.hLayout23.setContentsMargins(0, 0, 0, 0)
    self.hLayout23.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout23.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.lineStyleShadyLabel = QtWidgets.QLabel('Linestyle')
    self.lineStyleShadyLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.lineStyleShadyLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout23.addWidget(self.lineStyleShadyLabel)
    self.comboShadyStyle = QComboBoxMac()
    self.comboShadyStyle.setToolTip('Error shading line style of object')
    for entry in self.shadylinestyles:
      self.comboShadyStyle.addItem(entry)
    if(self.shadystyle['linestyle'] in self.shadylinestyles):
      currindex = self.shadylinestyles.index(self.shadystyle['linestyle'])
    else:
      currindex = 0
    self.comboShadyStyle.setCurrentIndex(currindex)
    self.comboShadyStyle.activated.connect(partial(self.selectShadyStyle, self.target, 'linestyle', self.comboShadyStyle))
    self.comboShadyStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboShadyStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout23.addWidget(self.comboShadyStyle)
    
    # fill color
    self.fillColorShadyGroup = QWidgetMac()
    self.vLayout2.addWidget(self.fillColorShadyGroup)
    self.hLayout24 = QtWidgets.QHBoxLayout(self.fillColorShadyGroup)
    self.hLayout24.setContentsMargins(0, 0, 0, 0)
    self.hLayout24.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout24.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.fillColorShadyLabel = QtWidgets.QLabel('Fillcolor')
    self.fillColorShadyLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.fillColorShadyLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout24.addWidget(self.fillColorShadyLabel)
      
    self.fillColorShadyButton = QPushButtonColor()
    self.fillColorShadyButton.setToolTip('Error shading fill color of object')
    self.fillColorShadyButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.shadystyle['facecolor'][0:3]]
    self.fillColorShadyButton.setMyColor(*colorvalue)
    self.fillColorShadyButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorShadyButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.fillColorShadyButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.fillColorShadyButton.clicked.connect(partial(self.setShadyColor, target = self.target, key = 'facecolor'))
    self.hLayout24.addWidget(self.fillColorShadyButton)

  def setShadyDisplay(self, target=None):
    # toggled display of error bars
    if(target != None):
      self.shadystyle['visible'] = self.displayShadyCheck.isChecked()
      target.setErrorShadyStyle('visible', self.shadystyle['visible'], redraw=True)

  def setDisplay(self, target=None):
    # toggled display of error bars
    if(target != None):
      self.style['visible'] = self.displayLabel.isChecked()
      target.setErrorStyle('visible', self.style['visible'], redraw=True)

  def setZOrder(self, target=None):
    # toggle relative z order of error bars
    state = self.markerZOrderLabel.isChecked()
    self.style['errorInFront'] = state
    if(target != None):
      target.setZOrderError(state, redraw=True)

  def setColor(self, target=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'color'):
          self.lineColorButton.setMyColor(*value)
        elif(key == 'markerfacecolor'):
          self.markerFaceColorButton.setMyColor(*value)
        elif(key == 'markeredgecolor'):
          self.markerEdgeColorButton.setMyColor(*value)
        elif(key == 'markerfacecoloralt'):
          self.markerAltColorButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.style[key] = value
        if(self.residMode):
          target.setResidStyle(key, value, redraw=True)
        else:
          target.setErrorStyle(key, value, redraw=True)

      # bring back the QMenu
      self.parent.togglePersist(False)
    
  def setShadyColor(self, target=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if (key in self.shadystyle):
        prevColor = [int(255 * i) for i in self.shadystyle[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'edgecolor'):
          self.lineColorShadyButton.setMyColor(*value)
        elif(key == 'facecolor'):
          self.fillColorShadyButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        target.setErrorShadyStyle(key, value, redraw=True)

      # bring back the QMenu
      self.parent.togglePersist(False)
          
  def selectStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      if(key == 'marker'):
        value = self.orderedstyles[entryfield.currentIndex()]
      elif(key == 'markerX'):
        value = self.okayStyles[entryfield.currentIndex()]
      else:
        value = str(entryfield.currentText())
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStyle(key, value, redraw=True)
      else:
        target.setErrorStyle(key, value, redraw=True)
        # update legend if needed - not needed as errors do not feature in legend
        #self.updateLegend(redraw=True)
      
  def selectShadyStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      value = str(entryfield.currentText())
      if(key in self.shadystyle):
        self.shadystyle[key] = value
      target.setErrorShadyStyle(key, value, redraw=True)
      # update legend if needed - not needed as errors do not feature in legend
      #self.updateLegend(redraw=True)

  def toggleMarkerFill(self, target = None, entryfield = None):
    # toggles whether to fill markers or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['doFill'] = value
      if(self.residMode):
        target.toggleResidMarkerFill(value, redraw=True)
      else:
        target.toggleErrorMarkerFill(value, redraw=True)

  def alterStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
      if(self.residMode):
        target.setResidStyle(key, value, redraw=True)
      else:
        target.setErrorStyle(key, value, redraw=True)

  def alterShadyStyle(self, target=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.shadystyle):
        self.shadystyle[key] = value
      target.setErrorShadyStyle(key, value, redraw=True)

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.parent.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

  def markerCallback(self, callButton=None):
    # opens menu to access advanced configuration options
    self.menu = MarkerMenu(self)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # bottom align position of QMenu
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)
    
    # toggle to discriminate between marker and markerX
    self.menu.setAlternate(callButton == self.markerTypeX)

  def setMarker(self, symbol='', alternate=False):
    # sets marker symbol to selected type
    if(alternate):
      key, targetField, testList = 'markerX', self.markerTypeX, self.okayStyles
    else:
      key, targetField, testList = 'marker', self.markerType, self.orderedstyles
        
    if((symbol in testList) and (symbol != self.style[key])):
      self.style[key] = symbol
      targetField.setSymbol(symbol)
      self.target.setErrorStyle(key, symbol, redraw=True)

class ObjectsArea(QWidgetMac):
  def __init__(self, parent = None):
    super(ObjectsArea, self).__init__()
    self.parent = parent
    
    # set up cycling colors and markers
    self.cycleColors = [[0.886, 0.29, 0.2, 1.0], [0.204, 0.5414, 0.7414, 1.0], [0.596, 0.557, 0.835, 1.0],
                        [0.467, 0.467, 0.467, 1.0], [0.984, 0.757, 0.369, 1.0], [0.557, 0.7299, 0.259, 1.0]]
    self.cycleMarkers = ['o', '^', 's', 'p', 'h', 'd']
    self.cycleLinestyles = ['solid', 'dashed', 'dotted', 'dashdot']
    
    # control display of config menu
    self.displayBarConfig, self.displayErrorConfig, self.displayTextConfig, self.displayLineConfig = False, False, False, True
    self.displayDeclusterConfig, self.displayViolinConfig, self.displayIntervalConfig, self.displayBootConfig = False, False, False, False
    self.displayNotesConfig, self.displayAreaConfig, self.displayScatterConfig = False, False, False
    self.declusterTolX, self.declusterTolY, self.declusterMaxX = 0.1, 0.1, 2
    self.formulaX, self.formulaY = '', ''
    self.interpolateMode, self.interpolateCheck, self.interpolateSmooth = 'cubic', False, 0.2
    self.avgMovingCheck, self.avgNumberPoints = True, 2
    self.variationStyle = {'varyItem': 'line', 'varyItem2': 'line', 'varyCycle': 10, 'varyCycleOffset': 0, 'varyCmap': 'jet',
                  'varyLinestyle': 2, 'varyLinestyleOffset': 0, 'varyMarkerstyle': 4, 'varyMarkerstyleOffset': 0, 'visOnly': True}
    
    # advanced interpolation setting
    self.advancedInterpolate = {'points': 1000}

    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)

    self.masterwidget = QtWidgets.QSplitter(QtCore.Qt.Orientation.Vertical)
    self.vLayout.addWidget(self.masterwidget)
    self.masterwidget.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
    self.masterwidget.setChildrenCollapsible(False)

    # data sets
    self.dataSetOuterContainer = QWidgetMac()
    self.vLayout_data = QtWidgets.QVBoxLayout(self.dataSetOuterContainer)
    self.vLayout_data.setContentsMargins(0, 0, 0, 0)
    self.vLayout_data.setSpacing(2)
    self.masterwidget.addWidget(self.dataSetOuterContainer)

    self.dataSetContainer = QWidgetMac()
    self.hLayout00 = QtWidgets.QHBoxLayout(self.dataSetContainer)
    self.hLayout00.setContentsMargins(0, 0, 0, 0)
    self.vLayout_data.addWidget(self.dataSetContainer)
    self.dataSetLabel = QtWidgets.QLabel()
    useFont = self.dataSetLabel.font()
    useFont.setBold(True)
    self.dataSetLabel.setFont(useFont)
    self.dataSetLabel.setText('Data Sets')
    self.hLayout00.addWidget(self.dataSetLabel)
    self.dataSetCreateButton = QPushButtonMac()
    self.dataSetCreateButton.setText(' Add Data Set')
    self.dataSetCreateButton.setToolTip('Create empty data object')
    self.dataSetCreateButton.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.dataSetCreateButton.setMaximumSize(QtCore.QSize(int(90 * SCALEFONT), int(BASE_SIZE)))
    self.dataSetCreateButton.clicked.connect(self.dataSetCreate)
    self.dataSetCreateButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout00.addWidget(self.dataSetCreateButton)
    self.hLayout00.addStretch()

    self.dataSetTable = QtWidgets.QTableWidget()
    self.dataSetTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.vLayout_data.addWidget(self.dataSetTable)
    self.refreshDataTable()

    # curves
    self.curvesOuterContainer = QWidgetMac()
    self.vLayout_curves = QtWidgets.QVBoxLayout(self.curvesOuterContainer)
    self.vLayout_curves.setContentsMargins(0, 0, 0, 0)
    self.vLayout_curves.setSpacing(2)
    self.masterwidget.addWidget(self.curvesOuterContainer)

    self.curvesContainer = QWidgetMac()
    self.hLayout0 = QtWidgets.QHBoxLayout(self.curvesContainer)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.vLayout_curves.addWidget(self.curvesContainer)
    self.curvesLabel = QtWidgets.QLabel()
    useFont = self.curvesLabel.font()
    useFont.setBold(True)
    self.curvesLabel.setFont(useFont)
    self.curvesLabel.setText('Curves')
    self.hLayout0.addWidget(self.curvesLabel)
    self.curvesCreateButton = QPushButtonMac()
    self.curvesCreateButton.setText(' Add Curve')
    self.curvesCreateButton.setToolTip('Create new curve object')
    self.curvesCreateButton.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.curvesCreateButton.setMaximumSize(QtCore.QSize(int(90 * SCALEFONT), int(BASE_SIZE)))
    self.curvesCreateButton.clicked.connect(self.curveCreate)
    self.curvesCreateButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout0.addWidget(self.curvesCreateButton)
    self.hLayout0.addStretch()

    self.curvesTable = QtWidgets.QTableWidget()
    self.curvesTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.vLayout_curves.addWidget(self.curvesTable)
    self.refreshCurvesTable()
    
    # extras
    self.extrasOuterContainer = QWidgetMac()
    self.vLayout_extras = QtWidgets.QVBoxLayout(self.extrasOuterContainer)
    self.vLayout_extras.setContentsMargins(0, 0, 0, 0)
    self.vLayout_extras.setSpacing(2)
    self.masterwidget.addWidget(self.extrasOuterContainer)

    self.extrasContainer = QWidgetMac()
    self.hLayout = QtWidgets.QHBoxLayout(self.extrasContainer)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout_extras.addWidget(self.extrasContainer)

    self.extrasLabel = QtWidgets.QLabel()
    useFont = self.extrasLabel.font()
    useFont.setBold(True)
    self.extrasLabel.setFont(useFont)
    self.extrasLabel.setText('Extras')
    self.hLayout.addWidget(self.extrasLabel)
    self.extrasCreateLineButton = QPushButtonMac()
    self.extrasCreateLineButton.setText(' Line')
    self.extrasCreateLineButton.setToolTip('Create new line object')
    self.extrasCreateLineButton.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateLineButton.setMaximumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateLineButton.clicked.connect(partial(self.extrasCreate, 'line'))
    self.extrasCreateLineButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout.addWidget(self.extrasCreateLineButton)

    self.extrasCreateShapeButton = QPushButtonMac()
    self.extrasCreateShapeButton.setText(' Shape')
    self.extrasCreateShapeButton.setToolTip('Create new shape object')
    self.extrasCreateShapeButton.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateShapeButton.setMaximumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateShapeButton.clicked.connect(partial(self.extrasCreate, 'shape'))
    self.extrasCreateShapeButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout.addWidget(self.extrasCreateShapeButton)

    self.extrasCreateTextButton = QPushButtonMac()
    self.extrasCreateTextButton.setText(' Text')
    self.extrasCreateTextButton.setToolTip('Create new text object')
    self.extrasCreateTextButton.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateTextButton.setMaximumSize(QtCore.QSize(int(65 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateTextButton.clicked.connect(partial(self.extrasCreate, 'text'))
    self.extrasCreateTextButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout.addWidget(self.extrasCreateTextButton)

    self.extrasCreateAnnotationButton = QPushButtonMac()
    self.extrasCreateAnnotationButton.setText(' Annotation')
    self.extrasCreateAnnotationButton.setToolTip('Create new annotation object')
    self.extrasCreateAnnotationButton.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateAnnotationButton.setMaximumSize(QtCore.QSize(int(90 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateAnnotationButton.clicked.connect(partial(self.extrasCreate, 'annotation'))
    self.extrasCreateAnnotationButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout.addWidget(self.extrasCreateAnnotationButton)

    self.extrasCreateFreehandButton = QPushButtonCheckable()
    self.extrasCreateFreehandButton.setText(' Freehand')
    self.extrasCreateFreehandButton.setToolTip('Create new freehand object')
    self.extrasCreateFreehandButton.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateFreehandButton.setMaximumSize(QtCore.QSize(int(90 * SCALEFONT), int(BASE_SIZE)))
    self.extrasCreateFreehandButton.clicked.connect(partial(self.extrasCreate, 'freehand'))
    self.extrasCreateFreehandButton.setIcon(FOM_ICON_COMMAND_LINK)
    self.hLayout.addWidget(self.extrasCreateFreehandButton)
    self.hLayout.addStretch()
    
    self.extrasTable = QtWidgets.QTableWidget()
    self.extrasTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.vLayout_extras.addWidget(self.extrasTable)
    self.refreshExtrasTable()
    
    # resdiuals
    self.residualsOuterContainer = QWidgetMac()
    self.vLayout_residuals = QtWidgets.QVBoxLayout(self.residualsOuterContainer)
    self.vLayout_residuals.setContentsMargins(0, 0, 0, 0)
    self.vLayout_residuals.setSpacing(2)
    self.masterwidget.addWidget(self.residualsOuterContainer)

    # on Windows and Linux add markers for splitter
    if(not (platform == 'darwin')):
      for number in range(3):
        self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='horizontal', number=number + 1, repeat=5)

    self.residualsContainer = QWidgetMac()
    self.hLayout000 = QtWidgets.QHBoxLayout(self.residualsContainer)
    self.hLayout000.setContentsMargins(0, 0, 0, 0)
    self.vLayout_residuals.addWidget(self.residualsContainer)

    self.residLabel = QtWidgets.QLabel()
    useFont = self.residLabel.font()
    useFont.setBold(True)
    self.residLabel.setFont(useFont)
    self.residLabel.setText('Residuals')
    self.hLayout000.addWidget(self.residLabel)
    # include mock to ensure same height as for data sets, curves and extras
    mock = QWidgetMac()
    mock.setMinimumHeight(int(BASE_SIZE))
    self.hLayout000.addWidget(mock)
    self.residTable = QtWidgets.QTableWidget()
    self.residTable.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.NoSelection)
    self.vLayout_residuals.addWidget(self.residTable)
    self.refreshResidTable()

  def setAdvancedInterpolate(self, key=None, value=None):
    # updates advanced interpolate settings
    if(key in self.advancedInterpolate):
      self.advancedInterpolate[key] = value

  def addSplitterHandleSymbols(self, parentHandle=None, direction='horizontal', number=1, repeat=5):
    # adds spacer symbols to splitter handle
    if(parentHandle != None):
      if(direction in ['horizontal', 'vertical']):
        parentHandle.setHandleWidth(5)
        handli = parentHandle.handle(number)
        if(handli != None):
          if(direction == 'horizontal'):
            layoutli = QtWidgets.QHBoxLayout(handli)
          else:
            layoutli = QtWidgets.QVBoxLayout(handli)
          layoutli.addStretch()
          for repeat in range(repeat):
            bogus = QtWidgets.QWidget()
            bogus.setStyleSheet('QWidget {background-color: #888888;border-radius: 2px;}')
            bogus.setMinimumSize(4, 4)
            bogus.setMaximumSize(4, 4)
            layoutli.addWidget(bogus)
          layoutli.addStretch()
          layoutli.setSpacing(3)
          layoutli.setContentsMargins(1, 0, 0, 0)

  def reportState(self):
    # reports contents for saveState function
    retv = {}
    retv['activeFit'] = self.parent.activeFit
    retv['activeData'] = self.parent.activeData
    retv['visibilityData'] = [i.visibility for i in self.parent.data]
    retv['visibilityFit'] = [i.visibility for i in self.parent.fit]
    retv['visibilityExtras'] = [i.visibility for i in self.parent.extras]
    retv['visibilityResid'] = [i.visibilityResid for i in self.parent.data]
    
    retstring = ''
    for entry in retv:
      retstring += '>>>' + entry + '\n'
      retstring += repr(retv[entry]) + '\n'
    
    return retstring
  
  def restoreState(self, data, zoffsetData=0, zoffsetCurve=0, zoffsetExtras=0, zoffsetResid=0):
    # restores contents for loadState function
    # set visibility of objects
    if('visibilityData' in data):
      for index, entry in enumerate(data['visibilityData']):
        if(index + zoffsetData < len(self.parent.data)):
          self.parent.data[index + zoffsetData].setVisibility(entry, redraw=False)
    if('visibilityFit' in data):
      for index, entry in enumerate(data['visibilityFit']):
        if(index + zoffsetCurve < len(self.parent.fit)):
          self.parent.fit[index + zoffsetCurve].setVisibility(entry, redraw=False)
    if('visibilityExtras' in data):
      for index, entry in enumerate(data['visibilityExtras']):
        if(index + zoffsetExtras < len(self.parent.extras)):
          self.parent.extras[index + zoffsetExtras].setVisibility(entry, redraw=False)
    if('visibilityResid' in data):
      for index, entry in enumerate(data['visibilityResid']):
        if(index + zoffsetResid < len(self.parent.data)):
          self.parent.data[index + zoffsetResid].setVisibilityResid(entry, redraw=False)
    
    # fix possible gaps/duplications in zorder
    self.sanityCheckZOrder()
    
    # update tables
    self.refreshDataTable()
    self.refreshResidTable()
    self.refreshCurvesTable()
    self.parent.plotArea.updateToggloContainer()
    self.parent.globalarea.updateDataSetTable()
        
    # set active data set and curve
    if('activeData' in data):
      # check whether new object to be selected exists (can arise from edits in state file)
      if(zoffsetData + data['activeData'] < len(self.parent.data)):
        # turn off previous radio button
        prevActive = self.parent.activeData
        widget = self.dataSetTable.cellWidget(prevActive, 1)
        widget.blockSignals(True)
        widget.setChecked(False)
        widget.blockSignals(False)
        self.residTable.cellWidget(prevActive + 1, 1).setChecked(False)
        
        widget = self.dataSetTable.cellWidget(zoffsetData + data['activeData'], 1)
        widget.setChecked(True)
        self.residTable.cellWidget(zoffsetData + data['activeData'] + 1, 1).setChecked(True)
      
    if('activeFit' in data):
      # check whether new object to be selected exists (can arise from edits in state file)
      if(zoffsetCurve + data['activeFit'] < len(self.parent.fit)):
        # turn off previous radio button
        prevActive = self.parent.activeFit
        widget = self.curvesTable.cellWidget(prevActive, 1)
        widget.blockSignals(True)
        widget.setChecked(False)
        widget.blockSignals(False)
        
        widget = self.curvesTable.cellWidget(zoffsetCurve + data['activeFit'], 1)
        if(widget != None):
          # in case sth. went wrong with the state file
          widget.blockSignals(True)
          widget.setChecked(True)
          widget.blockSignals(False)
          self.changeActiveCurve(zoffsetCurve + data['activeFit'], redraw=False)

  def extrasCreate(self, extrasType='text'):
    # check whether this is a freehand item which will be handled differently
    if(extrasType == 'freehand'):
      state = self.extrasCreateFreehandButton.isChecked()
      self.parent.plotArea.drawFreehand = state
      if(state):
        self.parent.statusbar.showMessage('Click left mouse button and draw freehand shape on canvas.', self.parent.STATUS_TIME, color='blue')
      return
    # generate extras element
    self.parent.extras.append(ExtrasObject(self.parent))
    # x values
    if(self.parent.plotArea.modeX == 'linear'):
      x = (self.parent.plotArea.minX + self.parent.plotArea.maxX) / 2.0
      arrow__x = (self.parent.plotArea.minX + 2 * self.parent.plotArea.maxX) / 3.0
    else:
      x = np.exp((np.log(self.parent.plotArea.minX) + np.log(self.parent.plotArea.maxX)) / 2.0)
      arrow__x = np.exp((np.log(self.parent.plotArea.minX) + 2 * np.log(self.parent.plotArea.maxX)) / 3.0)
    arrow__x_alt = abs((self.parent.plotArea.minX - self.parent.plotArea.maxX) / 2.0)
    # y values
    if(self.parent.plotArea.modeY == 'linear'):
      y = (self.parent.plotArea.minY + self.parent.plotArea.maxY) / 2.0
      arrow__y = y
    else:
      y = np.exp((np.log(self.parent.plotArea.minY) + np.log(self.parent.plotArea.maxY)) / 2.0)
      arrow__y = y
    arrow__y_alt = abs((self.parent.plotArea.minY - self.parent.plotArea.maxY) / 2.0)
    # process and report
    if(extrasType == 'shape'):
      arrow__x, arrow__y = arrow__x_alt, arrow__y_alt
    x, y, arrow__x, arrow__y = self.roundNumber(x), self.roundNumber(y), self.roundNumber(arrow__x), self.roundNumber(arrow__y)
    x2, y2 = arrow__x, arrow__y
    labeltext = extrasType.capitalize()
    valueDict = {'x': x, 'y': y, 'labeltext': labeltext, 'extrasType': extrasType,\
                 'arrow__x': arrow__x, 'arrow__y': arrow__y, 'x2': x2, 'y2': y2}
    self.parent.extras[-1].setValues(valueDict, redraw=True)
    
    # check for split y axis
    if(self.parent.plotArea.splitY):
      self.parent.extras[-1].onBothAxes = True
      self.parent.extras[-1].duplicateForSplit()

    # update extras table
    self.refreshExtrasTable()
    self.refreshCurvesTable()
    self.refreshDataTable()
    self.parent.plotArea.updateToggloContainer()
      
  def refreshExtrasTable(self):
    # updates extras table
    number_extrasEntry = len(self.parent.extras)
    self.extrasTable.setRowCount(number_extrasEntry)
    self.extrasTable.setColumnCount(7)
    labels = ['vis', 'z', 'name', '', 'action', '', 'axes']
    toolTips = ['This column controls the visibility of the extras objects.',
                'This column controls the z order of the extras objects.',
                'This column holds the name of the extra objects.\n(Displayed on screen for text and annotation objects.)',
                'This column allows to configure the extra objects.',
                'This column allows to copy the extra objects.',
                'This column allows to delete the extra objects.',
                'This column controls on which axis the extra objects are displayed.']
    indices = [i for i in range(len(labels))]
    for index, label, toolTip in zip(indices, labels, toolTips):
      item = QTableWidgetItemToolTip(label)
      item.setToolTip(toolTip)
      self.extrasTable.setHorizontalHeaderItem(index, item)
    # set row height and prevent from resizing
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.extrasTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    hheader = self.extrasTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    # allow name field to grow/shrink
    columns2Resize = [0, 1, 3, 4, 5, 6]
    hheader.setSectionResizeMode(2, QtWidgets.QHeaderView.ResizeMode.Stretch)
    self.extraSecondAxes, self.extraBothAxes = [], []

    for index in range(number_extrasEntry):
      vheader.setSectionResizeMode(index, QtWidgets.QHeaderView.ResizeMode.Fixed)

      visibilityExtra = QPushButtonCheckable()
      visibilityExtra.setPadMe(2)
      visibilityExtra.setText('')
      visibilityExtra.setToolTip('Set visibility of extra object ' + str(index + 1))
      visibilityExtra.setCheckMe(True)
      visibilityExtra.setChecked(self.parent.extras[index].visibility)
      visibilityExtra.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityExtra.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityExtra.clicked.connect(partial(self.toggleVisibilityExtras, index, True))
      self.extrasTable.setCellWidget(index, 0, visibilityExtra)

      spinselector = QSpinClick()
      spinselector.setToolTip('Set z order of extra object ' + str(index + 1))
      spinselector.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      spinselector.setMinimum(1)
      spinselector.setMaximum(self.parent.zcount)
      spinselector.setValue(self.parent.extras[index].zorder)
      spinselector.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.editingFinished.connect(partial(self.changeZOrder, group = 'extras', index = index))
      spinselector.setAdaptive(False)
      spinselector.setAccelerated(False)
      self.extrasTable.setCellWidget(index, 1, spinselector)

      entryField = QLineEditClick(str(self.parent.extras[index].labeltext))
      entryField.setToolTip('Name of extra object ' + str(index + 1))
      entryField.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      entryField.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      ###entryField.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      entryField.editingFinished.connect(partial(self.editNameExtra, index))
      entryField.setCursorPosition(0)
      self.extrasTable.setCellWidget(index, 2, entryField)
      
      extrasButton = QPushButtonMac()
      extrasButton.setText(' Conf')
      extrasButton.setToolTip('Configure extra object ' + str(index + 1))
      extrasButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      extrasButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      extrasButton.clicked.connect(partial(self.changeStyleExtra, index, extrasButton))
      extrasButton.setIcon(FOM_ICON_TOOL)
      self.extrasTable.setCellWidget(index, 3, extrasButton)

      extrasButton = QPushButtonMac()
      extrasButton.setText(' Copy')
      extrasButton.setToolTip('Copy extra object ' + str(index + 1))
      extrasButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      extrasButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      extrasButton.clicked.connect(partial(self.copyExtra, index))
      extrasButton.setIcon(FOM_ICON_COPY)
      self.extrasTable.setCellWidget(index, 4, extrasButton)
        
      extrasButton = QPushButtonMac()
      extrasButton.setText(' Del')
      extrasButton.setToolTip('Delete extra object ' + str(index + 1))
      extrasButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      extrasButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      extrasButton.clicked.connect(partial(self.deleteExtra, index, True))
      extrasButton.setIcon(FOM_ICON_DIALOG_DISCARD_BUTTON)
      self.extrasTable.setCellWidget(index, 5, extrasButton)
    
      extraAxesBox = QWidgetMac()
      extraAxesBox.setMinimumSize(QtCore.QSize(int(BASE_SIZE * 2 + 2), int(BASE_SIZE)))
      extraAxesBox.setMaximumSize(QtCore.QSize(int(BASE_SIZE * 2 + 2), int(BASE_SIZE)))
      Layout_extraAxesBox = QtWidgets.QHBoxLayout(extraAxesBox)
      Layout_extraAxesBox.setContentsMargins(0, 0, 0, 0)
      Layout_extraAxesBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      Layout_extraAxesBox.setSpacing(0)
      self.extrasTable.setCellWidget(index, 6, extraAxesBox)

      self.extraSecondAxes.append(QPushButtonCheckable())
      self.extraSecondAxes[-1].setPadMe(2)
      self.extraSecondAxes[-1].setText('2')
      self.extraSecondAxes[-1].setToolTip('Move extra object ' + str(index + 1) + ' to second axes')
      self.extraSecondAxes[-1].setChecked(self.parent.extras[index].onSecondAxes)
      self.extraSecondAxes[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      self.extraSecondAxes[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      self.extraSecondAxes[-1].clicked.connect(partial(self.toggleSecondAxesExtras, index))
      Layout_extraAxesBox.addWidget(self.extraSecondAxes[-1])

      if(self.parent.plotArea.splitY):
        self.extraBothAxes.append(QPushButtonCheckable())
        self.extraBothAxes[-1].setPadMe(2)
        self.extraBothAxes[-1].setText('D')
        self.extraBothAxes[-1].setToolTip('Display extra object ' + str(index + 1) + ' on both of the split axes')
        self.extraBothAxes[-1].setChecked(self.parent.extras[index].onBothAxes)
        self.extraBothAxes[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        self.extraBothAxes[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        self.extraBothAxes[-1].clicked.connect(partial(self.toggleBothAxesExtras, index))
        Layout_extraAxesBox.addWidget(self.extraBothAxes[-1])
      Layout_extraAxesBox.addStretch()
      
    # resize columns
    for index in columns2Resize:
      self.extrasTable.resizeColumnToContents(index)

  def sanityCheckZOrder(self):    
    # checks for inconsistencies in zorder (can happen upon restoration/deletion of entries)
    # refresh tables is probably necessary but should be done outside this function
    # data sets and curves
    zorders = {}
    for entry in self.parent.data:
      key = entry.zorder
      while(key in zorders):
        key += 0.001
      zorders[key] = entry
    for entry in self.parent.fit:
      key = entry.zorder
      while(key in zorders):
        key += 0.001
      zorders[key] = entry
    for entry in self.parent.extras:
      key = entry.zorder
      while(key in zorders):
        key += 0.001
      zorders[key] = entry
    
    # reassign zorder values
    keys = sorted(list(zorders.keys()))
    index = 1
    for entry in keys:
      zorders[entry].setZOrder(index, redraw=False)
      index += 1
      
    # residuals
    zordersResid = {}
    # populate with resid zero line
    zordersResid[self.parent.plotArea.zorderResidLine] = 'zero_line'
    for entry in self.parent.data:
      key = entry.zorderResid
      while(key in zordersResid):
        key += 0.001
      zordersResid[key] = entry
    
    # reassign zorderResid values
    keys = sorted(list(zordersResid.keys()))
    index = 1
    for entry in keys:
      if(zordersResid[entry] == 'zero_line'):
        self.parent.plotArea.setZOrderResidLine(index, redraw=False)
      else:
        zordersResid[entry].setZOrderResid(index, redraw=False)
      index += 1
      
  def refreshResidTable(self):
    # updates resid table
    number_residEntry = len(self.parent.data)
    self.residTable.setRowCount(number_residEntry + 1)
    self.residTable.setColumnCount(5)
    #self.residTable.horizontalHeader().hide()
    labels = ['vis', 'act', 'z', 'name', 'action']
    toolTips = ['This column controls the visibility of the residuals objects.',
                'This column shows which residuals object is currently active.',
                'This column controls the z order of the residuals objects.',
                'This column holds the name of the residuals objects.',
                'This column allows to configure the residuals objects.']
    indices = [i for i in range(len(labels))]
    for index, label, toolTip in zip(indices, labels, toolTips):
      item = QTableWidgetItemToolTip(label)
      item.setToolTip(toolTip)
      self.residTable.setHorizontalHeaderItem(index, item)
    # set row height and prevent from resizing
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.residTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    hheader = self.residTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    # allow name field to grow/shrink
    columns2Resize = [0, 1, 2, 4]
    hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch)
    # set up group box for active curve selection
    self.activeResidBox = QtWidgets.QGroupBox()

    for index in range(number_residEntry + 1):
      self.residTable.setVerticalHeaderItem(index, QtWidgets.QTableWidgetItem(str(index)))
      vheader.setSectionResizeMode(index, QtWidgets.QHeaderView.ResizeMode.Fixed)

      visibilityResid = QPushButtonCheckable()
      visibilityResid.setPadMe(2)
      visibilityResid.setText('')
      visibilityResid.setCheckMe(True)
      if(index):
        visibilityResid.setChecked(self.parent.data[index-1].visibilityResid)
        visibilityResid.setToolTip('Set visibility of residuals object ' + str(index))
      else:
        visibilityResid.setChecked(self.parent.plotArea.visibilityResidLine)
        visibilityResid.setToolTip('Set visibility of zero line')
      visibilityResid.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityResid.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityResid.clicked.connect(partial(self.toggleVisibilityResid, index, True))
      self.residTable.setCellWidget(index, 0, visibilityResid)

      if(index):
        radiobutton = QRadioButtonToolTip(self.activeResidBox)
        radiobutton.setToolTip('Is residuals object ' + str(index) + ' active?')
        radiobutton.setObjectName('listos')
        radiobutton.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        radiobutton.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        radiobutton.setChecked(index - 1 == self.parent.activeData)
        radiobutton.setText('')
        radiobutton.setEnabled(False)
        self.residTable.setCellWidget(index, 1, radiobutton)

      spinselector = QSpinClick()
      spinselector.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      spinselector.setMinimum(1)
      spinselector.setMaximum(len(self.parent.data) + 1)
      if(index):
        spinselector.setValue(self.parent.data[index-1].zorderResid)
        spinselector.setToolTip('Set z order of residuals object ' + str(index))
      else:
        spinselector.setValue(self.parent.plotArea.zorderResidLine)
        spinselector.setToolTip('Set z order of zero line')
      spinselector.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setAdaptive(False)
      spinselector.setAccelerated(False)
      spinselector.editingFinished.connect(partial(self.changeZOrderResid, index = index))
      self.residTable.setCellWidget(index, 2, spinselector)

      if(index):
        entryField = QLineEditClick(str(self.parent.data[index-1].nameResid))
        entryField.editingFinished.connect(partial(self.editNameResid, self.parent.data[index-1], index))
        entryField.setToolTip('Name of residuals object ' + str(index))
      else:
        entryField = QLineEditClick('zero line')
        entryField.setToolTip('Name of zero line')
      entryField.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      entryField.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      ###entryField.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      entryField.setCursorPosition(0)
      self.residTable.setCellWidget(index, 3, entryField)
      
      residButton = QPushButtonMac()
      residButton.setText(' Conf')
      if(index):
        residButton.setToolTip('Configure residuals object ' + str(index))
      else:
        residButton.setToolTip('Configure zero line')
      residButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      residButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      if(index):
        residButton.clicked.connect(partial(self.changeStyle, self.parent.data[index-1], False, True, residButton))
      else:
        residButton.clicked.connect(partial(self.changeResidZeroStyle, residButton))
      residButton.setIcon(FOM_ICON_TOOL)
      self.residTable.setCellWidget(index, 4, residButton)
        
    # resize columns
    for index in columns2Resize:
      self.residTable.resizeColumnToContents(index)

  def refreshCurvesTable(self):
    # updates curves table
    number_curveEntry = len(self.parent.fit)
    self.curvesTable.setRowCount(number_curveEntry)
    self.curvesTable.setColumnCount(8)
    labels = ['vis', 'act', 'z', 'name', '', 'action', '', 'axes']
    toolTips = ['This column controls the visibility of the curve objects.',
                'This column controls which curve object is active.',
                'This column controls the z order of the curve objects.',
                'This column holds the name of the curve objects.',
                'This column allows to configure the curve objects.',
                'This column allows to copy the curve objects.',
                'This column allows to delete the curve objects.',
                'This column controls on which axis the curve objects are displayed.']
    indices = [i for i in range(len(labels))]
    for index, label, toolTip in zip(indices, labels, toolTips):
      item = QTableWidgetItemToolTip(label)
      item.setToolTip(toolTip)
      self.curvesTable.setHorizontalHeaderItem(index, item)
    # set row height and prevent from resizing
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.curvesTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    hheader = self.curvesTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    # allow name field to grow/shrink
    columns2Resize = [0, 1, 2, 4, 5, 6, 7]
    hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch)
    self.curveSecondAxes, self.curveBothAxes = [], []
    # set up group box for active curve selection
    self.activeCurveBox = QtWidgets.QGroupBox()

    for index in range(number_curveEntry):
      vheader.setSectionResizeMode(index, QtWidgets.QHeaderView.ResizeMode.Fixed)

      visibilityCurve = QPushButtonCheckable()
      visibilityCurve.setPadMe(2)
      visibilityCurve.setText('')
      visibilityCurve.setToolTip('Set visibility of curve object ' + str(index + 1))
      visibilityCurve.setCheckMe(True)
      visibilityCurve.setChecked(self.parent.fit[index].visibility)
      visibilityCurve.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityCurve.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityCurve.clicked.connect(partial(self.toggleVisibilityCurve, index, True))
      self.curvesTable.setCellWidget(index, 0, visibilityCurve)
      
      radiobutton = QRadioButtonToolTip(self.activeCurveBox)
      radiobutton.setToolTip('Set curve object ' + str(index + 1) + ' active')
      radiobutton.setObjectName('listos')
      radiobutton.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      radiobutton.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      radiobutton.setChecked(index == self.parent.activeFit)
      radiobutton.toggled.connect(partial(self.changeActiveCurve, index, redraw=True, clicked=True))
      radiobutton.setText('')
      self.curvesTable.setCellWidget(index, 1, radiobutton)

      spinselector = QSpinClick()
      spinselector.setToolTip('Set z order of curve object ' + str(index + 1))
      spinselector.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      spinselector.setMinimum(1)
      spinselector.setMaximum(self.parent.zcount)
      spinselector.setValue(self.parent.fit[index].zorder)
      spinselector.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setAdaptive(False)
      spinselector.setAccelerated(False)
      spinselector.editingFinished.connect(partial(self.changeZOrder, group = 'curve', index = index))
      self.curvesTable.setCellWidget(index, 2, spinselector)

      entryField = QLineEditClick(str(self.parent.fit[index].name))
      entryField.setToolTip('Name of curve object ' + str(index + 1))
      entryField.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      entryField.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      ###entryField.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      entryField.editingFinished.connect(partial(self.editNameCurve, self.parent.fit[index], index))
      entryField.setCursorPosition(0)
      self.curvesTable.setCellWidget(index, 3, entryField)
      
      curveButton = QPushButtonMac()
      curveButton.setText(' Conf')
      curveButton.setToolTip('Configure curve object ' + str(index + 1))
      curveButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      curveButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      curveButton.clicked.connect(partial(self.changeStyle, self.parent.fit[index], False, False, curveButton))
      curveButton.setIcon(FOM_ICON_TOOL)
      self.curvesTable.setCellWidget(index, 4, curveButton)

      curveButton = QPushButtonMac()
      curveButton.setText(' Copy')
      curveButton.setToolTip('Copy curve object ' + str(index + 1))
      curveButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      curveButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      curveButton.clicked.connect(partial(self.copyFit, index))
      curveButton.setIcon(FOM_ICON_COPY)
      self.curvesTable.setCellWidget(index, 5, curveButton)
        
      curveButton = QPushButtonMac()
      curveButton.setText(' Del')
      curveButton.setToolTip('Delete curve object ' + str(index + 1))
      curveButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      curveButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      curveButton.clicked.connect(partial(self.deleteCurve, index, True))
      curveButton.setIcon(FOM_ICON_DIALOG_DISCARD_BUTTON)
      self.curvesTable.setCellWidget(index, 6, curveButton)
    
      curveAxesBox = QWidgetMac()
      curveAxesBox.setMinimumSize(QtCore.QSize(int(BASE_SIZE * 2 + 2), int(BASE_SIZE)))
      curveAxesBox.setMaximumSize(QtCore.QSize(int(BASE_SIZE * 2 + 2), int(BASE_SIZE)))
      Layout_curveAxesBox = QtWidgets.QHBoxLayout(curveAxesBox)
      Layout_curveAxesBox.setContentsMargins(0, 0, 0, 0)
      Layout_curveAxesBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      Layout_curveAxesBox.setSpacing(0)
      self.curvesTable.setCellWidget(index, 7, curveAxesBox)

      self.curveSecondAxes.append(QPushButtonCheckable())
      self.curveSecondAxes[-1].setPadMe(2)
      self.curveSecondAxes[-1].setText('2')
      self.curveSecondAxes[-1].setToolTip('Move curve object ' + str(index + 1) + ' to second axes')
      self.curveSecondAxes[-1].setChecked(self.parent.fit[index].onSecondAxes)
      self.curveSecondAxes[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      self.curveSecondAxes[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      self.curveSecondAxes[-1].clicked.connect(partial(self.toggleSecondAxesCurve, index))
      Layout_curveAxesBox.addWidget(self.curveSecondAxes[-1])

      if(self.parent.plotArea.splitY):
        self.curveBothAxes.append(QPushButtonCheckable())
        self.curveBothAxes[-1].setPadMe(2)
        self.curveBothAxes[-1].setText('D')
        self.curveBothAxes[-1].setToolTip('Display curve object ' + str(index + 1) + ' on both of the split axes')
        self.curveBothAxes[-1].setChecked(self.parent.fit[index].onBothAxes)
        self.curveBothAxes[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        self.curveBothAxes[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        self.curveBothAxes[-1].clicked.connect(partial(self.toggleBothAxesCurve, index))
        Layout_curveAxesBox.addWidget(self.curveBothAxes[-1])
      Layout_curveAxesBox.addStretch()
      
    # resize columns
    for index in columns2Resize:
      self.curvesTable.resizeColumnToContents(index)

  def refreshDataTable(self):
    # updates data table
    number_dataEntry = len(self.parent.data)
    self.dataSetTable.setRowCount(number_dataEntry)
    self.dataSetTable.setColumnCount(8)
    labels = ['vis', 'act', 'z', 'name', '', 'action', '', 'axes']
    toolTips = ['This column controls the visibility of the data objects.',
                'This column controls which data object is active.',
                'This column controls the z order of the data objects.',
                'This column holds the name of the data objects.',
                'This column allows to configure the data objects.',
                'This column allows to copy the data objects.',
                'This column allows to delete the data objects.',
                'This column controls on which axis the data objects are displayed.']
    indices = [i for i in range(len(labels))]
    for index, label, toolTip in zip(indices, labels, toolTips):
      item = QTableWidgetItemToolTip(label)
      item.setToolTip(toolTip)
      self.dataSetTable.setHorizontalHeaderItem(index, item)
    # set row height and prevent from resizing
    self.rowHeight = int(BASE_SIZE + 2)
    vheader = self.dataSetTable.verticalHeader()
    vheader.setDefaultSectionSize(self.rowHeight)
    hheader = self.dataSetTable.horizontalHeader()
    hheader.setSectionResizeMode(QtWidgets.QHeaderView.ResizeMode.Fixed)
    # allow name field to grow/shrink
    columns2Resize = [0, 1, 2, 4, 5, 6, 7]
    hheader.setSectionResizeMode(3, QtWidgets.QHeaderView.ResizeMode.Stretch)
    self.dataSecondAxes, self.dataBothAxes = [], []
    # set up group box for active curve selection
    self.activeDataSetBox = QtWidgets.QGroupBox()

    for index in range(number_dataEntry):
      vheader.setSectionResizeMode(index, QtWidgets.QHeaderView.ResizeMode.Fixed)

      visibilityData = QPushButtonCheckable()
      visibilityData.setPadMe(2)
      visibilityData.setText('')
      visibilityData.setToolTip('Set visibility of data object ' + str(index + 1))
      visibilityData.setCheckMe(True)
      visibilityData.setChecked(self.parent.data[index].visibility)
      visibilityData.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityData.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      visibilityData.clicked.connect(partial(self.toggleVisibilityData, index, True))
      self.dataSetTable.setCellWidget(index, 0, visibilityData)

      radiobutton = QRadioButtonToolTip(self.activeDataSetBox)
      radiobutton.setToolTip('Set data object ' + str(index + 1) + ' active')
      radiobutton.setObjectName('listos')
      radiobutton.setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      radiobutton.setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      radiobutton.setChecked(index == self.parent.activeData)
      radiobutton.toggled.connect(partial(self.changeActiveDataSet, index, redraw=True, clicked=True))
      radiobutton.setText('')
      self.dataSetTable.setCellWidget(index, 1, radiobutton)

      spinselector = QSpinClick()
      spinselector.setToolTip('Set z order of data object ' + str(index + 1))
      spinselector.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      spinselector.setMinimum(1)
      spinselector.setMaximum(self.parent.zcount)
      spinselector.setValue(self.parent.data[index].zorder)
      spinselector.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      spinselector.setAdaptive(False)
      spinselector.setAccelerated(False)
      spinselector.editingFinished.connect(partial(self.changeZOrder, group = 'data', index = index))
      self.dataSetTable.setCellWidget(index, 2, spinselector)

      entryField = QLineEditClick(str(self.parent.data[index].name))
      entryField.setToolTip('Name of data object ' + str(index + 1))
      entryField.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      entryField.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      ###entryField.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      entryField.editingFinished.connect(partial(self.editNameData, self.parent.data[index], index))
      entryField.setCursorPosition(0)
      self.dataSetTable.setCellWidget(index, 3, entryField)

      dataButton = QPushButtonMac()
      dataButton.setText(' Conf')
      dataButton.setToolTip('Configure data object ' + str(index + 1))
      dataButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      dataButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      dataButton.clicked.connect(partial(self.changeStyle, self.parent.data[index], True, False, dataButton))
      dataButton.setIcon(FOM_ICON_TOOL)
      self.dataSetTable.setCellWidget(index, 4, dataButton)

      dataButton = QPushButtonMac()
      dataButton.setText(' Copy')
      dataButton.setToolTip('Copy data object ' + str(index + 1))
      dataButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      dataButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      dataButton.clicked.connect(partial(self.copyData, index))
      dataButton.setIcon(FOM_ICON_COPY)
      self.dataSetTable.setCellWidget(index, 5, dataButton)
        
      dataButton = QPushButtonMac()
      dataButton.setText(' Del')
      dataButton.setToolTip('Delete data object ' + str(index + 1))
      dataButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      dataButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      dataButton.clicked.connect(partial(self.deleteDataSet, index, True))
      dataButton.setIcon(FOM_ICON_DIALOG_DISCARD_BUTTON)
      self.dataSetTable.setCellWidget(index, 6, dataButton)

      dataAxesBox = QWidgetMac()
      dataAxesBox.setMinimumSize(QtCore.QSize(int(BASE_SIZE * 2 + 2), int(BASE_SIZE)))
      dataAxesBox.setMaximumSize(QtCore.QSize(int(BASE_SIZE * 2 + 2), int(BASE_SIZE)))
      Layout_dataAxesBox = QtWidgets.QHBoxLayout(dataAxesBox)
      Layout_dataAxesBox.setContentsMargins(0, 0, 0, 0)
      Layout_dataAxesBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      Layout_dataAxesBox.setSpacing(0)
      self.dataSetTable.setCellWidget(index, 7, dataAxesBox)

      self.dataSecondAxes.append(QPushButtonCheckable())
      self.dataSecondAxes[-1].setPadMe(2)
      self.dataSecondAxes[-1].setText('2')
      self.dataSecondAxes[-1].setToolTip('Move data object ' + str(index + 1) + ' to second axes')
      self.dataSecondAxes[-1].setChecked(self.parent.data[index].onSecondAxes)
      self.dataSecondAxes[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      self.dataSecondAxes[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
      self.dataSecondAxes[-1].clicked.connect(partial(self.toggleSecondAxesData, index))
      Layout_dataAxesBox.addWidget(self.dataSecondAxes[-1])

      if(self.parent.plotArea.splitY):
        self.dataBothAxes.append(QPushButtonCheckable())
        self.dataBothAxes[-1].setPadMe(2)
        self.dataBothAxes[-1].setText('D')
        self.dataBothAxes[-1].setToolTip('Display data object ' + str(index + 1) + ' on both of the split axes')
        self.dataBothAxes[-1].setChecked(self.parent.data[index].onBothAxes)
        self.dataBothAxes[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        self.dataBothAxes[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE), int(BASE_SIZE)))
        self.dataBothAxes[-1].clicked.connect(partial(self.toggleBothAxesData, index))
        Layout_dataAxesBox.addWidget(self.dataBothAxes[-1])
      Layout_dataAxesBox.addStretch()
      
    # resize columns
    for index in columns2Resize:
      self.dataSetTable.resizeColumnToContents(index)

  def deleteExtra(self, index, redraw=True):
    # deletes extra
    killObject = self.parent.extras[index]
    
    # check whether displayed on both axes
    if(killObject.onBothAxes):
      killObject.removeFromSplit()
      
    # delete from plot
    if(killObject.handle != None):
      killObject.handle.remove()
    if(killObject.handle_div != None):
      killObject.handle_div.remove()

    # delete from self.parent.fit
    nuList = [self.parent.extras[i] for i in range(len(self.parent.extras)) if (i != index)]
    self.parent.extras = nuList

    # destroy object
    del killObject
    
    # adjust zorders etc.
    self.parent.zcount -= 1
    self.sanityCheckZOrder()
    self.refreshDataTable()
    self.refreshCurvesTable()
    self.refreshExtrasTable()
    self.parent.plotArea.updateToggloContainer()
    self.checkSecondAxes()

    # update plot?
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def deleteCurve(self, index, redraw=True):
    # deletes curve
    if(len(self.parent.fit) == 1):
      self.parent.statusbar.showMessage('Cannot delete last and only curve!', self.parent.STATUS_TIME)
    else:
      killObject = self.parent.fit[index]
      
      # check whether displayed on both axes
      if(killObject.onBothAxes):
        killObject.removeFromSplit()
      
      # delete from plot
      if((hasattr(killObject, 'handlePlot')) and (killObject.handlePlot != None)):
        killObject.handlePlot.remove()
      if((hasattr(killObject, 'handlePlot_div')) and (killObject.handlePlot_div != None)):
        killObject.handlePlot_div.remove()
      if((hasattr(killObject, 'handleBoot')) and (killObject.handleBoot != None)):
        killObject.handleBoot.remove()
      if((hasattr(killObject, 'handleBoot_div')) and (killObject.handleBoot_div != None)):
        killObject.handleBoot_div.remove()

      # delete from self.parent.fit
      nuList = [self.parent.fit[i] for i in range(len(self.parent.fit)) if (i != index)]
      self.parent.fit = nuList

      if(index == self.parent.activeFit):
        # our active fit was deleted, too bad
        if(self.parent.activeFit):
          self.parent.activeFit -= 1
        # activate new function
        self.parent.fit[self.parent.activeFit].retired = False
        # change fit formula
        parameters, formula, values, active, fitresults, confidence, confidence_apriori = self.parent.fit[self.parent.activeFit].retrieveInfo()
        self.parent.fitarea.restoreFfunc(parameters, formula, values, active, fitresults, confidence, confidence_apriori, redraw=False)
      elif((index < self.parent.activeFit) and (self.parent.activeFit > 0)):
        # need to shift number of activeFit
        self.parent.activeFit -= 1
      
      # destroy object
      del killObject
      
      # adjust zorders etc.
      self.parent.zcount -= 1
      self.sanityCheckZOrder()
      self.refreshDataTable()
      self.refreshCurvesTable()
      self.refreshExtrasTable()
      self.parent.plotArea.updateToggloContainer()
      self.parent.globalarea.updateDataSetTable()
      self.checkSecondAxes()

      # update legend
      self.updateLegend(redraw=redraw)

  def deleteDataSet(self, index, redraw=True):
    # deletes data set
    if(len(self.parent.data) == 1):
      self.parent.statusbar.showMessage('Cannot delete last and only data set!', self.parent.STATUS_TIME)
    else:
      killObject = self.parent.data[index]
      
      # check whether displayed on both axes
      if(killObject.onBothAxes):
        killObject.removeFromSplit()
      
      # delete from plot
      items = 'handleData,handleErr,handleErrShady,handleBar,handleStack,handleStackNeg,handleText,handleViolin,handleScatter,handleResid,handleResidBar,handleResidStack,handleResidStackNeg,handleResidScatter'.split(',')
      items.extend('handleData_div,handleErr_div,handleErrShady_div,handleBar_div,handleStack_div,handleStackNeg_div,handleText_div,handleViolin_div,handleScatter_div,handleResid_div,handleResidBar_div,handleResidStack_div,handleResidStackNeg_div,handleResidScatter,_div'.split(','))
      for entry in items:
        # need to catch this in case previous data import caused problems
        try:
          if((hasattr(killObject, entry)) and (killObject.__dict__[entry] != None)):
            if(entry in ['handleText', 'handleText_div']):
              for item in killObject.__dict__[entry]:
                item.remove()
            elif(entry in ['handleViolin', 'handleViolin_div']):
              for entry2 in killObject.__dict__[entry]:
                if(type(killObject.__dict__[entry][entry2]) == type([])):
                  for item in killObject.__dict__[entry][entry2]:
                    item.remove()
                else:
                  killObject.__dict__[entry][entry2].remove()
                
            elif(not (entry in ['handleErr', 'handleErr_div'])):
              killObject.__dict__[entry].remove()
            else:
              killObject.__dict__[entry][0].remove()
              for entry2 in killObject.__dict__[entry][1]:
                entry2.remove()
              for entry2 in killObject.__dict__[entry][2]:
                entry2.remove()
        except:
          pass

      # delete from self.parent.data
      nuList = [self.parent.data[i] for i in range(len(self.parent.data)) if (i != index)]
      self.parent.data = nuList

      if(index == self.parent.activeData):
        # our active data set was deleted, too bad
        if(self.parent.activeData):
          self.parent.activeData -= 1
        # if selected dataset exists, clear it
        if(self.parent.selectedData.isLive):
          self.parent.selectedData.clearMe(redraw=False)
      elif((index < self.parent.activeData) and (self.parent.activeData > 0)):
        # need to shift number of activeFit
        self.parent.activeData -= 1

      # update results table
      self.parent.resultsarea.setDataSet(currIndex=self.parent.activeData, maxIndex=len(self.parent.data) - 1)
      
      # destroy object
      del killObject
      
      # adjust zorders etc.
      self.parent.zcount -= 1
      self.sanityCheckZOrder()
      self.refreshDataTable()
      self.refreshCurvesTable()
      self.refreshResidTable()
      self.refreshExtrasTable()
      self.parent.plotArea.updateToggloContainer()
      self.parent.globalarea.updateDataSetTable()
      self.checkSecondAxes()

      # update legend
      self.updateLegend(redraw=redraw)
      if(redraw):
        self.parent.plotArea.residplotwidget.myRefresh()

  def changeActiveCurve(self, index=0, redraw=True, clicked=False):
    # changes active curve 
    if(len(self.parent.fit) - 1):
      prevActive = self.parent.activeFit
      if(prevActive != index):
        # update active fit function
        self.parent.activeFit = index
        # stuff to do
        # 1. retire previous function
        self.parent.fit[prevActive].retired = True
        #self.parent.fit[prevActive].retireMe()
        # 2. activate new function
        self.parent.fit[self.parent.activeFit].retired = False
        # change fit formula
        parameters, formula, values, active, fitresults, confidence, confidence_apriori = self.parent.fit[self.parent.activeFit].retrieveInfo()
        self.parent.fitarea.restoreFfunc(parameters, formula, values, active, fitresults, confidence, confidence_apriori, redraw=redraw)
        # change fit parameter table
        # change last fit results
        # only update when clicked upon (to prevent premature firing of togglo buttons in sidebar)
        if(clicked):
          self.parent.plotArea.updateToggloActive(activeFit=self.parent.activeFit)
    else:
      # have to set button to checked to prevent turning off
      widget = self.curvesTable.cellWidget(0, 1)
      widget.blockSignals(True)
      widget.setChecked(True)
      widget.blockSignals(False)
  
  def changeActiveDataSet(self, index=0, setCheck=True, redraw=True, clicked=False):
    # changes active data set
    if(len(self.parent.data) - 1):
      prevActive = self.parent.activeData
      if(prevActive != index):
        # if selected dataset exists, clear it
        if(self.parent.selectedData.isLive):
          self.parent.selectedData.clearMe(redraw=redraw)
        # update active data set and residuals
        self.parent.activeData = index
        if(setCheck):
          self.residTable.cellWidget(index + 1, 1).setChecked(True)
        # update results table
        self.parent.resultsarea.setDataSet(currIndex=self.parent.activeData, maxIndex=len(self.parent.data) - 1)
        # only do the update when clicked upon
        if(clicked):
          self.parent.plotArea.updateToggloActive(activeData=self.parent.activeData)
    else:
      # have to set button to checked to prevent turning off
      widget = self.dataSetTable.cellWidget(0, 1)
      widget.blockSignals(True)
      widget.setChecked(True)
      widget.blockSignals(False)
  
  def changeZOrderResid(self, index=0):
    # updates z-order of plot items
    source_index = index
    new_zorder = self.residTable.cellWidget(index, 2).value()
    if(source_index):
      sourceItem = self.parent.data[source_index-1]
      orig_zorder = sourceItem.zorderResid
    else:
      sourceItem = self.parent.plotArea
      orig_zorder = sourceItem.zorderResidLine

    # hunt for the item that needs to be swapped in z-order
    flag = False
    # parse data sets
    for index in range(len(self.parent.data) + 1):
      if(index == 0):
        if(self.parent.plotArea.zorderResidLine == new_zorder):
          target_index = index
          flag = True
      elif(self.parent.data[index-1].zorderResid == new_zorder):
        # we found the guy
        target_index = index
        flag = True

    # now swap the z-order values
    if (flag):
      dest_qspin = self.residTable.cellWidget(target_index, 2)
      # have to temporarily disable event logging
      dest_qspin.blockSignals(True)
      dest_qspin.setValue(orig_zorder)
      dest_qspin.blockSignals(False)
      # update z-order in source object
      if(source_index):
        sourceItem.setZOrderResid(new_zorder, redraw=False)
      else:
        sourceItem.setZOrderResidLine(new_zorder, redraw=False)
      # update z-order in destination object
      if(target_index):
        self.parent.data[target_index-1].setZOrderResid(orig_zorder, redraw=False)
      else:
        self.parent.plotArea.setZOrderResidLine(orig_zorder, redraw=False)

      # trigger redrawing of objects
      self.parent.plotArea.residplotwidget.myRefresh()

  def changeZOrder(self, group='data', index=0):
    # updates z-order of plot items
    if(group in ['data', 'curve', 'extras']):
      # determine field that triggered event
      sourceItem = None
      if (group == 'data'):
        # crashes sporadically when loading state files -- likely related to order of events
        try:
          sourceItem = self.parent.data[index]
          new_zorder = self.dataSetTable.cellWidget(index, 2).value()
        except:
          pass
      elif(group == 'curve'):
        # crashes sometimes with 'index out of range'
        # likely related to behavior of QSpinClick()
        # can possibly remove this try/except
        try:
          sourceItem = self.parent.fit[index]
          new_zorder = self.curvesTable.cellWidget(index, 2).value()
        except:
          pass
      else:
        # crashes sporadically when loading state files -- likely related to order of events
        try:
          sourceItem = self.parent.extras[index]
          new_zorder = self.extrasTable.cellWidget(index, 1).value()
        except:
          pass
        
      # terminate execution here -- will cause wrong z values but better than triggering critical failure
      if(sourceItem == None):
        # could issue a warning to the user here
        pass
      else:
        orig_zorder = sourceItem.zorder
        
        # hunt for the item that needs to be swapped in z-order
        flag = False
        # parse data sets
        for index in range(len(self.parent.data)):
          if(self.parent.data[index].zorder == new_zorder):
            # we found the guy
            flag = True
            dest_qspin = self.dataSetTable.cellWidget(index, 2)
            # have to temporarily disable event logging
            dest_qspin.blockSignals(True)
            dest_qspin.setValue(orig_zorder)
            dest_qspin.blockSignals(False)
            destItem = self.parent.data[index]
            index = len(self.parent.data) + 1
        # parse curves
        if (not flag):
          for index in range(len(self.parent.fit)):
            if(self.parent.fit[index].zorder == new_zorder):
              # we found the guy
              flag = True
              dest_qspin = self.curvesTable.cellWidget(index, 2)
              # have to temporarily disable event logging
              dest_qspin.blockSignals(True)
              dest_qspin.setValue(orig_zorder)
              dest_qspin.blockSignals(False)
              destItem = self.parent.fit[index]
              index = len(self.parent.fit) + 1
        # parse extras
        if (not flag):
          for index in range(len(self.parent.extras)):
            if(self.parent.extras[index].zorder == new_zorder):
              # we found the guy
              flag = True
              dest_qspin = self.extrasTable.cellWidget(index, 1)
              # have to temporarily disable event logging
              dest_qspin.blockSignals(True)
              dest_qspin.setValue(orig_zorder)
              dest_qspin.blockSignals(False)
              destItem = self.parent.extras[index]
              index = len(self.parent.extras) + 1
              
        # now swap the z-order values
        if (flag):
          # set z-order in objects to new values
          sourceItem.setZOrder(new_zorder, redraw=False)
          destItem.setZOrder(orig_zorder, redraw=False)
          
          # update legend if needed
          self.updateLegend(redraw=True)
        
  def checkSecondAxes(self):
    # checks whether at least one item slaved to the second axes
    state = False or self.parent.plotArea.splitY
    for entry in self.parent.data:
      state = state or entry.onSecondAxes
    for entry in self.parent.fit:
      state = state or entry.onSecondAxes
    for entry in self.parent.extras:
      state = state or entry.onSecondAxes
    # control display of axes controls
    self.parent.plotArea.toggleSecondAxes(state)
        
  def toggleVisibilityExtras(self, index=0, redraw=True):
    visibilityExtra = self.extrasTable.cellWidget(index, 0)
    if (visibilityExtra.isChecked()):
      self.parent.extras[index].setVisibility(True, redraw=False)
      # need to check for bad fonts here
      if(self.parent.extras[index].extrasType in ['text', 'annotation']):
        ###safeFont = 'DejaVu Sans'
        _, _, descent = self.parent.extras[index].handle._get_layout(self.parent.plotArea.matplot.canvas.renderer)
        if(not (descent > 0)):
          self.parent.statusbar.showMessage('Experiencing problems setting font ' + self.parent.extras[index].fontname + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
          self.parent.extras[index].fontname = SAFE_FONT
          self.parent.extras[index].handle.set_fontname(SAFE_FONT)
    else:
      self.parent.extras[index].setVisibility(False, redraw=False)
    # update toggle buttons in side bar
    self.parent.plotArea.checkToggloItem(kind='extra', index=index, state=visibilityExtra.isChecked())
    # update plot
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()

  def toggleBothAxesExtras(self, index=0):
    extrasBothAxes = self.extraBothAxes[index]
    state = extrasBothAxes.isChecked()
    self.parent.extras[index].setBothAxes(state, redraw=False)
    # update legend if needed
    self.parent.plotArea.dataplotwidget.myRefresh()
    self.checkSecondAxes()

  def toggleSecondAxesExtras(self, index=0):
    extrasSecondAxes = self.extraSecondAxes[index]
    if (extrasSecondAxes.isChecked()):
      self.parent.extras[index].setSecondAxes(True, redraw=False)
      # need to check for bad fonts here
      if(self.parent.extras[index].extrasType in ['text', 'annotation']):
        ###safeFont = 'DejaVu Sans'
        _, _, descent = self.parent.extras[index].handle_div._get_layout(self.parent.plotArea.matplot.canvas.renderer)
        if(not (descent > 0)):
          self.parent.statusbar.showMessage('Experiencing problems setting font ' + self.parent.extras[index].fontname + ' -- reverting to ' + SAFE_FONT, self.parent.STATUS_TIME)
          self.parent.extras[index].fontname = SAFE_FONT
          self.parent.extras[index].handle_div.set_fontname(SAFE_FONT)
    else:
      self.parent.extras[index].setSecondAxes(False, redraw=False)
    # update plot
    self.parent.plotArea.dataplotwidget.myRefresh()
    self.checkSecondAxes()

  def toggleBothAxesCurve(self, index=0):
    state = self.curveBothAxes[index].isChecked()
    self.parent.fit[index].setBothAxes(state, redraw=False)
    # update legend if needed
    self.updateLegend(redraw=True)
    self.checkSecondAxes()

  def toggleSecondAxesCurve(self, index=0):
    curveSecondAxes = self.curveSecondAxes[index]
    state = curveSecondAxes.isChecked()
    self.parent.fit[index].setSecondAxes(state, redraw=False)
    # update legend if needed
    self.updateLegend(redraw=True)
    self.checkSecondAxes()

  def toggleBothAxesData(self, index=0):
    dataBothAxes = self.dataBothAxes[index]
    state = dataBothAxes.isChecked()
    self.parent.data[index].setBothAxes(state, redraw=False)
    # update legend if needed
    self.updateLegend(redraw=True)
    self.checkSecondAxes()

  def toggleSecondAxesData(self, index=0):
    dataSecondAxes = self.dataSecondAxes[index]
    state = dataSecondAxes.isChecked()
    self.parent.data[index].setSecondAxes(state, redraw=False)
    # update legend if needed
    self.updateLegend(redraw=True)
    self.checkSecondAxes()

  def toggleVisibilityCurve(self, index=0, redraw=True):
    visibilityCurve = self.curvesTable.cellWidget(index, 0)
    state = visibilityCurve.isChecked()
    self.parent.fit[index].setVisibility(state, redraw=False)
    # update toggle buttons in side bar
    self.parent.plotArea.checkToggloItem(kind='fit', index=index, state=state)
    # update legend if needed
    self.updateLegend(redraw=redraw)

  def toggleVisibilityData(self, index=0, redraw=True):
    visibilityData = self.dataSetTable.cellWidget(index, 0)
    state = visibilityData.isChecked()
    self.parent.data[index].setVisibility(state, redraw=False)
    # update toggle buttons in side bar
    self.parent.plotArea.checkToggloItem(kind='data', index=index, state=state)
    # update legend if needed
    self.updateLegend(redraw=redraw)

  def toggleVisibilityResid(self, index=0, redraw=True):
    visibilityResid = self.residTable.cellWidget(index, 0)
    if(index):
      method = self.parent.data[index - 1].setVisibilityResid
    else:
      method = self.parent.plotArea.setVisibilityResidLine
    if (visibilityResid.isChecked()):
      method(True, redraw=redraw)
    else:
      method(False, redraw)
    # update toggle buttons in side bar
    self.parent.plotArea.checkToggloItem(kind='resid', index=index, state=visibilityResid.isChecked())

  def editNameExtra(self, targetIndex=None):
    if (targetIndex != None):
      entryField = self.extrasTable.cellWidget(targetIndex, 2)
      nuName = str(entryField.text())
      prevName = self.parent.extras[targetIndex].labeltext
      valueDict = {'labeltext': nuName}
      # update label if needed
      if(prevName != nuName):
        self.parent.extras[targetIndex].setValues(valueDict, redraw=True)
 
  def editNameCurve(self, target=None, index=0):
    if (target != None):
      entryField = self.curvesTable.cellWidget(index, 3)
      nuName = str(entryField.text())
      prevName = target.name
      target.setName(nuName)
      # update legend and global tab if needed
      if(prevName != nuName):
        self.updateLegend(redraw=True)
        self.parent.globalarea.updateDataSetTable()
 
  def editNameData(self, target=None, index=0):
    if (target != None):
      entryField = self.dataSetTable.cellWidget(index, 3)
      nuName = str(entryField.text())
      prevName = target.name
      target.setName(nuName)
      # update legend and global tab if needed
      if(prevName != nuName):
        self.updateLegend(redraw=True)
        self.parent.globalarea.updateDataSetTable()
 
  def editNameResid(self, target=None, index=0):
    if (target != None):
      entryField = self.residTable.cellWidget(index, 3)
      nuName = str(entryField.text())
      target.setNameResid(nuName)
 
  def changeResidZeroStyle(self, callButton=None):
    # display menu at current mouse pointer
    if((callButton == None) or hasattr(callButton, 'alignMeRight')):
      # dissociated menu => display name of item
      displayName = True
    else:
      displayName = False
    self.menu = ConfigMenu(self, target=self.parent.plotArea, residMode=False, residZero=True, displayName=displayName)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      if(hasattr(callButton, 'alignMeRight')):
        menuX += callButton.geometry().width() - self.menu.width()
      else:
        menuX += callButton.geometry().width() / 2
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()
      menuX -= self.menu.width() / 2

    menuY -= self.menu.height()
    menuX, menuY = max(menuX, 0), max(menuY, 0)
    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)

  def changeStyleMyMaster(self, callButton=None, kind='data'):
    # display menu at current mouse pointer
    self.menu = ConfigMenuMyMaster(parent=self, kind=kind)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())

    # adjust window position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      if(hasattr(callButton, 'alignMeRight')):
        menuX += callButton.geometry().width() - self.menu.width()
      else:
        menuX += callButton.geometry().width() / 2
      menuY += callButton.geometry().height()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()
      menuX -= self.menu.width() / 2

    menuX, menuY = max(menuX, 0), max(menuY, 0)
    self.menu.move(QtCore.QPoint(int(menuX), int(menuY)))

  def changeStyle(self, target=None, errorbar=False, residMode=False, callButton=None):
    # display menu at current mouse pointer
    if((callButton == None) or hasattr(callButton, 'alignMeRight')):
      # dissociated menu => display name of item
      displayName = True
    else:
      displayName = False
    self.menu = ConfigMenu(self, target, residMode, displayName=displayName)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())

    # adjust window position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      if(hasattr(callButton, 'alignMeRight')):
        menuX += callButton.geometry().width() - self.menu.width()
      else:
        menuX += callButton.geometry().width() / 2
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()
      menuX -= self.menu.width() / 2

    if(residMode):
      # bottom align position of QMenu
      # now move window to new position
      menuY -= self.menu.height()
      menuX, menuY = max(menuX, 0), max(menuY, 0)
      self.menu.move(QtCore.QPoint(int(menuX), int(menuY)))
    else:
      if(callButton != None):
        menuY += callButton.geometry().height()
      menuX, menuY = max(menuX, 0), max(menuY, 0)
      self.menu.move(QtCore.QPoint(int(menuX), int(menuY)))
    
  def changeStyleExtra(self, targetIndex=None, callButton=None):
    # display menu at current mouse pointer
    if((callButton == None) or hasattr(callButton, 'alignMeRight')):
      # dissociated menu => display name of item
      displayName = True
    else:
      displayName = False
    self.menu = ConfigMenuExtra(self, targetIndex, displayName=displayName)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # adjust window position
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      menuY += callButton.geometry().height()
      if(hasattr(callButton, 'alignMeRight')):
        menuX += callButton.geometry().width() - self.menu.width()
      else:
        menuX += callButton.geometry().width() / 2
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()
      menuX -= self.menu.width() / 2
      menuX, menuY = max(menuX, 0), max(menuY, 0)
    # move window to correct position
    self.menu.move(QtCore.QPoint(int(menuX), int(menuY)))
    
  def copyData(self, source=0):
    # this routine copies the current data set
    sourceData = self.parent.data[source].value()
    if('x' in sourceData):
      # would need some kind of deep copy to make this work ...
      # ... but deepcopy produces an error due to matplotlib links, so do it manually
      self.parent.data.append(DataObject(self.parent))
      self.parent.data[-1].setName('Data_' + str(len(self.parent.data)))
      # need to copy contents of original object
      self.parent.data[-1].spawned(self.parent.data[source])
      # set new data object as active
      #self.parent.activeData = (len(self.parent.data) - 1)
      self.changeActiveDataSet(len(self.parent.data) - 1, setCheck=False, redraw=False)
      # cause data to be drawn
      self.parent.data[-1].drawMe(redraw=False)
      self.refreshDataTable()
      self.dataSetTable.scrollToBottom()
      # also create a new resid object
      self.parent.data[-1].drawMeResid()
      self.refreshResidTable()
      self.residTable.scrollToBottom()
      # also refresh curves table to account for increased total number of items
      self.refreshCurvesTable()
      self.refreshExtrasTable()
      self.parent.plotArea.updateToggloContainer()
      self.parent.globalarea.updateDataSetTable()
      # update results table
      self.parent.resultsarea.setDataSet(currIndex=len(self.parent.data) - 1, maxIndex=len(self.parent.data) - 1)
      # duplicate?
      if(self.parent.plotArea.splitY and self.parent.data[-1].onBothAxes):
        self.parent.data[-1].duplicateForSplit()
      # update legend if needed
      self.updateLegend(redraw=True)
    else:
      self.parent.statusbar.showMessage('Data object is empty, will not copy!', self.parent.STATUS_TIME)

  def copyFit(self, source=0):
    # this routine copies the current curve
    self.parent.fit.append(FitObject(self.parent))
    self.parent.fit[-1].setName('Curve_' + str(len(self.parent.fit)))
    self.parent.fit[-1].retired = True
    # need to copy contents of original object
    self.parent.fit[-1].spawned(self.parent.fit[source])
    # set new curve object as active
    #self.parent.activeFit = (len(self.parent.fit) - 1)
    self.changeActiveCurve(len(self.parent.fit) - 1, redraw=False)
    # cause fxn to be drawn
    self.parent.fit[-1].drawMe(redraw=False)
    self.refreshCurvesTable()
    self.curvesTable.scrollToBottom()
    # also refresh data set table to account for increased total number of items
    self.refreshDataTable()
    self.refreshExtrasTable()
    self.parent.plotArea.updateToggloContainer()
    self.parent.globalarea.updateDataSetTable()
    # duplicate?
    if(self.parent.plotArea.splitY and self.parent.fit[-1].onBothAxes):
      self.parent.fit[-1].duplicateForSplit()
    # update legend if needed
    self.updateLegend(redraw=True)
    
  def curveCreate(self):
    # generates a new curve with default settings (vs. copying the fit)
    self.parent.fit.append(FitObject(self.parent))
    self.parent.fit[-1].setName('Curve_' + str(len(self.parent.fit)))
    self.parent.fit[-1].retired = True
    # need to initialize object with default settings
    self.parent.fit[-1].initialize()
    # check whether style definitions are available
    if(len(self.parent.fit) <= len(self.parent.fitStyles)):
      self.parent.fit[-1].setFitStyleSet(index=len(self.parent.fit) - 1)
    else:
      # use default style and update styles to cycling styles
      useColor = self.cycleColors[(len(self.parent.fit) - 1) % len(self.cycleColors)]
      useLinestyle = self.cycleLinestyles[int((len(self.parent.fit) - 1) / len(self.cycleColors))]
      self.parent.fit[-1].style['color'], self.parent.fit[-1].style['markerfacecolor'] = useColor, useColor
      self.parent.fit[-1].style['linestyle'] = useLinestyle
      self.parent.fit[-1].bootStyle['edgecolor'], self.parent.fit[-1].bootStyle['facecolor'] = useColor, useColor[:3] + [0.5]
    # set new curve object as active
    self.changeActiveCurve(len(self.parent.fit) - 1, redraw=False)
    # cause fxn to be drawn
    self.parent.fit[-1].drawMe(redraw=False)
    # check for split y axis
    if(self.parent.plotArea.splitY):
      self.parent.fit[-1].onBothAxes = True
      self.parent.fit[-1].duplicateForSplit()
    self.refreshCurvesTable()
    self.curvesTable.scrollToBottom()
    # also refresh data set table to account for increased total number of items
    self.refreshDataTable()
    self.refreshExtrasTable()
    self.parent.plotArea.updateToggloContainer()
    self.parent.globalarea.updateDataSetTable()
    # update legend if needed
    self.updateLegend(redraw=True)

  def dataSetCreate(self):
    # generates a new, emtpy data set with default settings
    self.parent.data.append(DataObject(self.parent))
    self.parent.data[-1].setName('Data_' + str(len(self.parent.data)))
    # check whether style definitions are available
    if(len(self.parent.data) <= len(self.parent.dataStyles)):
      self.parent.data[-1].setDataStyleSet(index=len(self.parent.data) - 1)
      self.parent.data[-1].setResidStyleSet(index=len(self.parent.data) - 1)
    else:
      # use default style and update styles to cycling styles
      useColor = self.cycleColors[(len(self.parent.data) - 1) % len(self.cycleColors)]
      useMarker = self.cycleMarkers[int((len(self.parent.data) - 1) / len(self.cycleColors)) % len(self.cycleMarkers)]
      self.parent.data[-1].style['color'], self.parent.data[-1].Residstyle['color'] = useColor, useColor
      self.parent.data[-1].style['markerfacecolor'], self.parent.data[-1].Residstyle['markerfacecolor'] = useColor, useColor
      self.parent.data[-1].style['marker'], self.parent.data[-1].Residstyle['marker'] = useMarker, useMarker
      self.parent.data[-1].Errorstyle['color'] = useColor
      self.parent.data[-1].Errorstyle['markerfacecolor'], self.parent.data[-1].Errorstyle['markeredgecolor'] = useColor[:3] + [0.5], useColor
      self.parent.data[-1].ErrorShadystyle['facecolor'], self.parent.data[-1].ErrorShadystyle['edgecolor'] = useColor[:3] + [0.5], useColor
      self.parent.data[-1].Barstyle['facecolor'], self.parent.data[-1].Barstyle['facecolorAlt'] = useColor, [i * 0.5 for i in useColor[:3]] + [1.0]
      self.parent.data[-1].Stackstyle['facecolor'], self.parent.data[-1].Stackstyle['facecolorAlt'] = useColor, [i * 0.5 for i in useColor[:3]] + [1.0]
      self.parent.data[-1].Violinstyle['bodies']['facecolor'], self.parent.data[-1].Violinstyle['boxes']['color'] = [min(1.0, i * 1.3) for i in useColor[:3]] + [1.0], [min(1.0, i * 1.3) for i in useColor[:3]] + [1.0]
    # check whether selectedData exist
    if(self.parent.selectedData.isLive):
      # then we should redraw to clear marker symbols
      redraw = True
    else:
      redraw = False
    # set new data object as active
    self.changeActiveDataSet(len(self.parent.data) - 1, setCheck=False, redraw=False)
    # do not need to draw data as it is empty
    ###self.parent.data[-1].drawMe(redraw=False)
    # check for split y axis
    if(self.parent.plotArea.splitY):
      self.parent.data[-1].onBothAxes = True
      self.parent.data[-1].duplicateForSplit()
    self.refreshDataTable()
    self.dataSetTable.scrollToBottom()
    # also create a new resid object
    ###self.parent.data[-1].drawMeResid()
    self.refreshResidTable()
    self.residTable.scrollToBottom()
    # also refresh curves table to account for increased total number of items
    self.refreshCurvesTable()
    self.refreshExtrasTable()
    self.parent.plotArea.updateToggloContainer()
    self.parent.globalarea.updateDataSetTable()
    # update results table
    self.parent.resultsarea.setDataSet(currIndex=len(self.parent.data) - 1, maxIndex=len(self.parent.data) - 1)
    # update legend if needed (no redraw as newly generated data set is empty)
    self.updateLegend(redraw=redraw)
    
  def copyExtra(self, source=0):
    # this routine copies the current extra
    self.parent.extras.append(ExtrasObject(self.parent))
    # need to copy contents of original object
    self.parent.extras[-1].spawned(self.parent.extras[source])
    # cause fxn to be drawn
    self.parent.extras[-1].drawMe(redraw=False)
    self.refreshExtrasTable()
    self.extrasTable.scrollToBottom()
    # also refresh data set table to account for increased total number of items
    self.refreshDataTable()
    self.refreshCurvesTable()
    self.parent.plotArea.updateToggloContainer()
    # duplicate?
    if(self.parent.plotArea.splitY and self.parent.extras[-1].onBothAxes):
      self.parent.extras[-1].duplicateForSplit()
    # update legend if needed
    self.parent.plotArea.dataplotwidget.myRefresh()

  def updateLegend(self, redraw=True):
    # does legend need to be updated?
    value = self.parent.plotArea.legendVisible
    if(value or redraw):
      self.parent.plotArea.setLegend(value=value, redraw=redraw)

  def roundNumber(self, number, places=3):
    # formats number for output
    NUMBER_SWITCH = 1e3
    FORMAT_DECIMAL = '{:.' + str(places) + 'f}'
    FORMAT_SCIENTIFIC = '{:.' + str(places) + 'e}'
    # determine return string
    if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0/NUMBER_SWITCH)):
      numberstr = FORMAT_SCIENTIFIC.format(number)
    else:
      numberstr = FORMAT_DECIMAL.format(number)

    return float(numberstr)

class ConfigMenuExtra(KuhMenu):
  def __init__(self, parent = None, targetIndex = None, displayName=False):
    super(ConfigMenuExtra, self).__init__()
    self.parent = parent
    self.targetIndex = targetIndex
    self.displayName = displayName
    self.extrasType = self.parent.parent.extras[targetIndex].extrasType
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.linestyles = ['solid', 'dashed', 'dashdot', 'dotted']
    self.dashstyles = ['butt', 'round', 'projecting']
    self.hatchStyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    self.hatchMultiplystyles = [str(i) for i in range(1, 11)]
    self.shapetypes = ['rect', 'circle', 'poly', 'star', 'torus', 'arrow']
      
    # float validator
    self.validFloat = MyValidFloat()
    # int validator
    self.validInt = MyValidInt()
    
    # set up default style
    defaultStyle = {'arrow__x': 2, 'arrow__y': 2, 'arrow__arrowstyle': '->', 'arrow__head_length': .4, 'arrow_head_width': .4, 'arrow__tail_width': .2,
                  'arrow__facecolor': [1.0, 1.0, 1.0, 0.7], 'arrow__edgecolor': [0.0, 0.0, 0.0, 1.0], 'arrow__linewidth': 1., 'arrow__linestyle': 'solid', 'arrow__dash_capstyle': 'butt',
                  'arrow__shrinkA': 5, 'arrow__shrinkB': 5, 'arrow__lengthA': .5, 'arrow__lengthB': .5, 'arrow__widthA': .5, 'arrow__widthB': .5,
                  'arrow__connector': 'arc3', 'arrow__hatch': '', 'arrow_hatchMultiply': 1, 'arrow_hatch_linewidth': 1., 'arrow__doFill': True,
                  'bbox__show': True, 'bbox__boxstyle': 'square', 'bbox__facecolor': [1.0, 1.0, 1.0, 0.7], 'bbox__edgecolor': [0.0, 0.0, 0.0, 1.0],
                  'bbox__linewidth': 1., 'bbox__linestyle': 'solid', 'bbox__dash_capstyle': 'butt', 'bbox__pad': .5,
                  'bbox__hatch': '', 'bbox__hatchMultiply': 1, 'bbox__hatch_linewidth': 1., 'bbox__tooth_size': .5, 'bbox__rounding_size': .5, 'bbox__doFill': True,
                  'x2': 2, 'y2': 2, 'line__linewidth': 1., 'line__linestyle': 'solid',
                  'line__dash_capstyle': 'butt', 'line__solid_capstyle': 'butt', 'line__color': [0.0, 0.0, 0.0, 1.0], 'line__gapcolor': [0, 0, .8, 1.], 'line__dogap': False,
                  'shape__type': 'rect', 'shape__facecolor': [1.0, 1.0, 1.0, 1.0], 'shape__hatch': '', 'shape__hatchMultiply': 1, 'shape__hatch_linewidth': 1.,
                  'shape__numSpokes': 5, 'shape__spikiness': .6, 'shape__doFill': True, 'shape__innerRadius': 0.5,
                  'shape__pie': 0.5, 'shape__vertices': [], 'shape__closed': False, 'shape__dx': .5, 'shape__dy': .5, 'shape__ind': 0.,
                  'extrasType': 'text', 'x': 1, 'y': 1, 'labeltext': 'text', 'color': [0.0, 0.0, 0.0, 1.0], 'fontsize': 12, 'fontname': 'DejaVu Sans',\
                  'rotation': 0.0, 'horizontalalignment': 'center', 'verticalalignment': 'center',\
                  'fontweight': 'normal', 'fontstyle': 'normal', 'linespacing': 1.}

    # set up initial values (needs to be much expanded)
    if (self.targetIndex != None):
      self.style = self.parent.parent.extras[targetIndex].getStyle()
      defaultStyle.update(self.style)
      self.style = defaultStyle
    else:
      self.style = defaultStyle

    self.persist = False
    self.persistPos = None

    # initialize drag values
    self.startPos = None
      
    # set up GUI
    self.buildRessource()

  def togglePersist(self, state):
    # toggles QMenu persistence for click events that would close it, see below
    # when state True, save current menu coordinates
    # when state False, restore QMenu at previous coordinates
    self.persist = state
    if(state):
      self.persistPos = self.pos()
    elif(self.persistPos != None):
      self.popup(self.persistPos)
      self.persistPos = None

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(self.underMouse()):
      if(self.displayName and (event.button() == QtCore.Qt.MouseButton.LeftButton)):
        self.startPos = event.globalPosition().toPoint()
    else:
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def mouseMoveEvent(self, event):
    if((event.buttons() == QtCore.Qt.MouseButton.LeftButton) and (self.startPos != None)):
      currentPos = event.globalPosition().toPoint()
      menuPos = self.mapToGlobal(self.pos()) + (currentPos - self.startPos)
      menuPos = self.mapFromGlobal(menuPos)
      self.move(menuPos)
      self.startPos = currentPos
    
  def mouseReleaseEvent(self, event):
    # turn off tracking
    if(event.button() == QtCore.Qt.MouseButton.LeftButton):
      self.startPos = None

  def buildRessource(self):
    # build outer gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[4]*4)
    self.vLayout.setSpacing(2)
    
    # display name of item?
    if(self.displayName):
      self.topContainer = QWidgetMac(self)
      self.vLayout.addWidget(self.topContainer)
      self.LayoutTopContainer = QtWidgets.QHBoxLayout(self.topContainer)
      self.LayoutTopContainer.setContentsMargins(0, 0, 0, 0)

      leader = 'Extra ' + str(self.targetIndex + 1)
      self.targetLabel = QtWidgets.QLabel()
      self.targetLabel.setText(leader)
      font = self.targetLabel.font()
      font.setBold(True)
      self.targetLabel.setFont(font)
      self.LayoutTopContainer.addWidget(self.targetLabel)
      
      # create entry field to edit name
      name = self.parent.parent.extras[self.targetIndex].labeltext
      if(name != None):
        name = name.replace('\n', '\\n')
        name = name.replace('\t', '\\t')
        self.entryField = QLineEditClick(name)
        self.entryField.setToolTip('Name of extra object ' + str(self.targetIndex))
        self.entryField.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        ###self.entryField.setMaximumSize(QtCore.QSize(int(150 * SCALEFONT), int(BASE_SIZE)))
        self.entryField.setMinimumSize(QtCore.QSize(int(150 * SCALEFONT), int(BASE_SIZE)))
        self.entryField.editingFinished.connect(partial(self.setField, self.targetIndex))
        self.LayoutTopContainer.addWidget(self.entryField)

      # put controls for altering z position
      ###self.LayoutTopContainer.addStretch()
      self.zLabel = QtWidgets.QLabel('z')
      self.LayoutTopContainer.addWidget(self.zLabel)

      self.innerContainer = QWidgetMac(self)
      self.LayoutTopContainer.addWidget(self.innerContainer)
      self.LayoutInnerContainer = QtWidgets.QHBoxLayout(self.innerContainer)
      self.LayoutInnerContainer.setContentsMargins(0, 0, 0, 0)
      self.LayoutInnerContainer.setSpacing(0)

      self.zDownButton = QPushButtonMac()
      self.zDownButton.setText('\u25be')
      self.zDownButton.setToolTip('Lower z order')
      self.zDownButton.setAutoRepeat(True)
      self.zDownButton.setMaximumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zDownButton.setMinimumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zDownButton.clicked.connect(partial(self.alterZ, self.targetIndex, -1))
      self.LayoutInnerContainer.addWidget(self.zDownButton)

      self.zUpButton = QPushButtonMac()
      self.zUpButton.setToolTip('Raise z order')
      self.zUpButton.setText('\u25b4')
      self.zUpButton.setAutoRepeat(True)
      self.zUpButton.setMaximumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zUpButton.setMinimumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zUpButton.clicked.connect(partial(self.alterZ, self.targetIndex, 1))
      self.LayoutInnerContainer.addWidget(self.zUpButton)
      
      # call alterZ to initiate button disabling
      self.alterZ(self.targetIndex, 0)
     
      blah = HLine()
      self.vLayout.addWidget(blah)

    self.upperRow = QWidgetMac()
    self.vLayout.addWidget(self.upperRow)

    self.hLayout0 = QtWidgets.QHBoxLayout(self.upperRow)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)

    # handle area separately -- too different from the other objects
    if(self.extrasType == 'area'):
      # prepare format strings for nicer output
      x, x2, y, y2 = self.style['x'], self.style['x2'], self.style['y'], self.style['y2']
      FORMAT_X = self.parent.parent.getPrecisionFormatPairedNumbers(x, x2)
      FORMAT_Y = self.parent.parent.getPrecisionFormatPairedNumbers(y, y2)

      # build gui for line formatting
      self.formatArea = QWidgetMac()    
      self.vLayout = QtWidgets.QVBoxLayout(self.formatArea)
      self.vLayout.setContentsMargins(0, 0, 0, 0)
      self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
      self.vLayout.setSpacing(2)
      self.hLayout0.addWidget(self.formatArea)
      
      # heading
      self.extrasStyleLabel = QtWidgets.QLabel('Area')
      font = self.extrasStyleLabel.font()
      font.setBold(True)
      self.extrasStyleLabel.setFont(font)
      self.vLayout.addWidget(self.extrasStyleLabel)    
      
      # line position x
      self.labelXGroup = QWidgetMac()
      self.vLayout.addWidget(self.labelXGroup)
      self.hLayout = QtWidgets.QHBoxLayout(self.labelXGroup)
      self.hLayout.setContentsMargins(0, 0, 0, 0)
      self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.labelXLabel = QtWidgets.QLabel('x')
      self.labelXLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelXLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout.addWidget(self.labelXLabel)
  
      self.labelXEntry = QLineEditClickExtra()
      self.labelXEntry.setFormatString(FORMAT_X)
      self.labelXEntry.setValue(self.style['x'])
      self.labelXEntry.setToolTip('X coordinate of extra object')
      ###self.labelXEntry.setText(str(self.style['x']))
      self.labelXEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'x', self.labelXEntry, None, None))
      self.labelXEntry.setValidator(self.validFloat)
      self.hLayout.addWidget(self.labelXEntry)
  
      # line position y
      self.labelYGroup = QWidgetMac()
      self.vLayout.addWidget(self.labelYGroup)
      self.labelYLabel = QtWidgets.QLabel('y')
      self.labelYLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelYLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout.addWidget(self.labelYLabel)
  
      self.labelYEntry = QLineEditClickExtra()
      self.labelYEntry.setFormatString(FORMAT_Y)
      self.labelYEntry.setValue(self.style['y'])
      self.labelYEntry.setToolTip('Y coordinate of extra object')
      ###self.labelYEntry.setText(str(self.style['y']))
      self.labelYEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'y', self.labelYEntry, None, None))
      self.labelYEntry.setValidator(self.validFloat)
      self.hLayout.addWidget(self.labelYEntry)

      # line position x2
      self.labelXGroup2 = QWidgetMac()
      self.vLayout.addWidget(self.labelXGroup2)
      self.hLayout1 = QtWidgets.QHBoxLayout(self.labelXGroup2)
      self.hLayout1.setContentsMargins(0, 0, 0, 0)
      self.hLayout1.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.labelXLabel2 = QtWidgets.QLabel('width')
      self.labelXLabel2.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelXLabel2.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout1.addWidget(self.labelXLabel2)
  
      self.labelXEntry2 = QLineEditClickExtra()
      self.labelXEntry2.setFormatString(FORMAT_X)
      self.labelXEntry2.setValue(self.style['x2'])
      self.labelXEntry2.setToolTip('Width of extra object')
      ###self.labelXEntry2.setText(str(self.style['x2']))
      self.labelXEntry2.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry2.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry2.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'x2', self.labelXEntry2, None, None))
      self.labelXEntry2.setValidator(self.validFloat)
      self.hLayout1.addWidget(self.labelXEntry2)

      # line position y
      self.labelYGroup2 = QWidgetMac()
      self.vLayout.addWidget(self.labelYGroup2)
      self.labelYLabel2 = QtWidgets.QLabel('height')
      self.labelYLabel2.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelYLabel2.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout1.addWidget(self.labelYLabel2)
  
      self.labelYEntry2 = QLineEditClickExtra()
      self.labelYEntry2.setFormatString(FORMAT_Y)
      self.labelYEntry2.setValue(self.style['y2'])
      self.labelYEntry2.setToolTip('Height of extra object')
      ###self.labelYEntry2.setText(str(self.style['y2']))
      self.labelYEntry2.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry2.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry2.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'y2', self.labelYEntry2, 0, None))
      self.labelYEntry2.setValidator(self.validFloat)
      self.hLayout1.addWidget(self.labelYEntry2)

      # line width
      self.lineWidthGroup = QWidgetMac()
      self.vLayout.addWidget(self.lineWidthGroup)
      self.hLayout1 = QtWidgets.QHBoxLayout(self.lineWidthGroup)
      self.hLayout1.setContentsMargins(0, 0, 0, 0)
      self.hLayout1.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.lineWidthLabel = QtWidgets.QLabel('Line')
      self.lineWidthLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout1.addWidget(self.lineWidthLabel)

      self.lineWidthSpin = QDoubleSpinClick()
      self.hLayout1.addWidget(self.lineWidthSpin)
      self.lineWidthSpin.setMinimum(0.0)
      self.lineWidthSpin.setMaximum(100.0)
      self.lineWidthSpin.setValue(self.style['bbox__linewidth'])
      self.lineWidthSpin.setToolTip('Edge width of area')
      self.lineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'bbox__linewidth', self.lineWidthSpin))
    
      # line color
      self.lineColorButton = QPushButtonColor()
      self.lineColorButton.setToolTip('Edge color of area')
      self.lineColorButton.setAutoFillBackground(False)
      colorvalue = [int(i * 255.0) for i in self.style['bbox__edgecolor'][0:3]]
      self.lineColorButton.setMyColor(*colorvalue)
      self.lineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.lineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.lineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.lineColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'bbox__edgecolor'))
      self.hLayout1.addWidget(self.lineColorButton)
    
      # line style
      self.lineStyleGroup = QWidgetMac()
      self.vLayout.addWidget(self.lineStyleGroup)
      self.hLayout3 = QtWidgets.QHBoxLayout(self.lineStyleGroup)
      self.hLayout3.setContentsMargins(0, 0, 0, 0)
      self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.lineStyleLabel = QtWidgets.QLabel('Style')
      self.lineStyleLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.lineStyleLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.lineStyleLabel)
      self.comboLineStyle = QComboBoxMac()
      self.comboLineStyle.setToolTip('Edge line style of are')
      for entry in self.linestyles:
        self.comboLineStyle.addItem(entry)
      if(self.style['bbox__linestyle'] in self.linestyles):
        currindex = self.linestyles.index(self.style['bbox__linestyle'])
      else:
        currindex = 0
      self.comboLineStyle.setCurrentIndex(currindex)
      self.comboLineStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__linestyle', self.comboLineStyle))
      self.comboLineStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.comboLineStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.comboLineStyle)

      # cap style
      self.comboDashStyle = QComboBoxMac()
      self.comboDashStyle.setToolTip('Cap style of extra object')
      for entry in self.dashstyles:
        self.comboDashStyle.addItem(entry)
      if(self.style['line__dash_capstyle'] in self.dashstyles):
        currindex = self.dashstyles.index(self.style['line__dash_capstyle'])
      else:
        currindex = 0
      self.comboDashStyle.setCurrentIndex(currindex)
      self.comboDashStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'line__dash_capstyle', self.comboDashStyle))
      self.comboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.comboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.comboDashStyle)

      # fill color
      self.fillColorGroup = QWidgetMac()
      self.vLayout.addWidget(self.fillColorGroup)
      self.hLayout4 = QtWidgets.QHBoxLayout(self.fillColorGroup)
      self.hLayout4.setContentsMargins(0, 0, 0, 0)
      self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.faceColorLabel = QPushButtonCheckable()
      self.faceColorLabel.setText('Fill')
      self.faceColorLabel.setToolTip('Show fill of area')
      self.faceColorLabel.setChecked(self.style['bbox__doFill'])
      self.faceColorLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.faceColorLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.faceColorLabel.clicked.connect(partial(self.toggleShapeFill, self.targetIndex, 'bbox__doFill', self.faceColorLabel))
      self.hLayout4.addWidget(self.faceColorLabel)
        
      self.faceColorButton = QPushButtonColor()
      self.faceColorButton.setToolTip('Fill color of area')
      self.faceColorButton.setAutoFillBackground(False)
      colorvalue = [int(i * 255.0) for i in self.style['bbox__facecolor'][0:3]]
      self.faceColorButton.setMyColor(*colorvalue)
      self.faceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.faceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.faceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.faceColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'bbox__facecolor'))
      self.hLayout4.addWidget(self.faceColorButton)
    
      # hatch style
      self.hatchGroup = QWidgetMac()
      self.vLayout.addWidget(self.hatchGroup)
      self.hLayout45 = QtWidgets.QHBoxLayout(self.hatchGroup)
      self.hLayout45.setContentsMargins(0, 0, 0, 0)
      self.hLayout45.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout45.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.hatchLineWidthLabel = QtWidgets.QLabel('# line')
      self.hatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout45.addWidget(self.hatchLineWidthLabel)

      self.comboHatchStyle = QComboBoxMac()
      self.comboHatchStyle.setToolTip('Hatch style of area')
      for entry in self.hatchStyles:
        self.comboHatchStyle.addItem(entry)
      if(self.style['bbox__hatch'] in self.hatchStyles):
        currindex = self.hatchStyles.index(self.style['bbox__hatch'])
      else:
        currindex = 0
      self.comboHatchStyle.setCurrentIndex(currindex)
      self.comboHatchStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__hatch', self.comboHatchStyle))
      self.comboHatchStyle.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.comboHatchStyle.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout45.addWidget(self.comboHatchStyle)
      
      self.comboHatchMultiplyStyle = QComboBoxMac()
      self.comboHatchMultiplyStyle.setToolTip('Hatch repeat of area')
      for entry in self.hatchMultiplystyles:
        self.comboHatchMultiplyStyle.addItem(str(entry))
      if(str(self.style['bbox__hatchMultiply']) in self.hatchMultiplystyles):
        currindex = self.hatchMultiplystyles.index(str(self.style['bbox__hatchMultiply']))
      else:
        currindex = 0
      self.comboHatchMultiplyStyle.setCurrentIndex(currindex)
      self.comboHatchMultiplyStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__hatchMultiply', self.comboHatchMultiplyStyle))
      self.comboHatchMultiplyStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
      self.comboHatchMultiplyStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout45.addWidget(self.comboHatchMultiplyStyle)
      
      # hatch line width spin box
      self.hatchLineWidthSpin = QDoubleSpinClick()
      self.hLayout45.addWidget(self.hatchLineWidthSpin)
      self.hatchLineWidthSpin.setMinimum(0.0)
      self.hatchLineWidthSpin.setMaximum(100.0)
      self.hatchLineWidthSpin.setValue(self.style['bbox__hatch_linewidth'])
      self.hatchLineWidthSpin.setToolTip('Hatch line width of object')
      self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'bbox__hatch_linewidth', self.hatchLineWidthSpin))

      # shape angle
      self.configAngleGroup = QWidgetMac()
      self.vLayout.addWidget(self.configAngleGroup)
      self.hLayout5 = QtWidgets.QHBoxLayout(self.configAngleGroup)
      self.hLayout5.setContentsMargins(0, 0, 0, 0)
      self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)    
      self.configAngleLabel = QtWidgets.QLabel('Angle')
      self.configAngleLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.configAngleLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout5.addWidget(self.configAngleLabel)
      # angle spin box
      self.configAngleSpin = QDoubleSpinClick()
      self.hLayout5.addWidget(self.configAngleSpin)
      self.configAngleSpin.setMinimum(0.0)
      self.configAngleSpin.setMaximum(360.0)
      self.configAngleSpin.setValue(self.style['rotation'])
      self.configAngleSpin.setToolTip('Angle of extra object')
      self.configAngleSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAngleSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAngleSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'rotation', self.configAngleSpin))
      self.configAngleSpin.setWrapping(True)
      self.configAngleSpin.setSingleStep(10)
      self.configAngleSpin.setAdaptive(False)

      # fill color
      self.resetGroup = QWidgetMac()
      self.vLayout.addWidget(self.resetGroup)
      self.hLayout6 = QtWidgets.QHBoxLayout(self.resetGroup)
      self.hLayout6.setContentsMargins(0, 0, 0, 0)
      self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.resetLabel = QPushButtonMac()
      self.resetLabel.setText('Reset')
      self.resetLabel.setToolTip('Clear all transformation matrixes for this area object')
      self.resetLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.resetLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.resetLabel.clicked.connect(partial(self.resetTransform, self.targetIndex))
      self.hLayout6.addWidget(self.resetLabel)

    # handle line separately -- too different from the other objects
    elif(self.extrasType in ['line', 'shape']):
      # determine required number precision for display
      x, x2, y, y2 = self.style['x'], self.style['x2'], self.style['y'], self.style['y2']
      FORMAT_X = self.parent.parent.getPrecisionFormatPairedNumbers(x, x2)
      FORMAT_Y = self.parent.parent.getPrecisionFormatPairedNumbers(y, y2)
          
      # build gui for line formatting
      self.formatLine = QWidgetMac()    
      self.vLayout = QtWidgets.QVBoxLayout(self.formatLine)
      self.vLayout.setContentsMargins(0, 0, 0, 0)
      self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
      self.vLayout.setSpacing(2)
      self.hLayout0.addWidget(self.formatLine)
      
      # heading
      self.extrasStyleLabel = QtWidgets.QLabel()
      if(self.extrasType == 'line'):
        self.extrasStyleLabel.setText('Line')
      else:
        self.extrasStyleLabel.setText('Shape')
      font = self.extrasStyleLabel.font()
      font.setBold(True)
      self.extrasStyleLabel.setFont(font)
      self.vLayout.addWidget(self.extrasStyleLabel)    
      
      # configure shape type
      if(self.extrasType == 'shape'):
        self.shapeTypeGroup = QWidgetMac()
        self.vLayout.addWidget(self.shapeTypeGroup)
        self.hLayout0 = QtWidgets.QHBoxLayout(self.shapeTypeGroup)
        self.hLayout0.setContentsMargins(0, 0, 0, 0)
        self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.shapeTypeLabel = QtWidgets.QLabel('Type')
        self.shapeTypeLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.shapeTypeLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0.addWidget(self.shapeTypeLabel)
  
        self.shapeType = QComboBoxMac()
        self.shapeType.setToolTip('Shape type of extra object')
        for entry in self.shapetypes:
          self.shapeType.addItem(entry)
        if(self.style['shape__type'] in self.shapetypes):
          currindex = self.shapetypes.index(self.style['shape__type'])
        else:
          currindex = 0
        self.shapeType.setCurrentIndex(currindex)
        self.shapeType.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'shape__type', self.shapeType))
        self.shapeType.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.shapeType.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0.addWidget(self.shapeType)
        
        self.spokeNumberSpinBox = QSpinClick()
        self.spokeNumberSpinBox.setToolTip('Number corners/spokes of extra object')
        self.spokeNumberSpinBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
        self.spokeNumberSpinBox.setMinimum(3)
        self.spokeNumberSpinBox.setMaximum(30)
        self.spokeNumberSpinBox.setValue(self.style['shape__numSpokes'])
        self.spokeNumberSpinBox.setMinimumSize(int(50 * SCALEFONT), int(BASE_SIZE))
        self.spokeNumberSpinBox.setMaximumSize(int(50 * SCALEFONT), int(BASE_SIZE))
        self.spokeNumberSpinBox.editingFinished.connect(partial(self.changeSpokeNumber, self.targetIndex, 'shape__numSpokes', self.spokeNumberSpinBox))
        self.hLayout0.addWidget(self.spokeNumberSpinBox)
        
        if(self.style['shape__type'] in ['poly', 'star']):
          self.spokeNumberSpinBox.show()
        else:
          self.spokeNumberSpinBox.hide()
          
        if(self.style['shape__type'] == 'freehand'):
          self.shapeTypeGroup.hide()

        # spikiness of spokes
        self.spikySpokeGroup = QWidgetMac()
        self.vLayout.addWidget(self.spikySpokeGroup)
        self.hLayout0A = QtWidgets.QHBoxLayout(self.spikySpokeGroup)
        self.hLayout0A.setContentsMargins(0, 0, 0, 0)
        self.hLayout0A.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout0A.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.spikySpokeLabel = QtWidgets.QLabel('Spiky')
        self.spikySpokeLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.spikySpokeLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0A.addWidget(self.spikySpokeLabel)
        
        self.spikySpokeEntry = QLineEditClick()
        self.spikySpokeEntry.setToolTip('Spikiness of extra object')
        self.spikySpokeEntry.setText(str(self.style['shape__spikiness']))
        self.spikySpokeEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.spikySpokeEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.spikySpokeEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'shape__spikiness', self.spikySpokeEntry, 0, 3))
        self.spikySpokeEntry.setValidator(self.validFloat)
        self.hLayout0A.addWidget(self.spikySpokeEntry)

        if(self.style['shape__type'] == 'star'):
          self.spikySpokeGroup.show()
        else:
          self.spikySpokeGroup.hide()

        # inner radius of torus
        self.innerRadiusGroup = QWidgetMac()
        self.vLayout.addWidget(self.innerRadiusGroup)
        self.hLayout0B = QtWidgets.QHBoxLayout(self.innerRadiusGroup)
        self.hLayout0B.setContentsMargins(0, 0, 0, 0)
        self.hLayout0B.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout0B.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

        self.innerRadiusLabel = QtWidgets.QLabel('Inner')
        self.innerRadiusLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.innerRadiusLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0B.addWidget(self.innerRadiusLabel)
        self.innerRadiusEntry = QLineEditClick()
        self.innerRadiusEntry.setToolTip('Inner radius of extra object')
        self.innerRadiusEntry.setText(str(self.style['shape__innerRadius']))
        self.innerRadiusEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.innerRadiusEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.innerRadiusEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'shape__innerRadius', self.innerRadiusEntry, 0, 1))
        self.innerRadiusEntry.setValidator(self.validFloat)
        self.hLayout0B.addWidget(self.innerRadiusEntry)

        self.pieLabel = QtWidgets.QLabel('Pie')
        self.pieLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.pieLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0B.addWidget(self.pieLabel)
        self.pieEntry = QLineEditClick()
        self.pieEntry.setToolTip('Pie slice of extra object')
        self.pieEntry.setText(str(self.style['shape__pie']))
        self.pieEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.pieEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.pieEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'shape__pie', self.pieEntry, 0, 1))
        self.pieEntry.setValidator(self.validFloat)
        self.hLayout0B.addWidget(self.pieEntry)

        if(self.style['shape__type'] == 'torus'):
          self.innerRadiusGroup.show()
        else:
          self.innerRadiusGroup.hide()

      # line position x
      self.labelXGroup = QWidgetMac()
      self.vLayout.addWidget(self.labelXGroup)
      self.hLayout = QtWidgets.QHBoxLayout(self.labelXGroup)
      self.hLayout.setContentsMargins(0, 0, 0, 0)
      self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.labelXLabel = QtWidgets.QLabel('x')
      self.labelXLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelXLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout.addWidget(self.labelXLabel)
  
      self.labelXEntry = QLineEditClickExtra()
      self.labelXEntry.setToolTip('X coordinate of extra object')
      self.labelXEntry.setFormatString(FORMAT_X)
      self.labelXEntry.setValue(self.style['x'])
      ####self.labelXEntry.setText(FORMAT_X.format(self.style['x']))
      self.labelXEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'x', self.labelXEntry, None, None))
      self.labelXEntry.setValidator(self.validFloat)
      self.hLayout.addWidget(self.labelXEntry)
  
      # line position y
      self.labelYGroup = QWidgetMac()
      self.vLayout.addWidget(self.labelYGroup)
      self.labelYLabel = QtWidgets.QLabel('y')
      self.labelYLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelYLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout.addWidget(self.labelYLabel)
  
      self.labelYEntry = QLineEditClickExtra()
      self.labelYEntry.setToolTip('Y coordinate of extra object')
      self.labelYEntry.setFormatString(FORMAT_Y)
      self.labelYEntry.setValue(self.style['y'])
      ###self.labelYEntry.setText(FORMAT_Y.format(self.style['y']))
      self.labelYEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'y', self.labelYEntry, None, None))
      self.labelYEntry.setValidator(self.validFloat)
      self.hLayout.addWidget(self.labelYEntry)

      # line position x2
      self.labelXGroup2 = QWidgetMac()
      self.vLayout.addWidget(self.labelXGroup2)
      self.hLayout1 = QtWidgets.QHBoxLayout(self.labelXGroup2)
      self.hLayout1.setContentsMargins(0, 0, 0, 0)
      self.hLayout1.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      if(self.extrasType == 'shape'):
        self.labelXLabel2 = QtWidgets.QLabel('width')
      else:
        self.labelXLabel2 = QtWidgets.QLabel('x2')
      self.labelXLabel2.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelXLabel2.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout1.addWidget(self.labelXLabel2)
  
      self.labelXEntry2 = QLineEditClickExtra()
      self.labelXEntry2.setFormatString(FORMAT_X)
      self.labelXEntry2.setValue(self.style['x2'])
      if(self.extrasType == 'shape'):
        self.labelXEntry2.setToolTip('Width of extra object')
      else:
        self.labelXEntry2.setToolTip('X2 coordinate of extra object')
      ###self.labelXEntry2.setText(str(self.style['x2']))
      self.labelXEntry2.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry2.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      if(self.extrasType == 'shape'):
        self.labelXEntry2.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'x2', self.labelXEntry2, 0, None))
      else:
        self.labelXEntry2.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'x2', self.labelXEntry2, None, None))
      self.labelXEntry2.setValidator(self.validFloat)
      self.hLayout1.addWidget(self.labelXEntry2)
  
      # line position y
      self.labelYGroup2 = QWidgetMac()
      self.vLayout.addWidget(self.labelYGroup2)
      if(self.extrasType == 'shape'):
        self.labelYLabel2 = QtWidgets.QLabel('height')
      else:
        self.labelYLabel2 = QtWidgets.QLabel('y2')
      self.labelYLabel2.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.labelYLabel2.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout1.addWidget(self.labelYLabel2)
  
      self.labelYEntry2 = QLineEditClickExtra()
      self.labelYEntry2.setFormatString(FORMAT_Y)
      self.labelYEntry2.setValue(self.style['y2'])
      if(self.extrasType == 'shape'):
        self.labelYEntry2.setToolTip('Height of extra object')
      else:
        self.labelYEntry2.setToolTip('Y2 coordinate of extra object')
      ###self.labelYEntry2.setText(str(self.style['y2']))
      self.labelYEntry2.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry2.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      if(self.extrasType == 'shape'):
        self.labelYEntry2.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'y2', self.labelYEntry2, 0, None))
      else:
        self.labelYEntry2.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'y2', self.labelYEntry2, None, None))
      self.labelYEntry2.setValidator(self.validFloat)
      self.hLayout1.addWidget(self.labelYEntry2)
      
      # configure shape type
      if(self.extrasType == 'shape'):
        # aspect ratio of arrow shape
        self.arrowAspectGroup = QWidgetMac()
        self.vLayout.addWidget(self.arrowAspectGroup)
        self.hLayout0C = QtWidgets.QHBoxLayout(self.arrowAspectGroup)
        self.hLayout0C.setContentsMargins(0, 0, 0, 0)
        self.hLayout0C.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout0C.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

        self.arrowAspectLabelX = QtWidgets.QLabel('dx')
        self.arrowAspectLabelX.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectLabelX.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0C.addWidget(self.arrowAspectLabelX)
 
        self.arrowAspectXEntry = QLineEditClickExtra()
        self.arrowAspectXEntry.setToolTip('Relative width of arrow tip')
        self.arrowAspectXEntry.setFormatString(FORMAT_X)
        self.arrowAspectXEntry.setValue(self.style['shape__dx'])
        self.arrowAspectXEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectXEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectXEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'shape__dx', self.arrowAspectXEntry, 0., 1.))
        self.arrowAspectXEntry.setValidator(self.validFloat)
        self.hLayout0C.addWidget(self.arrowAspectXEntry)

        self.arrowAspectLabelY = QtWidgets.QLabel('dy')
        self.arrowAspectLabelY.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectLabelY.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0C.addWidget(self.arrowAspectLabelY)
 
        self.arrowAspectYEntry = QLineEditClickExtra()
        self.arrowAspectYEntry.setToolTip('Relative height of arrow tip')
        self.arrowAspectYEntry.setFormatString(FORMAT_X)
        self.arrowAspectYEntry.setValue(self.style['shape__dy'])
        self.arrowAspectYEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectYEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectYEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'shape__dy', self.arrowAspectYEntry, 0., 1.))
        self.arrowAspectYEntry.setValidator(self.validFloat)
        self.hLayout0C.addWidget(self.arrowAspectYEntry)

        self.arrowAspectLabelInd = QtWidgets.QLabel('ind')
        self.arrowAspectLabelInd.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectLabelInd.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout0C.addWidget(self.arrowAspectLabelInd)
 
        self.arrowAspectIndEntry = QLineEditClickExtra()
        self.arrowAspectIndEntry.setToolTip('Indentation of arrow tip')
        self.arrowAspectIndEntry.setFormatString(FORMAT_X)
        self.arrowAspectIndEntry.setValue(self.style['shape__ind'])
        self.arrowAspectIndEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectIndEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowAspectIndEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'shape__ind', self.arrowAspectIndEntry, 0., 1.))
        self.arrowAspectIndEntry.setValidator(self.validFloat)
        self.hLayout0C.addWidget(self.arrowAspectIndEntry)

        if(self.style['shape__type'] == 'arrow'):
          self.arrowAspectGroup.show()
        else:
          self.arrowAspectGroup.hide()

      # line style
      self.linePropsGroup = QWidgetMac()
      self.vLayout.addWidget(self.linePropsGroup)
      self.hLayout2 = QtWidgets.QHBoxLayout(self.linePropsGroup)
      self.hLayout2.setContentsMargins(0, 0, 0, 0)
      self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.linePropsLabel = QtWidgets.QLabel('Line')
      self.linePropsLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.linePropsLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout2.addWidget(self.linePropsLabel)
      # line width spin box
      self.lineWidthSpin = QDoubleSpinClick()
      self.hLayout2.addWidget(self.lineWidthSpin)
      self.lineWidthSpin.setMinimum(0.0)
      self.lineWidthSpin.setMaximum(100.0)
      self.lineWidthSpin.setValue(self.style['line__linewidth'])
      self.lineWidthSpin.setToolTip('Line width of extra object')
      self.lineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'line__linewidth', self.lineWidthSpin))
    
      # line color
      self.lineColorButton = QPushButtonColor()
      self.lineColorButton.setToolTip('Line color of extra object')
      self.lineColorButton.setAutoFillBackground(False)
      colorvalue = [int(i * 255.0) for i in self.style['line__color'][0:3]]
      self.lineColorButton.setMyColor(*colorvalue)
      self.lineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.lineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.lineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.lineColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'line__color'))
      self.hLayout2.addWidget(self.lineColorButton)

      # gap color
      if(self.style['extrasType'] == 'line'):
        self.lineColorLabel2 = QPushButtonCheckable()
        self.lineColorLabel2.setText('Gap')
        self.lineColorLabel2.setToolTip('Show line gap color of object')
        self.lineColorLabel2.setChecked(self.style['line__dogap'])
        self.lineColorLabel2.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
        self.lineColorLabel2.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
        self.lineColorLabel2.clicked.connect(partial(self.toggleDoGap, self.targetIndex, 'line__dogap', self.lineColorLabel2))
        self.hLayout2.addWidget(self.lineColorLabel2)
          
        self.lineColorButton2 = QPushButtonColor()
        self.lineColorButton2.setToolTip('Line gap color of object')
        self.lineColorButton2.setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.style['line__gapcolor'][0:3]]
        self.lineColorButton2.setMyColor(*colorvalue)
        self.lineColorButton2.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.lineColorButton2.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.lineColorButton2.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.lineColorButton2.clicked.connect(partial(self.changeLabelColor, self.targetIndex, key='line__gapcolor'))
        self.hLayout2.addWidget(self.lineColorButton2)

      # close shape?
      if((self.style['extrasType'] == 'shape') and (self.style['shape__type'] == 'freehand')):
        self.closeFreehandButton = QPushButtonCheckable()
        self.closeFreehandButton.setText('closed')
        self.closeFreehandButton.setToolTip('Close freehand shape')
        self.closeFreehandButton.setChecked(self.style['shape__closed'])
        self.closeFreehandButton.setMaximumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
        self.closeFreehandButton.setMinimumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
        self.closeFreehandButton.clicked.connect(partial(self.toggleFreehandClose, self.targetIndex))
        self.hLayout2.addWidget(self.closeFreehandButton)
      
      # line style
      self.lineStyleGroup = QWidgetMac()
      self.vLayout.addWidget(self.lineStyleGroup)
      self.hLayout3 = QtWidgets.QHBoxLayout(self.lineStyleGroup)
      self.hLayout3.setContentsMargins(0, 0, 0, 0)
      self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.lineStyleLabel = QtWidgets.QLabel('Style')
      self.lineStyleLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.lineStyleLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.lineStyleLabel)

      self.lineStyle = QComboBoxMac()
      self.lineStyle.setToolTip('Line style of extra object')
      for entry in self.linestyles:
        self.lineStyle.addItem(entry)
      if(self.style['line__linestyle'] in self.linestyles):
        currindex = self.linestyles.index(self.style['line__linestyle'])
      else:
        currindex = 0
      self.lineStyle.setCurrentIndex(currindex)
      self.lineStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'line__linestyle', self.lineStyle))
      self.lineStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.lineStyle)
 
      # cap style
      self.comboDashStyle = QComboBoxMac()
      self.comboDashStyle.setToolTip('Cap style of extra object')
      for entry in self.dashstyles:
        self.comboDashStyle.addItem(entry)
      if(self.style['line__dash_capstyle'] in self.dashstyles):
        currindex = self.dashstyles.index(self.style['line__dash_capstyle'])
      else:
        currindex = 0
      self.comboDashStyle.setCurrentIndex(currindex)
      self.comboDashStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'line__dash_capstyle', self.comboDashStyle))
      self.comboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.comboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.comboDashStyle)
      
      # now add some extra attributes for shapes
      if(self.extrasType == 'shape'):
        # shape colors
        self.shapeColorGroup = QWidgetMac()
        self.vLayout.addWidget(self.shapeColorGroup)
        self.hLayout4 = QtWidgets.QHBoxLayout(self.shapeColorGroup)
        self.hLayout4.setContentsMargins(0, 0, 0, 0)
        self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.shapeColorLabel = QPushButtonCheckable()
        self.shapeColorLabel.setText('Fill')
        self.shapeColorLabel.setToolTip('Show fill of extra object')
        self.shapeColorLabel.setChecked(self.style['shape__doFill'])
        self.shapeColorLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.shapeColorLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.shapeColorLabel.clicked.connect(partial(self.toggleShapeFill, self.targetIndex, 'shape__doFill', self.shapeColorLabel))
        self.hLayout4.addWidget(self.shapeColorLabel)
    
        self.shapeFaceColorButton = QPushButtonColor()
        self.shapeFaceColorButton.setToolTip('Fill color of extra object')
        self.shapeFaceColorButton.setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.style['shape__facecolor'][0:3]]
        self.shapeFaceColorButton.setMyColor(*colorvalue)
        self.shapeFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.shapeFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.shapeFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.shapeFaceColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'shape__facecolor'))
        self.hLayout4.addWidget(self.shapeFaceColorButton)    
    
        # hatch style
        self.hatchGroup = QWidgetMac()
        self.vLayout.addWidget(self.hatchGroup)
        self.hLayout45 = QtWidgets.QHBoxLayout(self.hatchGroup)
        self.hLayout45.setContentsMargins(0, 0, 0, 0)
        self.hLayout45.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout45.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.hatchLineWidthLabel = QtWidgets.QLabel('# line')
        self.hatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout45.addWidget(self.hatchLineWidthLabel)

        self.comboShapeHatch = QComboBoxMac()
        self.comboShapeHatch.setToolTip('Hatch style of extra object')
        for entry in self.hatchStyles:
          self.comboShapeHatch.addItem(entry)
        if(self.style['shape__hatch'] in self.hatchStyles):
          currindex = self.hatchStyles.index(self.style['shape__hatch'])
        else:
          currindex = 0
        self.comboShapeHatch.setCurrentIndex(currindex)
        self.comboShapeHatch.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'shape__hatch', self.comboShapeHatch))
        self.comboShapeHatch.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.comboShapeHatch.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout45.addWidget(self.comboShapeHatch)
        self.comboShapeHatchMultiply = QComboBoxMac()
        self.comboShapeHatchMultiply.setToolTip('Hatch repeat of extra object')
        for entry in self.hatchMultiplystyles:
          self.comboShapeHatchMultiply.addItem(str(entry))
        if(str(self.style['shape__hatchMultiply']) in self.hatchMultiplystyles):
          currindex = self.hatchMultiplystyles.index(str(self.style['shape__hatchMultiply']))
        else:
          currindex = 0
        self.comboShapeHatchMultiply.setCurrentIndex(currindex)
        self.comboShapeHatchMultiply.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'shape__hatchMultiply', self.comboShapeHatchMultiply))
        self.comboShapeHatchMultiply.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.comboShapeHatchMultiply.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout45.addWidget(self.comboShapeHatchMultiply)

        # hatch line width spin box
        self.hatchLineWidthSpin = QDoubleSpinClick()
        self.hLayout45.addWidget(self.hatchLineWidthSpin)
        self.hatchLineWidthSpin.setMinimum(0.0)
        self.hatchLineWidthSpin.setMaximum(100.0)
        self.hatchLineWidthSpin.setValue(self.style['shape__hatch_linewidth'])
        self.hatchLineWidthSpin.setToolTip('Hatch line width of extra object')
        self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'shape__hatch_linewidth', self.hatchLineWidthSpin))

        # shape angle
        self.configAngleGroup = QWidgetMac()
        self.vLayout.addWidget(self.configAngleGroup)
        self.hLayout5 = QtWidgets.QHBoxLayout(self.configAngleGroup)
        self.hLayout5.setContentsMargins(0, 0, 0, 0)
        self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)    
        self.configAngleLabel = QtWidgets.QLabel('Angle')
        self.configAngleLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.configAngleLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout5.addWidget(self.configAngleLabel)
        # angle spin box
        self.configAngleSpin = QDoubleSpinClick()
        self.hLayout5.addWidget(self.configAngleSpin)
        self.configAngleSpin.setMinimum(0.0)
        self.configAngleSpin.setMaximum(360.0)
        self.configAngleSpin.setValue(self.style['rotation'])
        self.configAngleSpin.setToolTip('Angle of extra object')
        self.configAngleSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.configAngleSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.configAngleSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'rotation', self.configAngleSpin))
        self.configAngleSpin.setWrapping(True)
        self.configAngleSpin.setSingleStep(10)
        self.configAngleSpin.setAdaptive(False)

        # freehand shape reduction
        if(self.style['shape__type'] == 'freehand'):
          self.configReductionGroup = QWidgetMac()
          self.vLayout.addWidget(self.configReductionGroup)
          self.hLayout6 = QtWidgets.QHBoxLayout(self.configReductionGroup)
          self.hLayout6.setContentsMargins(0, 0, 0, 0)
          self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
          self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)    
          self.configReductionLabel = QtWidgets.QLabel('Vertex')
          self.configReductionLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
          self.configReductionLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
          self.hLayout6.addWidget(self.configReductionLabel)

          self.configReductionButton = QPushButtonMac()
          self.configReductionButton.setText('Simplify')
          self.configReductionButton.setToolTip('Reduce number of vertices in extra object')
          self.configReductionButton.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
          self.configReductionButton.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
          self.configReductionButton.clicked.connect(partial(self.toggleFreehandReduce, self.targetIndex))
          self.hLayout6.addWidget(self.configReductionButton)
          
          self.configReductionCount = QtWidgets.QLabel('')
          self.configReductionCount.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
          self.configReductionCount.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
          self.hLayout6.addWidget(self.configReductionCount)

          if(('shape__vertices' in self.style) and len(self.style['shape__vertices'])):
            self.configReductionCount.setText(str(len(self.style['shape__vertices'])))
    else:
      # determine required number precision for display
      x, x2, y, y2 = self.parent.parent.plotArea.minX, self.parent.parent.plotArea.maxX,\
        self.parent.parent.plotArea.minY, self.parent.parent.plotArea.maxY
      FORMAT_X = self.parent.parent.getPrecisionFormatPairedNumbers(x, x2)
      FORMAT_Y = self.parent.parent.getPrecisionFormatPairedNumbers(y, y2)
      x, x2, y, y2 = self.style['x'], self.style['arrow__x'], self.style['y'], self.style['arrow__y']
      FORMAT_ARROW_X = self.parent.parent.getPrecisionFormatPairedNumbers(x, x2)
      FORMAT_ARROW_Y = self.parent.parent.getPrecisionFormatPairedNumbers(y, y2)

      # build gui for label formatting
      self.formatLabel = QWidgetMac()    
      self.vLayout = QtWidgets.QVBoxLayout(self.formatLabel)
      self.vLayout.setContentsMargins(0, 0, 0, 0)
      self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
      self.vLayout.setSpacing(2)
      self.hLayout0.addWidget(self.formatLabel)
      
      # heading
      self.extrasStyleLabel = QtWidgets.QLabel()
      self.extrasStyleLabel.setText('Text')
      font = self.extrasStyleLabel.font()
      font.setBold(True)
      self.extrasStyleLabel.setFont(font)
      self.vLayout.addWidget(self.extrasStyleLabel)    
      
      # label position x
      self.labelXGroup = QWidgetMac()
      self.vLayout.addWidget(self.labelXGroup)
      self.hLayout = QtWidgets.QHBoxLayout(self.labelXGroup)
      self.hLayout.setContentsMargins(0, 0, 0, 0)
      self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.labelXLabel = QtWidgets.QLabel('x')
      self.labelXLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.labelXLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout.addWidget(self.labelXLabel)
  
      self.labelXEntry = QLineEditClickExtra()
      self.labelXEntry.setFormatString(FORMAT_X)
      self.labelXEntry.setValue(self.style['x'])
      self.labelXEntry.setToolTip('X coordinate of extra object')
      ###self.labelXEntry.setText(str(self.style['x']))
      self.labelXEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelXEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'x', self.labelXEntry, None, None))
      self.labelXEntry.setValidator(self.validFloat)
      self.hLayout.addWidget(self.labelXEntry)
  
      # label position y
      self.labelYGroup = QWidgetMac()
      self.vLayout.addWidget(self.labelYGroup)
      self.labelYLabel = QtWidgets.QLabel('y')
      self.labelYLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.labelYLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout.addWidget(self.labelYLabel)
  
      self.labelYEntry = QLineEditClickExtra()
      self.labelYEntry.setFormatString(FORMAT_Y)
      self.labelYEntry.setValue(self.style['y'])
      self.labelYEntry.setToolTip('Y coordinate of extra object')
      ###self.labelYEntry.setText(str(self.style['y']))
      self.labelYEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.labelYEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'y', self.labelYEntry, None, None))
      self.labelYEntry.setValidator(self.validFloat)
      self.hLayout.addWidget(self.labelYEntry)
      
      # label text style
      self.configSizeGroup = QWidgetMac()
      self.vLayout.addWidget(self.configSizeGroup)
      self.hLayout1 = QtWidgets.QHBoxLayout(self.configSizeGroup)
      self.hLayout1.setContentsMargins(0, 0, 0, 0)
      self.hLayout1.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configSizeLabel = QtWidgets.QLabel('Font')
      self.configSizeLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configSizeLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout1.addWidget(self.configSizeLabel)
  
      self.configColorLabelButton = QPushButtonColor()
      self.configColorLabelButton.setToolTip('Text color of extra object')
      self.configColorLabelButton.setAutoFillBackground(False)
      colorvalue = [int(i * 255.0) for i in self.style['color'][0:3]]
      self.configColorLabelButton.setMyColor(*colorvalue)
      self.configColorLabelButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configColorLabelButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configColorLabelButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.configColorLabelButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'color'))
      self.hLayout1.addWidget(self.configColorLabelButton)

      # font size spin box
      self.configLabelSizeSpin = QDoubleSpinClick()
      self.hLayout1.addWidget(self.configLabelSizeSpin)
      self.configLabelSizeSpin.setMinimum(0.0)
      self.configLabelSizeSpin.setMaximum(100.0)
      self.configLabelSizeSpin.setValue(self.style['fontsize'])
      self.configLabelSizeSpin.setToolTip('Text font size of extra object')
      self.configLabelSizeSpin.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configLabelSizeSpin.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configLabelSizeSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'fontsize', self.configLabelSizeSpin))
      
      self.configLabelBold = QPushButtonCheckable()
      self.configLabelBold.setToolTip('Text font weight of extra object')
      self.configLabelBold.setText('B')
      self.configLabelBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLabelBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      if(self.style['fontweight'] == 'bold'):
        self.configLabelBold.setChecked(True)
      useFont = self.configLabelBold.font()
      useFont.setBold(True)
      self.configLabelBold.setFont(useFont)
      self.configLabelBold.clicked.connect(partial(self.changeLabelFontStyle, self.targetIndex, 'fontweight', self.configLabelBold))
      self.hLayout1.addWidget(self.configLabelBold)
      
      self.configLabelItalic = QPushButtonCheckable()
      self.configLabelItalic.setText('I')
      self.configLabelItalic.setToolTip('Text font style of extra object')
      self.configLabelItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLabelItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      if(self.style['fontstyle'] == 'italic'):
        self.configLabelItalic.setChecked(True)
      useFont = self.configLabelItalic.font()
      useFont.setItalic(True)
      self.configLabelItalic.setFont(useFont)
      self.configLabelItalic.clicked.connect(partial(self.changeLabelFontStyle, self.targetIndex, 'fontstyle', self.configLabelItalic))
      self.hLayout1.addWidget(self.configLabelItalic)
      
      self.configSizeGroup2 = QWidgetMac()
      self.vLayout.addWidget(self.configSizeGroup2)
      self.hLayout2 = QtWidgets.QHBoxLayout(self.configSizeGroup2)
      self.hLayout2.setContentsMargins(0, 0, 0, 0)
      self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QtWidgets.QLabel('')
      spacer.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout2.addWidget(spacer)
  
      self.configLabelFont = FontoBox()
      self.configLabelFont.setToolTip('Text font of extra object')
      self.configLabelFont.addItems(items=self.parent.parent.fontNames)
      self.configLabelFont.setMaximumSize(QtCore.QSize(int(140 * SCALEFONT), int(BASE_SIZE)))
      self.configLabelFont.setMinimumSize(QtCore.QSize(int(140 * SCALEFONT), int(BASE_SIZE)))
      self.configLabelFont.setMe(item=self.style['fontname'])
      self.configLabelFont.activated.connect(partial(self.changeLabelFont, self.targetIndex))
      self.hLayout2.addWidget(self.configLabelFont)
  
      # label linespacing
      self.configLinespacingGroup = QWidgetMac()
      self.vLayout.addWidget(self.configLinespacingGroup)
      self.hLayout35 = QtWidgets.QHBoxLayout(self.configLinespacingGroup)
      self.hLayout35.setContentsMargins(0, 0, 0, 0)
      self.hLayout35.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout35.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configLinespacingLabel = QtWidgets.QLabel('Linesp.')
      self.configLinespacingLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configLinespacingLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout35.addWidget(self.configLinespacingLabel)
      # label linespacing dialog
      self.configLinespacing = QLineEditClick()
      self.hLayout35.addWidget(self.configLinespacing)
      self.configLinespacing.setText(str(self.style['linespacing']))
      self.configLinespacing.setToolTip('Text linespacing of extra object')
      self.configLinespacing.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configLinespacing.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configLinespacing.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'linespacing', self.configLinespacing, 0., 10.))

      # label angle
      self.configAngleGroup = QWidgetMac()
      self.vLayout.addWidget(self.configAngleGroup)
      self.hLayout3 = QtWidgets.QHBoxLayout(self.configAngleGroup)
      self.hLayout3.setContentsMargins(0, 0, 0, 0)
      self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)    
      self.configAngleLabel = QtWidgets.QLabel('Angle')
      self.configAngleLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configAngleLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout3.addWidget(self.configAngleLabel)
      # label angle spin box
      self.configAngleSpin = QDoubleSpinClick()
      self.hLayout3.addWidget(self.configAngleSpin)
      self.configAngleSpin.setMinimum(0.0)
      self.configAngleSpin.setMaximum(360.0)
      self.configAngleSpin.setValue(self.style['rotation'])
      self.configAngleSpin.setToolTip('Text angle of extra object')
      self.configAngleSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAngleSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAngleSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'rotation', self.configAngleSpin))
      self.configAngleSpin.setWrapping(True)
      self.configAngleSpin.setSingleStep(10)
      self.configAngleSpin.setAdaptive(False)
      
      # label alignment
      self.configAlignmentGroup = QWidgetMac()
      self.vLayout.addWidget(self.configAlignmentGroup)
      self.hLayout4 = QtWidgets.QHBoxLayout(self.configAlignmentGroup)
      self.hLayout4.setContentsMargins(0, 0, 0, 0)
      self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)    
      self.configAlignmentLabel = QtWidgets.QLabel('Align')
      self.configAlignmentLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configAlignmentLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout4.addWidget(self.configAlignmentLabel)
  
      self.alignHorizontal = ['left', 'center', 'right']
      self.configAlignment = QComboBoxMac()
      self.configAlignment.setToolTip('Text horizontal alignment of extra object')
      self.configAlignment.addItems(self.alignHorizontal)
      if(self.style['horizontalalignment'] in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.style['horizontalalignment'])
        self.configAlignment.setCurrentIndex(currindex)
      else:
        self.configAlignment.setCurrentIndex(0)
      self.configAlignment.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAlignment.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAlignment.activated.connect(partial(self.changeLabelAlignment, self.targetIndex))
      self.hLayout4.addWidget(self.configAlignment)
      
      self.alignVertical = ['top', 'center', 'bottom', 'baseline']
      self.configAlignmentVertical = QComboBoxMac()
      self.configAlignmentVertical.setToolTip('Text vertical alignment of extra object')
      self.configAlignmentVertical.addItems(self.alignVertical)
      if(self.style['verticalalignment'] in self.alignVertical):
        currindex = self.alignVertical.index(self.style['verticalalignment'])
        self.configAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configAlignmentVertical.setCurrentIndex(0)
      self.configAlignmentVertical.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAlignmentVertical.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAlignmentVertical.activated.connect(partial(self.changeLabelAlignmentVertical, self.targetIndex))
      self.hLayout4.addWidget(self.configAlignmentVertical)
 
      # checkbox for display of bbox
      self.bboxShowGroup = QWidgetMac()
      self.vLayout.addWidget(self.bboxShowGroup)
      self.hLayout5 = QtWidgets.QHBoxLayout(self.bboxShowGroup)
      self.hLayout5.setContentsMargins(0, 0, 0, 0)
      self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)    
      self.bboxShowLabel = QPushButtonCheckable()
      self.bboxShowLabel.setText('Box?')
      self.bboxShowLabel.setToolTip('Show bounding box of extra object')
      self.bboxShowLabel.setChecked(self.style['bbox__show'])
      self.bboxShowLabel.clicked.connect(partial(self.toggleBbox, self.targetIndex))
      self.bboxShowLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.bboxShowLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.hLayout5.addWidget(self.bboxShowLabel)
  
      # bbox config menu
      self.divider = VLine()
      self.hLayout0.addWidget(self.divider)
      # build gui for label formatting
      self.formatBbox = QWidgetMac()    
      self.vLayoutA1 = QtWidgets.QVBoxLayout(self.formatBbox)
      self.vLayoutA1.setContentsMargins(0, 0, 0, 0)
      self.vLayoutA1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
      self.vLayoutA1.setSpacing(2)
      self.hLayout0.addWidget(self.formatBbox)
        
      # heading
      self.extrasBboxLabel = QtWidgets.QLabel()
      self.extrasBboxLabel.setText('Box')
      font = self.extrasBboxLabel.font()
      font.setBold(True)
      self.extrasBboxLabel.setFont(font)
      self.vLayoutA1.addWidget(self.extrasBboxLabel)
      
      # bbox line style
      self.bboxLineGroup = QWidgetMac()
      self.vLayoutA1.addWidget(self.bboxLineGroup)
      self.hLayoutA2 = QtWidgets.QHBoxLayout(self.bboxLineGroup)
      self.hLayoutA2.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.bboxLineLabel = QtWidgets.QLabel('Line')
      self.bboxLineLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxLineLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA2.addWidget(self.bboxLineLabel)

      # bbox edge width spin box
      self.bboxLineWidthSpin = QDoubleSpinClick()
      self.hLayoutA2.addWidget(self.bboxLineWidthSpin)
      self.bboxLineWidthSpin.setMinimum(0.0)
      self.bboxLineWidthSpin.setMaximum(100.0)
      self.bboxLineWidthSpin.setValue(self.style['bbox__linewidth'])
      self.bboxLineWidthSpin.setToolTip('Bounding box edge width of extra object')
      self.bboxLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'bbox__linewidth', self.bboxLineWidthSpin))
    
      self.comboBboxLineStyle = QComboBoxMac()
      self.comboBboxLineStyle.setToolTip('Bounding box line style of extra object')
      for entry in self.linestyles:
        self.comboBboxLineStyle.addItem(entry)
      if(self.style['bbox__linestyle'] in self.linestyles):
        currindex = self.linestyles.index(self.style['bbox__linestyle'])
      else:
        currindex = 0
      self.comboBboxLineStyle.setCurrentIndex(currindex)
      self.comboBboxLineStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__linestyle', self.comboBboxLineStyle))
      self.comboBboxLineStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.comboBboxLineStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA2.addWidget(self.comboBboxLineStyle)
      
      # cap style
      self.bboxLineGroup2 = QWidgetMac()
      self.vLayoutA1.addWidget(self.bboxLineGroup2)
      self.hLayoutA22 = QtWidgets.QHBoxLayout(self.bboxLineGroup2)
      self.hLayoutA22.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA22.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA22.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.bboxLineLabel2 = QtWidgets.QLabel('')
      self.bboxLineLabel2.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxLineLabel2.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA22.addWidget(self.bboxLineLabel2)

      self.comboBboxDashStyle = QComboBoxMac()
      self.comboBboxDashStyle.setToolTip('Bounding box cap style of extra object')
      for entry in self.dashstyles:
        self.comboBboxDashStyle.addItem(entry)
      if(self.style['bbox__dash_capstyle'] in self.dashstyles):
        currindex = self.dashstyles.index(self.style['bbox__dash_capstyle'])
      else:
        currindex = 0
      self.comboBboxDashStyle.setCurrentIndex(currindex)
      self.comboBboxDashStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__dash_capstyle', self.comboBboxDashStyle))
      self.comboBboxDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.comboBboxDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA22.addWidget(self.comboBboxDashStyle)      
  
      self.bboxLineColorButton = QPushButtonColor()
      self.bboxLineColorButton.setToolTip('Bounding box edge color of extra object')
      self.bboxLineColorButton.setAutoFillBackground(False)
      colorvalue = [int(i * 255.0) for i in self.style['bbox__edgecolor'][0:3]]
      self.bboxLineColorButton.setMyColor(*colorvalue)
      self.bboxLineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.bboxLineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.bboxLineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.bboxLineColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'bbox__edgecolor'))
      self.hLayoutA22.addWidget(self.bboxLineColorButton)

      # bbox colors
      self.bboxColorGroup = QWidgetMac()
      self.vLayoutA1.addWidget(self.bboxColorGroup)
      self.hLayoutA3 = QtWidgets.QHBoxLayout(self.bboxColorGroup)
      self.hLayoutA3.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.bboxColorLabel = QPushButtonCheckable()
      self.bboxColorLabel.setText('Fill')
      self.bboxColorLabel.setToolTip('Show bounding box fill of extra object')
      self.bboxColorLabel.setChecked(self.style['bbox__doFill'])
      self.bboxColorLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxColorLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxColorLabel.clicked.connect(partial(self.toggleShapeFill, self.targetIndex, 'bbox__doFill', self.bboxColorLabel))
      self.hLayoutA3.addWidget(self.bboxColorLabel)
  
      self.bboxFaceColorButton = QPushButtonColor()
      self.bboxFaceColorButton.setToolTip('Bounding ox fill color of extra object')
      self.bboxFaceColorButton.setAutoFillBackground(False)
      colorvalue = [int(i * 255.0) for i in self.style['bbox__facecolor'][0:3]]
      self.bboxFaceColorButton.setMyColor(*colorvalue)
      self.bboxFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.bboxFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.bboxFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.bboxFaceColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'bbox__facecolor'))
      self.hLayoutA3.addWidget(self.bboxFaceColorButton)    

      # hatch style
      self.hatchGroup = QWidgetMac()
      self.vLayoutA1.addWidget(self.hatchGroup)
      self.hLayoutA35 = QtWidgets.QHBoxLayout(self.hatchGroup)
      self.hLayoutA35.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA35.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA35.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.hatchLineWidthLabel = QtWidgets.QLabel('# line')
      self.hatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA35.addWidget(self.hatchLineWidthLabel)

      self.comboBboxHatch = QComboBoxMac()
      self.comboBboxHatch.setToolTip('Bounding box hatch style of extra object')
      for entry in self.hatchStyles:
        self.comboBboxHatch.addItem(entry)
      if(self.style['bbox__hatch'] in self.hatchStyles):
        currindex = self.hatchStyles.index(self.style['bbox__hatch'])
      else:
        currindex = 0
      self.comboBboxHatch.setCurrentIndex(currindex)
      self.comboBboxHatch.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__hatch', self.comboBboxHatch))
      self.comboBboxHatch.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.comboBboxHatch.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA35.addWidget(self.comboBboxHatch)
      self.comboBboxHatchMultiply = QComboBoxMac()
      self.comboBboxHatchMultiply.setToolTip('Bounding box hatch repeat of extra object')
      for entry in self.hatchMultiplystyles:
        self.comboBboxHatchMultiply.addItem(str(entry))
      if(str(self.style['bbox__hatchMultiply']) in self.hatchMultiplystyles):
        currindex = self.hatchMultiplystyles.index(str(self.style['bbox__hatchMultiply']))
      else:
        currindex = 0
      self.comboBboxHatchMultiply.setCurrentIndex(currindex)
      self.comboBboxHatchMultiply.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__hatchMultiply', self.comboBboxHatchMultiply))
      self.comboBboxHatchMultiply.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.comboBboxHatchMultiply.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA35.addWidget(self.comboBboxHatchMultiply)

      # hatch line width spin box
      self.hatchLineWidthSpin = QDoubleSpinClick()
      self.hLayoutA35.addWidget(self.hatchLineWidthSpin)
      self.hatchLineWidthSpin.setMinimum(0.0)
      self.hatchLineWidthSpin.setMaximum(100.0)
      self.hatchLineWidthSpin.setValue(self.style['bbox__hatch_linewidth'])
      self.hatchLineWidthSpin.setToolTip('Bounding box hatch line width of extra object')
      self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'bbox__hatch_linewidth', self.hatchLineWidthSpin))

      # bbox boxstyle
      self.bboxBoxStyleGroup = QWidgetMac()
      self.vLayoutA1.addWidget(self.bboxBoxStyleGroup)
      self.hLayoutA4 = QtWidgets.QHBoxLayout(self.bboxBoxStyleGroup)
      self.hLayoutA4.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.bboxBoxStyleLabel = QtWidgets.QLabel('Style')
      self.bboxBoxStyleLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxBoxStyleLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA4.addWidget(self.bboxBoxStyleLabel)
  
      self.boxStyles = list(matplotlib.patches.BoxStyle.get_styles().keys())
      self.comboBboxBoxStyle = QComboBoxMac()
      self.comboBboxBoxStyle.setToolTip('Bounding box style of extra object')
      for entry in self.boxStyles:
        self.comboBboxBoxStyle.addItem(entry)
      if(self.style['bbox__boxstyle'] in self.boxStyles):
        currindex = self.boxStyles.index(self.style['bbox__boxstyle'])
      else:
        currindex = 0
      self.comboBboxBoxStyle.setCurrentIndex(currindex)
      self.comboBboxBoxStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'bbox__boxstyle', self.comboBboxBoxStyle))
      self.comboBboxBoxStyle.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      self.comboBboxBoxStyle.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA4.addWidget(self.comboBboxBoxStyle)
      
      # bbox pad
      self.bboxPadGroup = QWidgetMac()
      self.vLayoutA1.addWidget(self.bboxPadGroup)
      self.hLayoutA5 = QtWidgets.QHBoxLayout(self.bboxPadGroup)
      self.hLayoutA5.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.bboxPadLabel = QtWidgets.QLabel('Pad')
      self.bboxPadLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxPadLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA5.addWidget(self.bboxPadLabel)
  
      self.bboxPadEntry = QLineEditClick()
      self.bboxPadEntry.setToolTip('Bounding box padding of extra object')
      self.bboxPadEntry.setText(str(self.style['bbox__pad']))
      self.bboxPadEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxPadEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxPadEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'bbox__pad', self.bboxPadEntry, 0.0, 100.0))
      self.bboxPadEntry.setValidator(self.validFloat)
      self.hLayoutA5.addWidget(self.bboxPadEntry)
  
      # bbox tooth and round
      self.bboxMiscGroup = QWidgetMac()
      self.vLayoutA1.addWidget(self.bboxMiscGroup)
      self.hLayoutA6 = QtWidgets.QHBoxLayout(self.bboxMiscGroup)
      self.hLayoutA6.setContentsMargins(0, 0, 0, 0)
      self.hLayoutA6.setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayoutA6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.bboxToothLabel = QtWidgets.QLabel('Tooth')
      self.bboxToothLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxToothLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA6.addWidget(self.bboxToothLabel)
      self.bboxToothEntry = QLineEditClick()
      self.bboxToothEntry.setToolTip('Bounding box tooth size of extra object')
      self.bboxToothEntry.setText(str(self.style['bbox__tooth_size']))
      self.bboxToothEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxToothEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxToothEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'bbox__tooth_size', self.bboxToothEntry, 0.0, 100.0))
      self.bboxToothEntry.setValidator(self.validFloat)
      self.hLayoutA6.addWidget(self.bboxToothEntry)
  
      self.bboxRoundingLabel = QtWidgets.QLabel('Round')
      self.bboxRoundingLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.bboxRoundingLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
      self.hLayoutA6.addWidget(self.bboxRoundingLabel)
      self.bboxRoundingEntry = QLineEditClick()
      self.bboxRoundingEntry.setToolTip('Bounding box roundedness of extra object')
      self.bboxRoundingEntry.setText(str(self.style['bbox__rounding_size']))
      self.bboxRoundingEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxRoundingEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.bboxRoundingEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'bbox__rounding_size', self.bboxRoundingEntry, 0.0, 100.0))
      self.bboxRoundingEntry.setValidator(self.validFloat)
      self.hLayoutA6.addWidget(self.bboxRoundingEntry)
  
      # annotation arrow menu
      if(self.extrasType == 'annotation'):
        blah = VLine()
        self.hLayout0.addWidget(blah)
        # build gui for label formatting
        self.formatArrow = QWidgetMac()    
        self.vLayoutB1 = QtWidgets.QVBoxLayout(self.formatArrow)
        self.vLayoutB1.setContentsMargins(0, 0, 0, 0)
        self.vLayoutB1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
        self.vLayoutB1.setSpacing(2)
        self.hLayout0.addWidget(self.formatArrow)
        
        # heading
        self.extrasArrowLabel = QtWidgets.QLabel()
        self.extrasArrowLabel.setText('Arrow')
        font = self.extrasArrowLabel.font()
        font.setBold(True)
        self.extrasArrowLabel.setFont(font)
        self.vLayoutB1.addWidget(self.extrasArrowLabel)    
  
        # arrow tip position x
        self.arrowXGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowXGroup)
        self.hLayoutB1 = QtWidgets.QHBoxLayout(self.arrowXGroup)
        self.hLayoutB1.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB1.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.arrowXLabel = QtWidgets.QLabel('x')
        self.arrowXLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowXLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB1.addWidget(self.arrowXLabel)
        
        self.arrowXEntry = QLineEditClickExtra()
        self.arrowXEntry.setFormatString(FORMAT_ARROW_X)
        self.arrowXEntry.setValue(self.style['arrow__x'])
        self.arrowXEntry.setToolTip('Arrow x coordinate of extra object')
        ###self.arrowXEntry.setText(str(self.style['arrow__x']))
        self.arrowXEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowXEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowXEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__x', self.arrowXEntry, None, None))
        self.arrowXEntry.setValidator(self.validFloat)
        self.hLayoutB1.addWidget(self.arrowXEntry)
    
        # label position y
        self.arrowYGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowYGroup)
        self.arrowYLabel = QtWidgets.QLabel('y')
        self.arrowYLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowYLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB1.addWidget(self.arrowYLabel)
    
        self.arrowYEntry = QLineEditClickExtra()
        self.arrowYEntry.setFormatString(FORMAT_ARROW_Y)
        self.arrowYEntry.setValue(self.style['arrow__y'])
        self.arrowYEntry.setToolTip('Arrow y coordinate of extra object')
        ###self.arrowYEntry.setText(str(self.style['arrow__y']))
        self.arrowYEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowYEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowYEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__y', self.arrowYEntry, None, None))
        self.arrowYEntry.setValidator(self.validFloat)
        self.hLayoutB1.addWidget(self.arrowYEntry)
      
        # arrow line style
        self.arrowLineGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowLineGroup)
        self.hLayoutB2 = QtWidgets.QHBoxLayout(self.arrowLineGroup)
        self.hLayoutB2.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB2.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.arrowLineLabel = QtWidgets.QLabel('Line')
        self.arrowLineLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLineLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB2.addWidget(self.arrowLineLabel)

        # arrow edge width spin box
        self.arrowLineWidthSpin = QDoubleSpinClick()
        self.hLayoutB2.addWidget(self.arrowLineWidthSpin)
        self.arrowLineWidthSpin.setMinimum(0.0)
        self.arrowLineWidthSpin.setMaximum(100.0)
        self.arrowLineWidthSpin.setValue(self.style['arrow__linewidth'])
        self.arrowLineWidthSpin.setToolTip('Arrow edge width of extra object')
        self.arrowLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.targetIndex, 'arrow__linewidth', self.arrowLineWidthSpin))
    
        self.comboArrowLineStyle = QComboBoxMac()
        self.comboArrowLineStyle.setToolTip('Arrow line style of extra object')
        for entry in self.linestyles:
          self.comboArrowLineStyle.addItem(entry)
        if(self.style['arrow__linestyle'] in self.linestyles):
          currindex = self.linestyles.index(self.style['arrow__linestyle'])
        else:
          currindex = 0
        self.comboArrowLineStyle.setCurrentIndex(currindex)
        self.comboArrowLineStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'arrow__linestyle', self.comboArrowLineStyle))
        self.comboArrowLineStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.comboArrowLineStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB2.addWidget(self.comboArrowLineStyle)

        self.arrowLineColorButton = QPushButtonColor()
        self.arrowLineColorButton.setToolTip('Arrow edge color of extra object')
        self.arrowLineColorButton.setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.style['arrow__edgecolor'][0:3]]
        self.arrowLineColorButton.setMyColor(*colorvalue)
        self.arrowLineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.arrowLineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.arrowLineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.arrowLineColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'arrow__edgecolor'))
        self.hLayoutB2.addWidget(self.arrowLineColorButton)
    
        # arrow colors
        self.arrowColorGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowColorGroup)
        self.hLayoutB3 = QtWidgets.QHBoxLayout(self.arrowColorGroup)
        self.hLayoutB3.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB3.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.arrowColorLabel = QPushButtonCheckable()
        self.arrowColorLabel.setText('Fill')
        self.arrowColorLabel.setToolTip('Show arrow fill of extra object')
        self.arrowColorLabel.setChecked(self.style['arrow__doFill'])
        self.arrowColorLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowColorLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowColorLabel.clicked.connect(partial(self.toggleShapeFill, self.targetIndex, 'arrow__doFill', self.arrowColorLabel))
        self.hLayoutB3.addWidget(self.arrowColorLabel)

        self.arrowFaceColorButton = QPushButtonColor()
        self.arrowFaceColorButton.setToolTip('Arrow fill color of extra object')
        self.arrowFaceColorButton.setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.style['arrow__facecolor'][0:3]]
        self.arrowFaceColorButton.setMyColor(*colorvalue)
        self.arrowFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.arrowFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.arrowFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.arrowFaceColorButton.clicked.connect(partial(self.changeLabelColor, self.targetIndex, 'arrow__facecolor'))
        self.hLayoutB3.addWidget(self.arrowFaceColorButton)
        
        self.hatchStyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
        self.comboArrowHatch = QComboBoxMac()
        self.comboArrowHatch.setToolTip('Arrow hatch style of extra object')
        for entry in self.hatchStyles:
          self.comboArrowHatch.addItem(entry)
        if(self.style['arrow__hatch'] in self.hatchStyles):
          currindex = self.hatchStyles.index(self.style['arrow__hatch'])
        else:
          currindex = 0
        self.comboArrowHatch.setCurrentIndex(currindex)
        self.comboArrowHatch.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'arrow__hatch', self.comboArrowHatch))
        self.comboArrowHatch.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.comboArrowHatch.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB3.addWidget(self.comboArrowHatch)
        self.comboArrowHatchMultiply = QComboBoxMac()
        self.comboArrowHatchMultiply.setToolTip('Arrow hatch repeat of extra object')
        for entry in self.hatchMultiplystyles:
          self.comboArrowHatchMultiply.addItem(str(entry))
        if(str(self.style['arrow__hatchMultiply']) in self.hatchMultiplystyles):
          currindex = self.hatchMultiplystyles.index(str(self.style['arrow__hatchMultiply']))
        else:
          currindex = 0
        self.comboArrowHatchMultiply.setCurrentIndex(currindex)
        self.comboArrowHatchMultiply.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'arrow__hatchMultiply', self.comboArrowHatchMultiply))
        self.comboArrowHatchMultiply.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.comboArrowHatchMultiply.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB3.addWidget(self.comboArrowHatchMultiply)
      
        # arrow style
        self.arrowStyleGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowStyleGroup)
        self.hLayoutB5 = QtWidgets.QHBoxLayout(self.arrowStyleGroup)
        self.hLayoutB5.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB5.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.arrowStyleLabel = QtWidgets.QLabel('Style')
        self.arrowStyleLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowStyleLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB5.addWidget(self.arrowStyleLabel)
  
        self.arrowStyles = list(matplotlib.patches.ArrowStyle.get_styles().keys())
        self.asymmetricArrowStyles = [i for i in self.arrowStyles if i in [']-[', '-[', ']-', '|-|', 'simple', 'fancy', 'wedge']]
        self.comboArrowStyle = QComboBoxMac()
        self.comboArrowStyle.setToolTip('Arrow style of extra object')
        for entry in self.arrowStyles:
          self.comboArrowStyle.addItem(entry)
        if(self.style['arrow__arrowstyle'] in self.arrowStyles):
          currindex = self.arrowStyles.index(self.style['arrow__arrowstyle'])
        else:
          currindex = 0
        self.comboArrowStyle.setCurrentIndex(currindex)
        self.comboArrowStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'arrow__arrowstyle', self.comboArrowStyle))
        self.comboArrowStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.comboArrowStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB5.addWidget(self.comboArrowStyle)
        
        # connection style
        self.arrowConnectGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowConnectGroup)
        self.hLayoutB6 = QtWidgets.QHBoxLayout(self.arrowConnectGroup)
        self.hLayoutB6.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB6.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.arrowConnectLabel = QtWidgets.QLabel('Connect')
        self.arrowConnectLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowConnectLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB6.addWidget(self.arrowConnectLabel)
  
        self.connectStyles = list(matplotlib.patches.ConnectionStyle.get_styles().keys())
        if(('arc' in self.connectStyles) and ('arc3' in self.connectStyles)):
          self.connectStyles.remove('arc')
        if(('angle' in self.connectStyles) and ('angle3' in self.connectStyles)):
          self.connectStyles.remove('angle')
        self.comboConnectStyle = QComboBoxMac()
        self.comboConnectStyle.setToolTip('Arrow connector style of extra object')
        for entry in self.connectStyles:
          self.comboConnectStyle.addItem(entry)
        if(self.style['arrow__connector'] in self.connectStyles):
          currindex = self.connectStyles.index(self.style['arrow__connector'])
        else:
          currindex = 0
        self.comboConnectStyle.setCurrentIndex(currindex)
        self.comboConnectStyle.activated.connect(partial(self.changeLineStyle, self.targetIndex, 'arrow__connector', self.comboConnectStyle))
        self.comboConnectStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.comboConnectStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB6.addWidget(self.comboConnectStyle)
  
        # arrow shrink
        self.arrowShrinkGroup = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowShrinkGroup)
        self.hLayoutB4 = QtWidgets.QHBoxLayout(self.arrowShrinkGroup)
        self.hLayoutB4.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB4.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.arrowShrinkALabel = QtWidgets.QLabel('ShrinkA')
        self.arrowShrinkALabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowShrinkALabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB4.addWidget(self.arrowShrinkALabel)
  
        self.arrowShrinkAEntry = QLineEditClick()
        self.arrowShrinkAEntry.setToolTip('Arrow shrink factor A of extra object')
        self.arrowShrinkAEntry.setText(str(self.style['arrow__shrinkA']))
        self.arrowShrinkAEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowShrinkAEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowShrinkAEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__shrinkA', self.arrowShrinkAEntry, 0.0, 1000.0))
        self.arrowShrinkAEntry.setValidator(self.validFloat)
        self.hLayoutB4.addWidget(self.arrowShrinkAEntry)
        
        self.arrowShrinkBLabel = QtWidgets.QLabel('ShrinkB')
        self.arrowShrinkBLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowShrinkBLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB4.addWidget(self.arrowShrinkBLabel)
  
        self.arrowShrinkBEntry = QLineEditClick()
        self.arrowShrinkBEntry.setToolTip('Arrow shrink factor B of extra object')
        self.arrowShrinkBEntry.setText(str(self.style['arrow__shrinkB']))
        self.arrowShrinkBEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowShrinkBEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowShrinkBEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__shrinkB', self.arrowShrinkBEntry, 0.0, 1000.0))
        self.arrowShrinkBEntry.setValidator(self.validFloat)
        self.hLayoutB4.addWidget(self.arrowShrinkBEntry)
        
        # arrow configuration encore
        self.arrowParamGroup1 = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowParamGroup1)
        self.hLayoutB7 = QtWidgets.QHBoxLayout(self.arrowParamGroup1)
        self.hLayoutB7.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB7.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB7.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
        self.arrowLengthALabel = QtWidgets.QLabel('LengthA')
        self.arrowLengthALabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLengthALabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB7.addWidget(self.arrowLengthALabel)
        self.arrowLengthAEntry = QLineEditClick()
        self.arrowLengthAEntry.setToolTip('Arrow length A of extra object')
        self.arrowLengthAEntry.setText(str(self.style['arrow__lengthA']))
        self.arrowLengthAEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLengthAEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLengthAEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__lengthA', self.arrowLengthAEntry, 0.0, 500.0))
        self.arrowLengthAEntry.setValidator(self.validFloat)
        self.hLayoutB7.addWidget(self.arrowLengthAEntry)
  
        self.arrowLengthBLabel = QtWidgets.QLabel('LengthB')
        self.arrowLengthBLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLengthBLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB7.addWidget(self.arrowLengthBLabel)
        self.arrowLengthBEntry = QLineEditClick()
        self.arrowLengthBEntry.setToolTip('Arrow length B of extra object')
        self.arrowLengthBEntry.setText(str(self.style['arrow__lengthB']))
        self.arrowLengthBEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLengthBEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowLengthBEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__lengthB', self.arrowLengthBEntry, 0.0, 500.0))
        self.arrowLengthBEntry.setValidator(self.validFloat)
        self.hLayoutB7.addWidget(self.arrowLengthBEntry)
  
        self.arrowParamGroup2 = QWidgetMac()
        self.vLayoutB1.addWidget(self.arrowParamGroup2)
        self.hLayoutB8 = QtWidgets.QHBoxLayout(self.arrowParamGroup2)
        self.hLayoutB8.setContentsMargins(0, 0, 0, 0)
        self.hLayoutB8.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayoutB8.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
        self.arrowWidthALabel = QtWidgets.QLabel('WidthA')
        self.arrowWidthALabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowWidthALabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB8.addWidget(self.arrowWidthALabel)
        self.arrowWidthAEntry = QLineEditClick()
        self.arrowWidthAEntry.setToolTip('Arrow width A of extra object')
        self.arrowWidthAEntry.setText(str(self.style['arrow__widthA']))
        self.arrowWidthAEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowWidthAEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowWidthAEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__widthA', self.arrowWidthAEntry, 0.0, 500.0))
        self.arrowWidthAEntry.setValidator(self.validFloat)
        self.hLayoutB8.addWidget(self.arrowWidthAEntry)
  
        self.arrowWidthBLabel = QtWidgets.QLabel('WidthB')
        self.arrowWidthBLabel.setMaximumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.arrowWidthBLabel.setMinimumSize(QtCore.QSize(int(45 * SCALEFONT), int(BASE_SIZE)))
        self.hLayoutB8.addWidget(self.arrowWidthBLabel)
        self.arrowWidthBEntry = QLineEditClick()
        self.arrowWidthBEntry.setToolTip('Arrow width B of extra object')
        self.arrowWidthBEntry.setText(str(self.style['arrow__widthB']))
        self.arrowWidthBEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowWidthBEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.arrowWidthBEntry.editingFinished.connect(partial(self.changeStyle, self.targetIndex, 'arrow__widthB', self.arrowWidthBEntry, 0.0, 500.0))
        self.arrowWidthBEntry.setValidator(self.validFloat)
        self.hLayoutB8.addWidget(self.arrowWidthBEntry)
        
        # update arrow selection boxes
        self.toggleArrowStyles()
        
      # finally update display of bbox config
      self.toggleBbox(self.targetIndex)
    
    self.setFocus()
    self.focusNextChild()

  def resetTransform(self, targetIndex=None):
    # resets all transform operations on an area object
    if(targetIndex != None):
      self.parent.parent.extras[targetIndex].resetTransform()
      self.close()
  
  def toggleArrowStyles(self):
    # (de)activates config dialogues as required based on current arrow style
    if(self.style['arrow__arrowstyle'] in self.asymmetricArrowStyles):
      state = True
    else:
      state = False
      
    items = [self.arrowLengthAEntry, self.arrowWidthAEntry]
    for item in items:
      item.setEnabled(state)

  def toggleBbox(self, targetIndex=None):
    # toggles display of bbox
    if(targetIndex != None):
      state = self.bboxShowLabel.isChecked()
      self.style['bbox__show'] = state
      self.parent.parent.extras[targetIndex].setStyle('bbox__show', state, redraw=True)
      # toggle display of bbox config menu
      if(state):
        self.formatBbox.show()
        self.divider.show()
      else:
        self.formatBbox.hide()
        self.divider.hide()
      self.upperRow.adjustSize()
      self.adjustSize()

  def changeLabelAlignment(self, targetIndex=None):
    if(targetIndex != None):
      useAlignment = str(self.configAlignment.currentText())
      self.parent.parent.extras[targetIndex].setStyle('horizontalalignment', useAlignment, redraw=True)
    
  def changeLabelAlignmentVertical(self, targetIndex=None):
    if(targetIndex != None):
      useAlignment = str(self.configAlignmentVertical.currentText())
      self.parent.parent.extras[targetIndex].setStyle('verticalalignment', useAlignment, redraw=True)
    
  def changeLabelFont(self, targetIndex=None):
    if(targetIndex != None):
      useFont = str(self.configLabelFont.currentText())
      self.parent.parent.extras[targetIndex].setStyle('fontname', useFont, redraw=True)
    
  def changeLabelFontStyle(self, targetIndex=None, key=None, entryfield=None):
    if((targetIndex != None) and (key in ['fontweight', 'fontstyle']) and (entryfield != None)):
      value = 'normal'
      isChecked = entryfield.isChecked()
      if(key == 'fontweight'):
        if(isChecked):
          value = 'bold'
      else:
        if(isChecked):
          value = 'italic'
      self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

  def changeLabelColor(self, targetIndex=None, key=None):
    # colors the text element
    if((targetIndex != None) and (key in self.style)):
      # prevent QMenu from vanishing
      self.togglePersist(True)
      # get current color
      prevColor = [int(255 * i) for i in self.style[key]]
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'color'):
          self.configColorLabelButton.setMyColor(*value)
        elif(key == 'arrow__facecolor'):
          self.arrowFaceColorButton.setMyColor(*value)
        elif(key == 'arrow__edgecolor'):
          self.arrowLineColorButton.setMyColor(*value)
        elif(key == 'bbox__facecolor'):
          if(self.extrasType == 'area'):
            self.faceColorButton.setMyColor(*value)
          else:
            self.bboxFaceColorButton.setMyColor(*value)
        elif(key == 'bbox__edgecolor'):
          if(self.extrasType == 'area'):
            self.lineColorButton.setMyColor(*value)
          else:
            self.bboxLineColorButton.setMyColor(*value)
        elif(key == 'shape__facecolor'):
          self.shapeFaceColorButton.setMyColor(*value)
        elif(key == 'line__color'):
          self.lineColorButton.setMyColor(*value)
        elif(key == 'line__gapcolor'):
          self.lineColorButton2.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.style[key] = value
        self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

      # bring back the QMenu
      self.togglePersist(False)

  def alterStyle(self, targetIndex=None, key=None, entryfield=None):
    if((targetIndex != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if(key in self.style):
        self.style[key] = value
        self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

  def changeStyle(self, targetIndex=None, key=None, entryfield=None, minval=0, maxval=1):
    if((targetIndex != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      if(maxval != None):
        value = np.min((value, maxval))
      if(minval != None):
        value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      if(key in self.style):
        self.style[key] = value
        self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

  def changeSpokeNumber(self, targetIndex=None, key=None, entryfield=None):
    if((targetIndex != None) and (key != None)):
      # get current value
      value = entryfield.value()
      if(key in self.style):
        self.style[key] = value
        self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

  def changeLineStyle(self, targetIndex=None, key=None, entryfield=None, minval=0, maxval=1):
    if((targetIndex != None) and (key != None)):
      # check paramter boundaries
      value = str(entryfield.currentText())
      if(key in self.style):
        if('__hatchMultiply' in key):
          value = int(value)
        self.style[key] = value
        if(key == 'line__dash_capstyle'):
          self.parent.parent.extras[targetIndex].setStyle('line__solid_capstyle', value, redraw=False)
        self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)
        
      # update config menu?
      if(key == 'shape__type'):
        if(self.style['shape__type'] in ['poly', 'star']):
          self.spokeNumberSpinBox.show()
        else:
          self.spokeNumberSpinBox.hide()
        #
        if(self.style['shape__type'] == 'star'):
          self.spikySpokeGroup.show()
        else:
          self.spikySpokeGroup.hide()
        #
        if(self.style['shape__type'] == 'torus'):
          self.innerRadiusGroup.show()
        else:
          self.innerRadiusGroup.hide()
        #
        if(self.style['shape__type'] == 'arrow'):
          self.arrowAspectGroup.show()
        else:
          self.arrowAspectGroup.hide()
        self.formatLine.adjustSize()
        self.adjustSize()

      # update arrow selection boxes
      if(key == 'arrow__arrowstyle'):
        self.toggleArrowStyles()
        
  def alterZ(self, index, direction):
    field = None
    if(hasattr(self.parent.parent.objectsarea, 'extrasTable')):
      field = self.parent.parent.objectsarea.extrasTable.cellWidget(index, 1)
    if(field != None):
      value, minimum, maximum = field.value(), field.minimum(), field.maximum()
      newValue = value + direction
      newValue = max(minimum, newValue)
      newValue = min(maximum, newValue)
      # set new value
      if(newValue != value):
        field.setValue(newValue)
        # mysteriously, we have to formally trigger the event
        # recall that it was automatic in previous PyQt versions
        field.editingFinished.emit()
      # (in)activate control buttons
      if(newValue == minimum):
        self.zDownButton.setEnabled(False)
      else:
        self.zDownButton.setEnabled(True)
      if(newValue == maximum):
        self.zUpButton.setEnabled(False)
      else:
        self.zUpButton.setEnabled(True)

  def setField(self, index):
    text = self.entryField.text()
    if(hasattr(self.parent.parent.objectsarea, 'extrasTable')):
      field = self.parent.parent.objectsarea.extrasTable.cellWidget(index, 2)
      field.setText(text)
      field.editingFinished.emit()

  def toggleFreehandClose(self, targetIndex=None):
    # toggles whether freehand shape is closed
    if(targetIndex != None):
      value = self.closeFreehandButton.isChecked()
      key = 'shape__closed'
      self.style[key] = value
      self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

  def toggleFreehandReduce(self, targetIndex=None):
    # activates reduction of vertex no. in freehand object
    if(targetIndex != None):
      self.parent.parent.extras[targetIndex].simplifyMe(redraw=True)
      self.configReductionCount.setText(str(len(self.parent.parent.extras[targetIndex].shape__vertices)))
      
  def toggleShapeFill(self, targetIndex=None, key=None, entryfield=None):
    # toggles whether shape elements are filled or not
    if((targetIndex != None) and (key != None)):
      value = entryfield.isChecked()
      self.style[key] = value
      self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

  def toggleDoGap(self, targetIndex=None, key=None, entryfield=None):
    # toggles whether to draw gapcolor for line
    if((targetIndex != None) and (key != None)):
      value = entryfield.isChecked()
      self.style[key] = value
      self.parent.parent.extras[targetIndex].setStyle(key, value, redraw=True)

class violinBoxMenu(QWidgetMac):
  def __init__(self, parent=None, target=None):
    super(violinBoxMenu, self).__init__(parent)
    self.parent = parent
    self.target = target
    
    # set up mode
    getStyle = self.target.getViolinStyle()
    if('mode' in getStyle):
      self.mode = getStyle['mode']
    else:
      self.mode = 0

    # set up GUI
    self.buildRessource()

  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)

    self.topContainer = QWidgetMac(self)
    self.vLayout.addWidget(self.topContainer)
    self.hLayout = QtWidgets.QHBoxLayout(self.topContainer)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hLayout.setSpacing(2)
    
    # the main violin/box plot selector
    self.violinSelectorGroupBox = QtWidgets.QGroupBox()
    self.violinSelectorGroupBox.setMinimumHeight(int(BASE_SIZE + 4))
    self.violinSelectorGroupBox.setMaximumHeight(int(BASE_SIZE + 4))
    self.hLayout.addWidget(self.violinSelectorGroupBox)
    self.violinSelectorLayout = QtWidgets.QHBoxLayout(self.violinSelectorGroupBox)
    self.violinSelectorLayout.setContentsMargins(1, 0, 0, 0)

    self.violinSelectorGroup = QtWidgets.QButtonGroup()
    self.violinSelectorButtons = []
    self.violinSelectorButtons.append(QRadioButtonToolTip())
    self.violinSelectorButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), 2, int(44 * SCALEFONT), int(BASE_SIZE)))
    self.violinSelectorButtons[-1].setChecked(self.mode == 0)
    self.violinSelectorButtons[-1].toggled.connect(partial(self.toggleViolin, 0))
    self.violinSelectorButtons[-1].setText('none')  

    self.violinSelectorButtons.append(QRadioButtonToolTip())
    self.violinSelectorButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), 2, int(100 * SCALEFONT), int(BASE_SIZE)))
    self.violinSelectorButtons[-1].setChecked(self.mode == 1)
    self.violinSelectorButtons[-1].toggled.connect(partial(self.toggleViolin, 1))
    self.violinSelectorButtons[-1].setText('violin')
    
    self.violinSelectorButtons.append(QRadioButtonToolTip())
    self.violinSelectorButtons[-1].setGeometry(QtCore.QRect(int(2 * SCALEFONT), 2, int(100 * SCALEFONT), int(BASE_SIZE)))
    self.violinSelectorButtons[-1].setChecked(self.mode == 2)
    self.violinSelectorButtons[-1].toggled.connect(partial(self.toggleViolin, 2))
    self.violinSelectorButtons[-1].setText('box')
    
    for entry in self.violinSelectorButtons:
      self.violinSelectorLayout.addWidget(entry)
      self.violinSelectorGroup.addButton(entry)
    self.hLayout.addStretch()
     
    # put the menus
    self.violinContainer = QWidgetMac(self)
    self.vLayout.addWidget(self.violinContainer)
    self.vLayout1 = QtWidgets.QVBoxLayout(self.violinContainer)
    self.vLayout1.setContentsMargins(0, 0, 0, 0)
    self.vLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout1.setSpacing(0)
    self.violinStyleMenu = violinStyleMenu(self.parent, self.target)
    self.vLayout1.addWidget(self.violinStyleMenu)
    
    self.boxContainer = QWidgetMac(self)
    self.vLayout.addWidget(self.boxContainer)
    self.vLayout2 = QtWidgets.QVBoxLayout(self.boxContainer)
    self.vLayout2.setContentsMargins(0, 0, 0, 0)
    self.vLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout2.setSpacing(0)
    self.boxStyleMenu = boxStyleMenu(self.parent, self.target)
    self.vLayout2.addWidget(self.boxStyleMenu)
    
    # initialize menus
    self.setMenuDisplay()
    
  def setMenuDisplay(self):
    # sets display of menus
    if(self.mode == 1):
      self.violinContainer.show()
    else:
      self.violinContainer.hide()
    if(self.mode == 2):
      self.boxContainer.show()
    else:
      self.boxContainer.hide()
      
    # resize to contents
    start = self.geometry().height()
    self.adjustSize()
    # need to trigger the size animation in the parent object
    self.parent.toggleDisplay(button=self.parent.displayViolinButton, target=self.parent.violinRow, flag='displayViolinConfig', start=start)
    self.parent.adjustSize()

  def toggleViolin(self, mode=0):
    # toggles on/off violin plot
    # only trigger event for button that was turned on
    if(self.violinSelectorButtons[mode].isChecked()):
      self.mode = mode
      self.setMenuDisplay()
        
      if(self.target != None):
        self.target.toggleViolin(mode, redraw=True)

class violinStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None):
    super(violinStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target

    # float validator
    self.validFloat = MyValidFloat()

    # valid line styles
    self.linestyles = ['None', '-', '--', '-.', ':']
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.dashstyles = ['butt', 'round', 'projecting']
    self.hatchstyles = ['None', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*'] # unfortunately none is not interpreted correctly
    self.hatchstyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    self.hatchMultiplystyles = [str(i) for i in range(1, 11)]
    self.typestyles = ['full', 'left', 'right']
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      getStyle = self.target.getViolinStyle()

      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        if(type(getStyle[entry]) == type({})):
          # have to deepcopy inner dicts to prevent inadvertent overwriting of values
          self.style[entry] = deepcopy(getStyle[entry])
        else:
          self.style[entry] = getStyle[entry]
    
    # set default values
    defaultStyle = {}
    defaultStyle['mode'] = 0
    defaultStyle['bodies'] = {'type': 'full', 'facecolor': [0.8, 0.8, 0.9, 1.0], 'edgecolor': [0.2, 0.2, 0.2, 1.0], 'linestyle': 'solid', 'linewidth': 1.0,\
                'capstyle': 'butt', 'hatch': '', 'hatchMultiply': 1, 'hatch_linewidth': 1., 'visible': True, 'width': 0.5, 'doFill': True}
    defaultStyle['cmaxes'] = {'linestyle': 'solid', 'linewidth': 1.0, 'color': [0.2, 0.2, 0.2, 1.0], 'capstyle': 'butt', 'visible': True}
    for slaved in ['cbars', 'cmeans']:
      defaultStyle[slaved] = {}
      defaultStyle[slaved].update(defaultStyle['cmaxes'])
    defaultStyle['cmedians'] = {'visible': False}
    defaultStyle['cmins'] = {'visible': True}
    for entry in defaultStyle:
      if(not (entry in self.style)):
        self.style[entry] = defaultStyle[entry]
      elif(type(self.style[entry]) is dict):
        # update sub styles to avoid undefined settings
        defaultStyle[entry].update(self.style[entry])
        self.style[entry] = defaultStyle[entry]
    
    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.hLayout = QtWidgets.QHBoxLayout(self)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.hLayout.setSpacing(2)
    
    # bodies
    self.bodiesContainer = QWidgetMac(self)
    self.hLayout.addWidget(self.bodiesContainer)
    self.vLayout = QtWidgets.QVBoxLayout(self.bodiesContainer)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
      
    self.displayBodiesGroup = QWidgetMac()
    self.vLayout.addWidget(self.displayBodiesGroup)
    self.hLayout0 = QtWidgets.QHBoxLayout(self.displayBodiesGroup)
    self.hLayout0.setContentsMargins(0, 0, 0, 0)
    self.hLayout0.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesStyleLabel = QtWidgets.QLabel()
    self.bodiesStyleLabel.setText('Bodies')
    font = self.bodiesStyleLabel.font()
    font.setBold(True)
    self.bodiesStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesStyleLabel.setFont(font)
    self.hLayout0.addWidget(self.bodiesStyleLabel)    
    
    # display bodies?
    self.displayBodiesLabel = QPushButtonCheckable()
    self.displayBodiesLabel.setText('Show?')
    self.displayBodiesLabel.setToolTip('Show violin bodies of object')
    self.displayBodiesLabel.setChecked(self.style['bodies']['visible'])
    self.displayBodiesLabel.clicked.connect(partial(self.setDisplay, self.target, 'bodies', self.displayBodiesLabel))
    self.displayBodiesLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayBodiesLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout0.addWidget(self.displayBodiesLabel)

    # controls for split violin plots
    self.bodiesTypeGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesTypeGroup)
    self.hLayout05 = QtWidgets.QHBoxLayout(self.bodiesTypeGroup)
    self.hLayout05.setContentsMargins(0, 0, 0, 0)
    self.hLayout05.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout05.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesTypeLabel = QtWidgets.QLabel('Type')
    self.bodiesTypeLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesTypeLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout05.addWidget(self.bodiesTypeLabel)
    self.bodiesTypeStyle = QComboBoxMac()
    self.bodiesTypeStyle.setToolTip('Violin bodies type of object')
    for entry in self.typestyles:
      self.bodiesTypeStyle.addItem(entry)
    if(('type' in self.style['bodies']) and (self.style['bodies']['type'] in self.typestyles)):
      currindex = self.typestyles.index(self.style['bodies']['type'])
    else:
      currindex = 0
    self.bodiesTypeStyle.setCurrentIndex(currindex)
    self.bodiesTypeStyle.activated.connect(partial(self.selectStyle, self.target, 'bodies', 'type', self.bodiesTypeStyle))
    self.bodiesTypeStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesTypeStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout05.addWidget(self.bodiesTypeStyle)

    # line width
    self.bodiesLineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesLineWidthGroup)
    self.hLayout1 = QtWidgets.QHBoxLayout(self.bodiesLineWidthGroup)
    self.hLayout1.setContentsMargins(0, 0, 0, 0)
    self.hLayout1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hLayout1.setSpacing(int(-2 + 8 * SCALEFONT))
    self.bodiesLineWidthLabel = QtWidgets.QLabel('Width')
    self.bodiesLineWidthLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesLineWidthLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout1.addWidget(self.bodiesLineWidthLabel)
    # line width spin box
    self.bodiesLineWidthSpin = QDoubleSpinClick()
    self.hLayout1.addWidget(self.bodiesLineWidthSpin)
    self.bodiesLineWidthSpin.setMinimum(0.0)
    self.bodiesLineWidthSpin.setMaximum(100.0)
    self.bodiesLineWidthSpin.setValue(self.style['bodies']['linewidth'])
    self.bodiesLineWidthSpin.setToolTip('Violin bodies line width of object')
    self.bodiesLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'bodies', 'linewidth', self.bodiesLineWidthSpin))
    
    # line color
    self.bodiesLineColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesLineColorGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.bodiesLineColorGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesLineColorLabel = QtWidgets.QLabel('Color')
    self.bodiesLineColorLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesLineColorLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.bodiesLineColorLabel)
      
    self.bodiesLineColorButton = QPushButtonColor()
    self.bodiesLineColorButton.setToolTip('Violin bodies line color of object')
    self.bodiesLineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['bodies']['edgecolor'][0:3]]
    self.bodiesLineColorButton.setMyColor(*colorvalue)
    self.bodiesLineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.bodiesLineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.bodiesLineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.bodiesLineColorButton.clicked.connect(partial(self.setColor, self.target, 'bodies', 'edgecolor'))
    self.hLayout2.addWidget(self.bodiesLineColorButton)
      
    # line style
    self.bodiesLineStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesLineStyleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.bodiesLineStyleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesLineStyleLabel = QtWidgets.QLabel('Style')
    self.bodiesLineStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesLineStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.bodiesLineStyleLabel)
    self.bodiesComboStyle = QComboBoxMac()
    self.bodiesComboStyle.setToolTip('Violin bodies line style of object')
    for entry in self.linestyles:
      self.bodiesComboStyle.addItem(entry)
    if(self.style['bodies']['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['bodies']['linestyle'])
    else:
      currindex = 0
    self.bodiesComboStyle.setCurrentIndex(currindex)
    self.bodiesComboStyle.activated.connect(partial(self.selectStyle, self.target, 'bodies', 'linestyle', self.bodiesComboStyle))
    self.bodiesComboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesComboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.bodiesComboStyle)

    # cap style
    self.bodiesLineDashStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesLineDashStyleGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.bodiesLineDashStyleGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesLineDashStyleLabel = QtWidgets.QLabel('Cap')
    self.bodiesLineDashStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesLineDashStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.bodiesLineDashStyleLabel)
    self.bodiesComboDashStyle = QComboBoxMac()
    self.bodiesComboDashStyle.setToolTip('Violin bodies cap style of object')
    for entry in self.dashstyles:
      self.bodiesComboDashStyle.addItem(entry)
    if(self.style['bodies']['capstyle'] in self.dashstyles):
      currindex = self.dashstyles.index(self.style['bodies']['capstyle'])
    else:
      currindex = 0
    self.bodiesComboDashStyle.setCurrentIndex(currindex)
    self.bodiesComboDashStyle.activated.connect(partial(self.selectStyle, self.target, 'bodies', 'capstyle', self.bodiesComboDashStyle))
    self.bodiesComboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesComboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.bodiesComboDashStyle)

    # fill color
    self.bodiesFillColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesFillColorGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.bodiesFillColorGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesFillColorLabel = QPushButtonCheckable()
    self.bodiesFillColorLabel.setText('Fill')
    self.bodiesFillColorLabel.setToolTip('Show violin bodies fill of object')
    self.bodiesFillColorLabel.setChecked(self.style['bodies']['doFill'])
    self.bodiesFillColorLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesFillColorLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesFillColorLabel.clicked.connect(partial(self.toggleBodyFill, self.target, self.bodiesFillColorLabel))
    self.hLayout5.addWidget(self.bodiesFillColorLabel)
      
    self.bodiesFillColorButton = QPushButtonColor()
    self.bodiesFillColorButton.setToolTip('Violin bodies fill color of object')
    self.bodiesFillColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['bodies']['facecolor'][0:3]]
    self.bodiesFillColorButton.setMyColor(*colorvalue)
    self.bodiesFillColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.bodiesFillColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.bodiesFillColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.bodiesFillColorButton.clicked.connect(partial(self.setColor, self.target, 'bodies', 'facecolor'))
    self.hLayout5.addWidget(self.bodiesFillColorButton)
      
    # hatch style
    self.bodiesHatchStyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesHatchStyleGroup)
    self.hLayout6 = QtWidgets.QHBoxLayout(self.bodiesHatchStyleGroup)
    self.hLayout6.setContentsMargins(0, 0, 0, 0)
    self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesHatchStyleLabel = QtWidgets.QLabel('Hatch')
    self.bodiesHatchStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesHatchStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.bodiesHatchStyleLabel)
    self.bodiesComboHatchStyle = QComboBoxMac()
    self.bodiesComboHatchStyle.setToolTip('Violin bodies hatch style of object')
    for entry in self.hatchstyles:
      self.bodiesComboHatchStyle.addItem(entry)
    if(self.style['bodies']['hatch'] in self.hatchstyles):
      currindex = self.hatchstyles.index(self.style['bodies']['hatch'])
    else:
      currindex = 0
    self.bodiesComboHatchStyle.setCurrentIndex(currindex)
    self.bodiesComboHatchStyle.activated.connect(partial(self.selectStyle, self.target, 'bodies', 'hatch', self.bodiesComboHatchStyle))
    self.bodiesComboHatchStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesComboHatchStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.bodiesComboHatchStyle)
    self.bodiesComboHatchMultiplyStyle = QComboBoxMac()
    self.bodiesComboHatchMultiplyStyle.setToolTip('Violin bodies hatch repeat of object')
    for entry in self.hatchMultiplystyles:
      self.bodiesComboHatchMultiplyStyle.addItem(str(entry))
    if(str(self.style['bodies']['hatchMultiply']) in self.hatchMultiplystyles):
      currindex = self.hatchMultiplystyles.index(str(self.style['bodies']['hatchMultiply']))
    else:
      currindex = 0
    self.bodiesComboHatchMultiplyStyle.setCurrentIndex(currindex)
    self.bodiesComboHatchMultiplyStyle.activated.connect(partial(self.selectStyle, self.target, 'bodies', 'hatchMultiply', self.bodiesComboHatchMultiplyStyle))
    self.bodiesComboHatchMultiplyStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesComboHatchMultiplyStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.bodiesComboHatchMultiplyStyle)

    # hatch second row
    self.hatchLineWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.hatchLineWidthGroup)
    self.hLayout_h = QtWidgets.QHBoxLayout(self.hatchLineWidthGroup)
    self.hLayout_h.setContentsMargins(0, 0, 0, 0)
    self.hLayout_h.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout_h.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.hatchLineWidthLabel = QtWidgets.QLabel(' # line')
    self.hatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout_h.addWidget(self.hatchLineWidthLabel)
    # hatch line width spin box
    self.hatchLineWidthSpin = QDoubleSpinClick()
    self.hLayout_h.addWidget(self.hatchLineWidthSpin)
    self.hatchLineWidthSpin.setMinimum(0.0)
    self.hatchLineWidthSpin.setMaximum(100.0)
    self.hatchLineWidthSpin.setValue(self.style['bodies']['hatch_linewidth'])
    self.hatchLineWidthSpin.setToolTip('Violin bodies hatch line width of object')
    self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'bodies', 'hatch_linewidth', self.hatchLineWidthSpin))
    
    # body width
    self.bodiesWidthGroup = QWidgetMac()
    self.vLayout.addWidget(self.bodiesWidthGroup)
    self.hLayout05 = QtWidgets.QHBoxLayout(self.bodiesWidthGroup)
    self.hLayout05.setContentsMargins(0, 0, 0, 0)
    self.hLayout05.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout05.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bodiesWidthLabel = QtWidgets.QLabel('BWid')
    self.bodiesWidthLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesWidthLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout05.addWidget(self.bodiesWidthLabel)
    self.bodiesWidthEntry = QLineEditClick()
    self.bodiesWidthEntry.setToolTip('Violin bodies width of object')
    self.bodiesWidthEntry.setText(str(self.style['bodies']['width']))
    self.bodiesWidthEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesWidthEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.bodiesWidthEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'bodies', 'width', self.bodiesWidthEntry, 0.0, 100.0))
    self.bodiesWidthEntry.setValidator(self.validFloat)
    self.hLayout05.addWidget(self.bodiesWidthEntry)
    
    # cmaxes/cmins
    blah = VLine()
    self.hLayout.addWidget(blah)

    self.cmaxesContainer = QWidgetMac(self)
    self.hLayout.addWidget(self.cmaxesContainer)
    self.vLayout2 = QtWidgets.QVBoxLayout(self.cmaxesContainer)
    self.vLayout2.setContentsMargins(0, 0, 0, 0)
    self.vLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout2.setSpacing(2)
      
    self.displayCmaxesGroup = QWidgetMac()
    self.vLayout2.addWidget(self.displayCmaxesGroup)
    self.hLayout8 = QtWidgets.QHBoxLayout(self.displayCmaxesGroup)
    self.hLayout8.setContentsMargins(0, 0, 0, 0)
    self.hLayout8.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout8.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmaxesStyleLabel = QtWidgets.QLabel()
    # for time being control min and max lines jointly
    self.cmaxesStyleLabel.setText('Min/Max')
    font = self.cmaxesStyleLabel.font()
    font.setBold(True)
    self.cmaxesStyleLabel.setFont(font)
    self.hLayout8.addWidget(self.cmaxesStyleLabel)    
    
    # display min/max?
    self.displayCmaxesCheck = QPushButtonCheckable()
    self.displayCmaxesCheck.setText('S?')
    self.displayCmaxesCheck.setToolTip('Show violin minima of object')
    self.displayCmaxesCheck.setChecked(self.style['cmins']['visible'])
    self.displayCmaxesCheck.clicked.connect(partial(self.setDisplay, self.target, 'cmins', self.displayCmaxesCheck))
    self.displayCmaxesCheck.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmaxesCheck.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout8.addWidget(self.displayCmaxesCheck)
    self.displayCmaxesCheck2 = QPushButtonCheckable()
    self.displayCmaxesCheck2.setToolTip('Show violin maxima of object')
    self.displayCmaxesCheck2.setText('S?')
    self.displayCmaxesCheck2.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmaxesCheck2.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmaxesCheck2.setChecked(self.style['cmaxes']['visible'])
    self.displayCmaxesCheck2.clicked.connect(partial(self.setDisplay, self.target, 'cmaxes', self.displayCmaxesCheck2))
    self.hLayout8.addWidget(self.displayCmaxesCheck2)
    
    # line width
    self.cmaxesLineWidthGroup = QWidgetMac()
    self.vLayout2.addWidget(self.cmaxesLineWidthGroup)
    self.hLayout9 = QtWidgets.QHBoxLayout(self.cmaxesLineWidthGroup)
    self.hLayout9.setContentsMargins(0, 0, 0, 0)
    self.hLayout9.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout9.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmaxesLineWidthLabel = QtWidgets.QLabel('Width')
    self.cmaxesLineWidthLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesLineWidthLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout9.addWidget(self.cmaxesLineWidthLabel)
    # line width spin box
    self.cmaxesLineWidthSpin = QDoubleSpinClick()
    self.hLayout9.addWidget(self.cmaxesLineWidthSpin)
    self.cmaxesLineWidthSpin.setMinimum(0.0)
    self.cmaxesLineWidthSpin.setMaximum(100.0)
    self.cmaxesLineWidthSpin.setValue(self.style['cmaxes']['linewidth'])
    self.cmaxesLineWidthSpin.setToolTip('Violin min/max line width of object')
    self.cmaxesLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'cmaxes', 'linewidth', self.cmaxesLineWidthSpin))
    
    # line color
    self.cmaxesLineColorGroup = QWidgetMac()
    self.vLayout2.addWidget(self.cmaxesLineColorGroup)
    self.hLayout10 = QtWidgets.QHBoxLayout(self.cmaxesLineColorGroup)
    self.hLayout10.setContentsMargins(0, 0, 0, 0)
    self.hLayout10.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout10.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmaxesLineColorLabel = QtWidgets.QLabel('Color')
    self.cmaxesLineColorLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesLineColorLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout10.addWidget(self.cmaxesLineColorLabel)
      
    self.cmaxesLineColorButton = QPushButtonColor()
    self.cmaxesLineColorButton.setToolTip('Violin min/max line color of object')
    self.cmaxesLineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['cmaxes']['color'][0:3]]
    self.cmaxesLineColorButton.setMyColor(*colorvalue)
    self.cmaxesLineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.cmaxesLineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.cmaxesLineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.cmaxesLineColorButton.clicked.connect(partial(self.setColor, self.target, 'cmaxes', 'color'))
    self.hLayout10.addWidget(self.cmaxesLineColorButton)
      
    # line style
    self.cmaxesLineStyleGroup = QWidgetMac()
    self.vLayout2.addWidget(self.cmaxesLineStyleGroup)
    self.hLayout11 = QtWidgets.QHBoxLayout(self.cmaxesLineStyleGroup)
    self.hLayout11.setContentsMargins(0, 0, 0, 0)
    self.hLayout11.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout11.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmaxesLineStyleLabel = QtWidgets.QLabel('Style')
    self.cmaxesLineStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesLineStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout11.addWidget(self.cmaxesLineStyleLabel)
    self.cmaxesComboStyle = QComboBoxMac()
    self.cmaxesComboStyle.setToolTip('Violin min/max line style of object')
    for entry in self.linestyles:
      self.cmaxesComboStyle.addItem(entry)
    if(self.style['cmaxes']['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['cmaxes']['linestyle'])
    else:
      currindex = 0
    self.cmaxesComboStyle.setCurrentIndex(currindex)
    self.cmaxesComboStyle.activated.connect(partial(self.selectStyle, self.target, 'cmaxes', 'linestyle', self.cmaxesComboStyle))
    self.cmaxesComboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesComboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout11.addWidget(self.cmaxesComboStyle)

    # cap style
    self.cmaxesLineDashStyleGroup = QWidgetMac()
    self.vLayout2.addWidget(self.cmaxesLineDashStyleGroup)
    self.hLayout12 = QtWidgets.QHBoxLayout(self.cmaxesLineDashStyleGroup)
    self.hLayout12.setContentsMargins(0, 0, 0, 0)
    self.hLayout12.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout12.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmaxesLineDashStyleLabel = QtWidgets.QLabel('Cap')
    self.cmaxesLineDashStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesLineDashStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout12.addWidget(self.cmaxesLineDashStyleLabel)
    self.cmaxesComboDashStyle = QComboBoxMac()
    self.cmaxesComboDashStyle.setToolTip('Violin min/max cap style of object')
    for entry in self.dashstyles:
      self.cmaxesComboDashStyle.addItem(entry)
    if(self.style['cmaxes']['capstyle'] in self.dashstyles):
      currindex = self.dashstyles.index(self.style['cmaxes']['capstyle'])
    else:
      currindex = 0
    self.cmaxesComboDashStyle.setCurrentIndex(currindex)
    self.cmaxesComboDashStyle.activated.connect(partial(self.selectStyle, self.target, 'cmaxes', 'capstyle', self.cmaxesComboDashStyle))
    self.cmaxesComboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.cmaxesComboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout12.addWidget(self.cmaxesComboDashStyle)

    # cbars
    blah = VLine()
    self.hLayout.addWidget(blah)

    self.cbarsContainer = QWidgetMac(self)
    self.hLayout.addWidget(self.cbarsContainer)
    self.vLayout3 = QtWidgets.QVBoxLayout(self.cbarsContainer)
    self.vLayout3.setContentsMargins(0, 0, 0, 0)
    self.vLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout3.setSpacing(2)
      
    self.displaycbarsGroup = QWidgetMac()
    self.vLayout3.addWidget(self.displaycbarsGroup)
    self.hLayout13 = QtWidgets.QHBoxLayout(self.displaycbarsGroup)
    self.hLayout13.setContentsMargins(0, 0, 0, 0)
    self.hLayout13.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout13.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cbarsStyleLabel = QtWidgets.QLabel()
    # for time being control min and max lines jointly
    self.cbarsStyleLabel.setText('Bars')
    font = self.cbarsStyleLabel.font()
    font.setBold(True)
    self.cbarsStyleLabel.setFont(font)
    self.cbarsStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout13.addWidget(self.cbarsStyleLabel)    
    
    # display bars?
    self.displaycbarsLabel = QPushButtonCheckable()
    self.displaycbarsLabel.setText('Show?')
    self.displaycbarsLabel.setToolTip('Show violin bars of object')
    self.displaycbarsLabel.setChecked(self.style['cbars']['visible'])
    self.displaycbarsLabel.clicked.connect(partial(self.setDisplay, self.target, 'cbars', self.displaycbarsLabel))
    self.displaycbarsLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displaycbarsLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout13.addWidget(self.displaycbarsLabel)

    # line width
    self.cbarsLineWidthGroup = QWidgetMac()
    self.vLayout3.addWidget(self.cbarsLineWidthGroup)
    self.hLayout14 = QtWidgets.QHBoxLayout(self.cbarsLineWidthGroup)
    self.hLayout14.setContentsMargins(0, 0, 0, 0)
    self.hLayout14.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout14.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cbarsLineWidthLabel = QtWidgets.QLabel('Width')
    self.cbarsLineWidthLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsLineWidthLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout14.addWidget(self.cbarsLineWidthLabel)
    # line width spin box
    self.cbarsLineWidthSpin = QDoubleSpinClick()
    self.hLayout14.addWidget(self.cbarsLineWidthSpin)
    self.cbarsLineWidthSpin.setMinimum(0.0)
    self.cbarsLineWidthSpin.setMaximum(100.0)
    self.cbarsLineWidthSpin.setValue(self.style['cbars']['linewidth'])
    self.cbarsLineWidthSpin.setToolTip('Violin bars line width of object')
    self.cbarsLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'cbars', 'linewidth', self.cbarsLineWidthSpin))
    
    # line color
    self.cbarsLineColorGroup = QWidgetMac()
    self.vLayout3.addWidget(self.cbarsLineColorGroup)
    self.hLayout15 = QtWidgets.QHBoxLayout(self.cbarsLineColorGroup)
    self.hLayout15.setContentsMargins(0, 0, 0, 0)
    self.hLayout15.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout15.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cbarsLineColorLabel = QtWidgets.QLabel('Color')
    self.cbarsLineColorLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsLineColorLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout15.addWidget(self.cbarsLineColorLabel)
      
    self.cbarsLineColorButton = QPushButtonColor()
    self.cbarsLineColorButton.setToolTip('Violin bars line color of object')
    self.cbarsLineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['cbars']['color'][0:3]]
    self.cbarsLineColorButton.setMyColor(*colorvalue)
    self.cbarsLineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.cbarsLineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.cbarsLineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.cbarsLineColorButton.clicked.connect(partial(self.setColor, self.target, 'cbars', 'color'))
    self.hLayout15.addWidget(self.cbarsLineColorButton)
      
    # line style
    self.cbarsLineStyleGroup = QWidgetMac()
    self.vLayout3.addWidget(self.cbarsLineStyleGroup)
    self.hLayout16 = QtWidgets.QHBoxLayout(self.cbarsLineStyleGroup)
    self.hLayout16.setContentsMargins(0, 0, 0, 0)
    self.hLayout16.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout16.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cbarsLineStyleLabel = QtWidgets.QLabel('Style')
    self.cbarsLineStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsLineStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout16.addWidget(self.cbarsLineStyleLabel)
    self.cbarsComboStyle = QComboBoxMac()
    self.cbarsComboStyle.setToolTip('Violin bars line style of object')
    for entry in self.linestyles:
      self.cbarsComboStyle.addItem(entry)
    if(self.style['cbars']['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['cbars']['linestyle'])
    else:
      currindex = 0
    self.cbarsComboStyle.setCurrentIndex(currindex)
    self.cbarsComboStyle.activated.connect(partial(self.selectStyle, self.target, 'cbars', 'linestyle', self.cbarsComboStyle))
    self.cbarsComboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsComboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout16.addWidget(self.cbarsComboStyle)

    # cap style
    self.cbarsLineDashStyleGroup = QWidgetMac()
    self.vLayout3.addWidget(self.cbarsLineDashStyleGroup)
    self.hLayout17 = QtWidgets.QHBoxLayout(self.cbarsLineDashStyleGroup)
    self.hLayout17.setContentsMargins(0, 0, 0, 0)
    self.hLayout17.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout17.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cbarsLineDashStyleLabel = QtWidgets.QLabel('Cap')
    self.cbarsLineDashStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsLineDashStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout17.addWidget(self.cbarsLineDashStyleLabel)
    self.cbarsComboDashStyle = QComboBoxMac()
    self.cbarsComboDashStyle.setToolTip('Violin bars cap style of object')
    for entry in self.dashstyles:
      self.cbarsComboDashStyle.addItem(entry)
    if(self.style['cbars']['capstyle'] in self.dashstyles):
      currindex = self.dashstyles.index(self.style['cbars']['capstyle'])
    else:
      currindex = 0
    self.cbarsComboDashStyle.setCurrentIndex(currindex)
    self.cbarsComboDashStyle.activated.connect(partial(self.selectStyle, self.target, 'cbars', 'capstyle', self.cbarsComboDashStyle))
    self.cbarsComboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.cbarsComboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout17.addWidget(self.cbarsComboDashStyle)

    # cmeans
    blah = VLine()
    self.hLayout.addWidget(blah)

    self.cmeansContainer = QWidgetMac(self)
    self.hLayout.addWidget(self.cmeansContainer)
    self.vLayout4 = QtWidgets.QVBoxLayout(self.cmeansContainer)
    self.vLayout4.setContentsMargins(0, 0, 0, 0)
    self.vLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout4.setSpacing(2)
      
    self.displayCmeansGroup = QWidgetMac()
    self.vLayout4.addWidget(self.displayCmeansGroup)
    self.hLayout18 = QtWidgets.QHBoxLayout(self.displayCmeansGroup)
    self.hLayout18.setContentsMargins(0, 0, 0, 0)
    self.hLayout18.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout18.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmeansStyleLabel = QtWidgets.QLabel()
    # for time being control mean and median lines jointly
    self.cmeansStyleLabel.setText('Mean/Median')
    font = self.cmeansStyleLabel.font()
    font.setBold(True)
    self.cmeansStyleLabel.setFont(font)
    self.hLayout18.addWidget(self.cmeansStyleLabel)    
    
    # display mean/median?
    self.displayCmeansCheck = QPushButtonCheckable()
    self.displayCmeansCheck.setText('S?')
    self.displayCmeansCheck.setToolTip('Show violin means of object')
    self.displayCmeansCheck.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmeansCheck.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmeansCheck.setChecked(self.style['cmeans']['visible'])
    self.displayCmeansCheck.clicked.connect(partial(self.setDisplay, self.target, 'cmeans', self.displayCmeansCheck))
    self.hLayout18.addWidget(self.displayCmeansCheck)
    self.displayCmeansCheck2 = QPushButtonCheckable()
    self.displayCmeansCheck2.setText('S?')
    self.displayCmeansCheck2.setToolTip('Show violin medians of object')
    self.displayCmeansCheck2.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmeansCheck2.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
    self.displayCmeansCheck2.setChecked(self.style['cmedians']['visible'])
    self.displayCmeansCheck2.clicked.connect(partial(self.setDisplay, self.target, 'cmedians', self.displayCmeansCheck2))
    self.hLayout18.addWidget(self.displayCmeansCheck2)
    
    # line width
    self.cmeansLineWidthGroup = QWidgetMac()
    self.vLayout4.addWidget(self.cmeansLineWidthGroup)
    self.hLayout19 = QtWidgets.QHBoxLayout(self.cmeansLineWidthGroup)
    self.hLayout19.setContentsMargins(0, 0, 0, 0)
    self.hLayout19.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout19.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmeansLineWidthLabel = QtWidgets.QLabel('Width')
    self.cmeansLineWidthLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansLineWidthLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout19.addWidget(self.cmeansLineWidthLabel)
    # line width spin box
    self.cmeansLineWidthSpin = QDoubleSpinClick()
    self.hLayout19.addWidget(self.cmeansLineWidthSpin)
    self.cmeansLineWidthSpin.setMinimum(0.0)
    self.cmeansLineWidthSpin.setMaximum(100.0)
    self.cmeansLineWidthSpin.setValue(self.style['cmeans']['linewidth'])
    self.cmeansLineWidthSpin.setToolTip('Violin means/medians line width of object')
    self.cmeansLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'cmeans', 'linewidth', self.cmeansLineWidthSpin))
    
    # line color
    self.cmeansLineColorGroup = QWidgetMac()
    self.vLayout4.addWidget(self.cmeansLineColorGroup)
    self.hLayout20 = QtWidgets.QHBoxLayout(self.cmeansLineColorGroup)
    self.hLayout20.setContentsMargins(0, 0, 0, 0)
    self.hLayout20.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout20.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmeansLineColorLabel = QtWidgets.QLabel('Color')
    self.cmeansLineColorLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansLineColorLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout20.addWidget(self.cmeansLineColorLabel)
      
    self.cmeansLineColorButton = QPushButtonColor()
    self.cmeansLineColorButton.setToolTip('Violin means/medians line color of object')
    self.cmeansLineColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style['cmeans']['color'][0:3]]
    self.cmeansLineColorButton.setMyColor(*colorvalue)
    self.cmeansLineColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.cmeansLineColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.cmeansLineColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.cmeansLineColorButton.clicked.connect(partial(self.setColor, self.target, 'cmeans', 'color'))
    self.hLayout20.addWidget(self.cmeansLineColorButton)
      
    # line style
    self.cmeansLineStyleGroup = QWidgetMac()
    self.vLayout4.addWidget(self.cmeansLineStyleGroup)
    self.hLayout21 = QtWidgets.QHBoxLayout(self.cmeansLineStyleGroup)
    self.hLayout21.setContentsMargins(0, 0, 0, 0)
    self.hLayout21.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout21.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmeansLineStyleLabel = QtWidgets.QLabel('Style')
    self.cmeansLineStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansLineStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout21.addWidget(self.cmeansLineStyleLabel)
    self.cmeansComboStyle = QComboBoxMac()
    self.cmeansComboStyle.setToolTip('Violin means/medians line style of object')
    for entry in self.linestyles:
      self.cmeansComboStyle.addItem(entry)
    if(self.style['cmeans']['linestyle'] in self.linestyles):
      currindex = self.linestyles.index(self.style['cmeans']['linestyle'])
    else:
      currindex = 0
    self.cmeansComboStyle.setCurrentIndex(currindex)
    self.cmeansComboStyle.activated.connect(partial(self.selectStyle, self.target, 'cmeans', 'linestyle', self.cmeansComboStyle))
    self.cmeansComboStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansComboStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout21.addWidget(self.cmeansComboStyle)

    # cap style
    self.cmeansLineDashStyleGroup = QWidgetMac()
    self.vLayout4.addWidget(self.cmeansLineDashStyleGroup)
    self.hLayout22 = QtWidgets.QHBoxLayout(self.cmeansLineDashStyleGroup)
    self.hLayout22.setContentsMargins(0, 0, 0, 0)
    self.hLayout22.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout22.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.cmeansLineDashStyleLabel = QtWidgets.QLabel('Cap')
    self.cmeansLineDashStyleLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansLineDashStyleLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout22.addWidget(self.cmeansLineDashStyleLabel)
    self.cmeansComboDashStyle = QComboBoxMac()
    self.cmeansComboDashStyle.setToolTip('Violin means/medians cap style of object')
    for entry in self.dashstyles:
      self.cmeansComboDashStyle.addItem(entry)
    if(self.style['cmeans']['capstyle'] in self.dashstyles):
      currindex = self.dashstyles.index(self.style['cmeans']['capstyle'])
    else:
      currindex = 0
    self.cmeansComboDashStyle.setCurrentIndex(currindex)
    self.cmeansComboDashStyle.activated.connect(partial(self.selectStyle, self.target, 'cmeans', 'capstyle', self.cmeansComboDashStyle))
    self.cmeansComboDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.cmeansComboDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout22.addWidget(self.cmeansComboDashStyle)

  def alterStyle(self, target=None, subtarget=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if((subtarget != None) and (key in self.style[subtarget])):
        self.style[subtarget][key] = value
      elif(key in self.style):
        self.style[key] = value

      target.setViolinStyle(subtarget, key, value, redraw=True)

  def changeStyle(self, target=None, subtarget=None, key=None, entryfield=None, minval=0, maxval=1):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      if((subtarget != None) and (key in self.style[subtarget])):
        self.style[subtarget][key] = value
      elif(key in self.style):
        self.style[key] = value

      target.setViolinStyle(subtarget, key, value, redraw=True)

  def setDisplay(self, target=None, subtarget=None, entryfield=None):
    if(target != None):
      value = entryfield.isChecked()
      target.setViolinStyle(subtarget, 'visible', value, redraw=True)

  def selectStyle(self, target=None, subtarget=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      value = str(entryfield.currentText())
      if(key == 'hatchMultiply'):
        value = int(value)
      if((subtarget != None) and (key in self.style[subtarget])):
        self.style[subtarget][key] = value
      elif(key in self.style):
        self.style[key] = value

      target.setViolinStyle(subtarget, key, value, redraw=True)

  def setColor(self, target=None, subtarget=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if((subtarget != None) and (key in self.style[subtarget])):
        prevColor = [int(255 * i) for i in self.style[subtarget][key]]
        prevColor = QtGui.QColor(*prevColor)
      elif (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(subtarget == 'bodies'):
          if(key == 'edgecolor'):
            self.bodiesLineColorButton.setMyColor(*value)
          elif(key == 'facecolor'):
            self.bodiesFillColorButton.setMyColor(*value)
        elif((subtarget == 'cmaxes') and (key == 'color')):
          self.cmaxesLineColorButton.setMyColor(*value)
        elif((subtarget == 'cbars') and (key == 'color')):
          self.cbarsLineColorButton.setMyColor(*value)
        elif((subtarget == 'cmeans') and (key == 'color')):
          self.cmeansLineColorButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        if(subtarget != None):
          self.style[subtarget][key] = value
        else:
          self.style[key] = value

        target.setViolinStyle(subtarget, key, value, redraw=True)

      # bring back the QMenu
      self.parent.togglePersist(False)

  def toggleBodyFill(self, target=None, entryfield=None):
    # toggles whether to fill violin bodies or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['bodies']['doFill'] = value
      target.toggleViolinBodyFill(value, redraw=True)

class boxStyleMenu(QWidgetMac):
  def __init__(self, parent=None, target=None):
    super(boxStyleMenu, self).__init__(parent)
    self.parent = parent
    self.target = target

    # float validator
    self.validFloat = MyValidFloat()

    # valid line styles
    self.linestyles = ['None', '-', '--', '-.', ':']
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.dashstyles = ['butt', 'round', 'projecting']
    self.hatchstyles = ['None', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*'] # unfortunately none is not interpreted correctly
    self.hatchstyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    self.hatchMultiplystyles = [str(i) for i in range(1, 11)]

    self.markerstyles = []
    self.markerstyles.extend(matplotlib.lines.Line2D.markers)
    # weed out duplicate blank items
    blankItems = [i for i in self.markerstyles if i in ['', ' ', 'None', None]]
    while (len(blankItems) - 1):
      killItem = blankItems[-1]
      self.markerstyles = [i for i in self.markerstyles if i != killItem]
      blankItems = blankItems[:-1]
    # had to remove a couple of styles as fliers crash otherwise
    self.orderedstyles = ['.', ',', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', '*', 'h', 'H', '+',\
                          'x', 'D', 'd', '|', '_', 'P', 'X', 1, 2, 3, 4, 8, 'None', None, ' ', '']
    self.orderedstyles = [i for i in self.orderedstyles if (i in self.markerstyles)]
    if(self.parent.parent.parent.graphicsarea.advancedExport['advancedGraphics']):
      self.orderedstyles += [i for i in CUSTOM_MARKERS]

    self.fillstyles = [i for i in matplotlib.lines.Line2D.fillStyles if not i in ['', 'none', 'None', None]]
    self.typestyles = ['full', 'left', 'right']
    
    # set up initial values
    self.style = {}
    if (self.target != None):
      getStyle = self.target.getViolinStyle()

      # have to make a copy to not directly alter the original object
      for entry in getStyle:
        if(type(getStyle[entry]) == type({})):
          # have to deepcopy inner dicts to prevent inadvertent overwriting of values
          self.style[entry] = deepcopy(getStyle[entry])
        else:
          self.style[entry] = getStyle[entry]
    
    # set default values
    defaultStyle = {}

    defaultStyle['boxes'] = {'linestyle': 'solid', 'linewidth': 1.0, 'color': [0.2, 0.2, 0.2, 1.0], 'dash_capstyle': 'butt', 'visible': True}
    for slaved in ['whiskers', 'medians', 'caps']:
      defaultStyle[slaved] = {}
      defaultStyle[slaved].update(defaultStyle['boxes'])
    defaultStyle['boxes'].update({'edgecolor': [0.2, 0.2, 0.2, 1.0], 'width': 0.5, 'boxFill': True, 'facecolor': [0.8, 0.8, 0.9, 1.0], 'hatch': '', 'hatchMultiply': 1, 'hatch_linewidth': 1., 'type': 'full'})
    defaultStyle['fliers'] = {'visible': True, 'marker': 'o', 'markerfacecolor': [0.89, 0.29, 0.2, 1.0], 'markerfacecoloralt': [0.0, 0.0, 0.0, 1.0],\
                'markeredgecolor': [0.0, 0.0, 0.0, 1.0], 'markeredgewidth': 0.5, 'markersize': 10.0, 'fillstyle': 'full'}

    # only used this key temporarily for slaving whiskers etc.
    del defaultStyle['boxes']['color']

    for entry in defaultStyle:
      if(not (entry in self.style)):
        self.style[entry] = defaultStyle[entry]
      elif(type(self.style[entry]) is dict):
        # update sub styles to avoid undefined settings
        defaultStyle[entry].update(self.style[entry])
        self.style[entry] = defaultStyle[entry]

    # set up GUI
    self.buildRessource()
    
  def buildRessource(self):
    # build gui
    self.hLayout = QtWidgets.QHBoxLayout(self)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.hLayout.setSpacing(2)
    
    self.hLayouts, self.vLayouts = [], []
    self.generalContainers, self.generalLabels, self.generalGroups = [], [], []
    self.displayChecks, self.lineWidthEntries, self.lineColorButtons, self.lineStyleCombos, self.lineCapCombos = [], [], [], [], []
    items, labels = ['boxes', 'whiskers', 'caps', 'medians'], ['Boxes', 'Whisk.', 'Caps', 'Median']
    toolTips = ['box bodies', 'box whiskers', 'box caps', 'box medians']
    for i, j, useTip in zip(items, labels, toolTips):
      # header
      self.generalContainers.append(QWidgetMac(self))
      self.hLayout.addWidget(self.generalContainers[-1])
      self.vLayouts.append(QtWidgets.QVBoxLayout(self.generalContainers[-1]))
      self.vLayouts[-1].setContentsMargins(0, 0, 0, 0)
      self.vLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
      self.vLayouts[-1].setSpacing(2)
        
      self.generalGroups.append(QWidgetMac())
      self.vLayouts[-1].addWidget(self.generalGroups[-1])
      self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
      self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
      self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.generalLabels.append(QtWidgets.QLabel())
      self.generalLabels[-1].setText(j)
      font = self.generalLabels[-1].font()
      font.setBold(True)
      self.generalLabels[-1].setFont(font)
      self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.generalLabels[-1])    

      # display group
      self.displayChecks.append(QPushButtonCheckable())
      self.displayChecks[-1].setText('Show?')
      self.displayChecks[-1].setToolTip('Show ' + useTip + ' of object')
      self.displayChecks[-1].setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.displayChecks[-1].setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.displayChecks[-1].setChecked(self.style[i]['visible'])
      self.displayChecks[-1].clicked.connect(partial(self.setDisplay, self.target, i, self.displayChecks[-1]))
      self.hLayouts[-1].addWidget(self.displayChecks[-1])
      
      # controls for split violin plots
      if(i == 'boxes'):
        self.generalGroups.append(QWidgetMac())
        self.vLayouts[-1].addWidget(self.generalGroups[-1])
        self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
        self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
        self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.generalLabels.append(QtWidgets.QLabel())
        self.generalLabels[-1].setText('Type')
        self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.generalLabels[-1])    
        self.boxesTypeStyle = QComboBoxMac()
        self.boxesTypeStyle.setToolTip('Violin bodies type of object')
        for entry in self.typestyles:
          self.boxesTypeStyle.addItem(entry)
        if(('type' in self.style['boxes']) and (self.style['boxes']['type'] in self.typestyles)):
          currindex = self.typestyles.index(self.style['boxes']['type'])
        else:
          currindex = 0
        self.boxesTypeStyle.setCurrentIndex(currindex)
        self.boxesTypeStyle.activated.connect(partial(self.selectStyle, self.target, 'boxes', 'type', self.boxesTypeStyle))
        self.boxesTypeStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
        self.boxesTypeStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.boxesTypeStyle)

      # line width group
      self.generalGroups.append(QWidgetMac())
      self.vLayouts[-1].addWidget(self.generalGroups[-1])
      self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
      self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
      self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.generalLabels.append(QtWidgets.QLabel('Width'))
      self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.generalLabels[-1])

      # line width spin box
      self.lineWidthEntries.append(QDoubleSpinClick())
      self.hLayouts[-1].addWidget(self.lineWidthEntries[-1])
      self.lineWidthEntries[-1].setMinimum(0.0)
      self.lineWidthEntries[-1].setMaximum(100.0)
      self.lineWidthEntries[-1].setValue(self.style[i]['linewidth'])
      self.lineWidthEntries[-1].setToolTip(useTip.capitalize() + ' line width of object')
      self.lineWidthEntries[-1].setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthEntries[-1].setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.lineWidthEntries[-1].editingFinished.connect(partial(self.alterStyle, self.target, i, 'linewidth', self.lineWidthEntries[-1]))
    
      # line color group
      self.generalGroups.append(QWidgetMac())
      self.vLayouts[-1].addWidget(self.generalGroups[-1])
      self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
      self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
      self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.generalLabels.append(QtWidgets.QLabel('Color'))
      self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.generalLabels[-1])

      self.lineColorButtons.append(QPushButtonColor())
      self.lineColorButtons[-1].setAutoFillBackground(False)
      self.lineColorButtons[-1].setToolTip(useTip.capitalize() + ' line color of object')
      if(i == 'boxes'):
        colorvalue = [int(i * 255.0) for i in self.style[i]['edgecolor'][0:3]]
      else:
        colorvalue = [int(i * 255.0) for i in self.style[i]['color'][0:3]]
      self.lineColorButtons[-1].setMyColor(*colorvalue)
      self.lineColorButtons[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.lineColorButtons[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.lineColorButtons[-1].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      if(i == 'boxes'):
        self.lineColorButtons[-1].clicked.connect(partial(self.setColor, self.target, i, 'edgecolor'))
      else:
        self.lineColorButtons[-1].clicked.connect(partial(self.setColor, self.target, i, 'color'))
      self.hLayouts[-1].addWidget(self.lineColorButtons[-1])

      # line styles group
      self.generalGroups.append(QWidgetMac())
      self.vLayouts[-1].addWidget(self.generalGroups[-1])
      self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
      self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
      self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.generalLabels.append(QtWidgets.QLabel('Style'))
      self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.generalLabels[-1])

      self.lineStyleCombos.append(QComboBoxMac())
      self.lineStyleCombos[-1].setToolTip(useTip.capitalize() + ' line style of object')
      for entry in self.linestyles:
        self.lineStyleCombos[-1].addItem(entry)
      if(self.style[i]['linestyle'] in self.linestyles):
        currindex = self.linestyles.index(self.style[i]['linestyle'])
      else:
        currindex = 0
      self.lineStyleCombos[-1].setCurrentIndex(currindex)
      self.lineStyleCombos[-1].activated.connect(partial(self.selectStyle, self.target, i, 'linestyle', self.lineStyleCombos[-1]))
      self.lineStyleCombos[-1].setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.lineStyleCombos[-1].setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.lineStyleCombos[-1])

      # cap styles group
      self.generalGroups.append(QWidgetMac())
      self.vLayouts[-1].addWidget(self.generalGroups[-1])
      self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
      self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
      self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
      self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.generalLabels.append(QtWidgets.QLabel('Cap'))
      self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.generalLabels[-1])

      self.lineCapCombos.append(QComboBoxMac())
      self.lineCapCombos[-1].setToolTip(useTip.capitalize() + ' cap style of object')
      for entry in self.dashstyles:
        self.lineCapCombos[-1].addItem(entry)
      if(self.style[i]['dash_capstyle'] in self.dashstyles):
        currindex = self.dashstyles.index(self.style[i]['dash_capstyle'])
      else:
        currindex = 0
      self.lineCapCombos[-1].setCurrentIndex(currindex)
      self.lineCapCombos[-1].activated.connect(partial(self.selectStyle, self.target, i, 'dash_capstyle', self.lineCapCombos[-1]))
      self.lineCapCombos[-1].setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.lineCapCombos[-1].setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.hLayouts[-1].addWidget(self.lineCapCombos[-1])
      
      # extra options for boxes
      if(i == 'boxes'):
        # fill color group
        self.generalGroups.append(QWidgetMac())
        self.vLayouts[-1].addWidget(self.generalGroups[-1])
        self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
        self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
        self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.generalLabels.append(QPushButtonCheckable())
        self.generalLabels[-1].setText('Fill')
        self.generalLabels[-1].setToolTip('Show box bodies fill of object')
        self.generalLabels[-1].setChecked(self.style[i]['boxFill'])
        self.generalLabels[-1].clicked.connect(partial(self.toggleBoxFill, self.target, self.generalLabels[-1]))
        self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.generalLabels[-1])
        
        self.lineColorButtons.append(QPushButtonColor())
        self.lineColorButtons[-1].setToolTip('Box bodies fill color of object')
        self.lineColorButtons[-1].setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.style[i]['facecolor'][0:3]]
        self.lineColorButtons[-1].setMyColor(*colorvalue)
        self.lineColorButtons[-1].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.lineColorButtons[-1].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.lineColorButtons[-1].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.lineColorButtons[-1].clicked.connect(partial(self.setColor, self.target, i, 'facecolor'))
        self.hLayouts[-1].addWidget(self.lineColorButtons[-1])

        # hatch group
        self.generalGroups.append(QWidgetMac())
        self.vLayouts[-1].addWidget(self.generalGroups[-1])
        self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
        self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
        self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
        self.generalLabels.append(QtWidgets.QLabel('Hatch'))
        self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.generalLabels[-1])

        self.boxesComboHatchStyle = QComboBoxMac()
        self.boxesComboHatchStyle.setToolTip('Box bodies hatch style of object')
        for entry in self.hatchstyles:
          self.boxesComboHatchStyle.addItem(entry)
        if(self.style['boxes']['hatch'] in self.hatchstyles):
          currindex = self.hatchstyles.index(self.style['boxes']['hatch'])
        else:
          currindex = 0
        self.boxesComboHatchStyle.setCurrentIndex(currindex)
        self.boxesComboHatchStyle.activated.connect(partial(self.selectStyle, self.target, 'boxes', 'hatch', self.boxesComboHatchStyle))
        self.boxesComboHatchStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.boxesComboHatchStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.boxesComboHatchStyle)
        self.boxesComboHatchMultiplyStyle = QComboBoxMac()
        self.boxesComboHatchMultiplyStyle.setToolTip('Box bodies hatch repeat of object')
        for entry in self.hatchMultiplystyles:
          self.boxesComboHatchMultiplyStyle.addItem(str(entry))
        if(str(self.style['boxes']['hatchMultiply']) in self.hatchMultiplystyles):
          currindex = self.hatchMultiplystyles.index(str(self.style['boxes']['hatchMultiply']))
        else:
          currindex = 0
        self.boxesComboHatchMultiplyStyle.setCurrentIndex(currindex)
        self.boxesComboHatchMultiplyStyle.activated.connect(partial(self.selectStyle, self.target, 'boxes', 'hatchMultiply', self.boxesComboHatchMultiplyStyle))
        self.boxesComboHatchMultiplyStyle.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.boxesComboHatchMultiplyStyle.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.boxesComboHatchMultiplyStyle)

        # hatch second row
        self.generalGroups.append(QWidgetMac())
        self.vLayouts[-1].addWidget(self.generalGroups[-1])
        self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
        self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
        self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.generalLabels.append(QtWidgets.QLabel(' # line'))
        self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.generalLabels[-1])
        # hatch line width spin box
        self.hatchLineWidthSpin = QDoubleSpinClick()
        self.hLayouts[-1].addWidget(self.hatchLineWidthSpin)
        self.hatchLineWidthSpin.setMinimum(0.0)
        self.hatchLineWidthSpin.setMaximum(100.0)
        self.hatchLineWidthSpin.setValue(self.style['boxes']['hatch_linewidth'])
        self.hatchLineWidthSpin.setToolTip('Box bodies hatch line width of object')
        self.hatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hatchLineWidthSpin.editingFinished.connect(partial(self.alterStyle, self.target, 'boxes', 'hatch_linewidth', self.hatchLineWidthSpin))

        # body width group
        self.generalGroups.append(QWidgetMac())
        self.vLayouts[-1].addWidget(self.generalGroups[-1])
        self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
        self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
        self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
        self.generalLabels.append(QtWidgets.QLabel('BWid'))
        self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.hLayouts[-1].addWidget(self.generalLabels[-1])
        self.boxesWidthEntry = QLineEditClick()
        self.boxesWidthEntry.setToolTip('Box bodies width of object')
        self.boxesWidthEntry.setText(str(self.style['boxes']['width']))
        self.boxesWidthEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.boxesWidthEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.boxesWidthEntry.editingFinished.connect(partial(self.changeStyle, self.target, 'boxes', 'width', self.boxesWidthEntry, 0.0, 100.0))
        self.boxesWidthEntry.setValidator(self.validFloat)
        self.hLayouts[-1].addWidget(self.boxesWidthEntry)

      # add a spacing line
      blah = VLine()
      self.hLayout.addWidget(blah)

    # now put config of outliers
    i, j = 'fliers', 'Outliers'
    # header
    self.generalContainers.append(QWidgetMac(self))
    self.hLayout.addWidget(self.generalContainers[-1])
    self.vLayouts.append(QtWidgets.QVBoxLayout(self.generalContainers[-1]))
    self.vLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.vLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayouts[-1].setSpacing(2)
      
    self.generalGroups.append(QWidgetMac())
    self.vLayouts[-1].addWidget(self.generalGroups[-1])
    self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
    self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.generalLabels.append(QtWidgets.QLabel())
    self.generalLabels[-1].setText(j)
    font = self.generalLabels[-1].font()
    font.setBold(True)
    self.generalLabels[-1].setFont(font)
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])    

    # display group
    self.displayChecks.append(QPushButtonCheckable())
    self.displayChecks[-1].setText('Show?')
    self.displayChecks[-1].setToolTip('Show box outliers of object')
    self.displayChecks[-1].setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayChecks[-1].setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.displayChecks[-1].setChecked(self.style[i]['visible'])
    self.displayChecks[-1].clicked.connect(partial(self.setDisplay, self.target, i, self.displayChecks[-1]))
    self.hLayouts[-1].addWidget(self.displayChecks[-1])
      
    # marker size group
    self.generalGroups.append(QWidgetMac())
    self.vLayouts[-1].addWidget(self.generalGroups[-1])
    self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
    self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.generalLabels.append(QtWidgets.QLabel('Size'))
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])
    # marker size spin box
    self.markerSizeEntry = QDoubleSpinClick()
    self.hLayouts[-1].addWidget(self.markerSizeEntry)
    self.markerSizeEntry.setMinimum(0.0)
    self.markerSizeEntry.setMaximum(100.0)
    self.markerSizeEntry.setValue(self.style[i]['markersize'])
    self.markerSizeEntry.setToolTip('Box outlier marker size of object')
    self.markerSizeEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerSizeEntry.editingFinished.connect(partial(self.alterStyle, self.target, i, 'markersize', self.markerSizeEntry))

    # marker color group
    self.generalGroups.append(QWidgetMac())
    self.vLayouts[-1].addWidget(self.generalGroups[-1])
    self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
    self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.generalLabels.append(QPushButtonCheckable())
    self.generalLabels[-1].setText('Face')
    self.generalLabels[-1].setToolTip('Show box outlier marker fill of object')
    self.generalLabels[-1].setChecked(self.style[i]['doFill'])
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].clicked.connect(partial(self.toggleFliersMarkerFill, self.target, self.generalLabels[-1]))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])
    self.markerFaceColorButton = QPushButtonColor()
    self.markerFaceColorButton.setToolTip('Box outlier marker fill color of object')
    self.markerFaceColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style[i]['markerfacecolor'][0:3]]
    self.markerFaceColorButton.setMyColor(*colorvalue)
    self.markerFaceColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerFaceColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerFaceColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerFaceColorButton.clicked.connect(partial(self.setColor, self.target, i, key='markerfacecolor'))
    self.hLayouts[-1].addWidget(self.markerFaceColorButton)

    self.generalLabels.append(QtWidgets.QLabel('Edge'))
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])
    self.markerEdgeColorButton = QPushButtonColor()
    self.markerEdgeColorButton.setToolTip('Box outlier marker edge color of object')
    self.markerEdgeColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style[i]['markeredgecolor'][0:3]]
    self.markerEdgeColorButton.setMyColor(*colorvalue)
    self.markerEdgeColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerEdgeColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerEdgeColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerEdgeColorButton.clicked.connect(partial(self.setColor, self.target, i, key='markeredgecolor'))
    self.hLayouts[-1].addWidget(self.markerEdgeColorButton)

    # marker edge width group
    self.generalGroups.append(QWidgetMac())
    self.vLayouts[-1].addWidget(self.generalGroups[-1])
    self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
    self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.generalLabels.append(QtWidgets.QLabel('Edgewidth'))
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])
    # boxes width spin box
    self.markerEdgeWidthEntry = QDoubleSpinClick()
    self.hLayouts[-1].addWidget(self.markerEdgeWidthEntry)
    self.markerEdgeWidthEntry.setMinimum(0.0)
    self.markerEdgeWidthEntry.setMaximum(100.0)
    self.markerEdgeWidthEntry.setValue(self.style[i]['markeredgewidth'])
    self.markerEdgeWidthEntry.setToolTip('Box outlier marker edge width of object')
    self.markerEdgeWidthEntry.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthEntry.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.markerEdgeWidthEntry.editingFinished.connect(partial(self.alterStyle, self.target, i, 'markeredgewidth', self.markerEdgeWidthEntry))

    # marker style group
    self.generalGroups.append(QWidgetMac())
    self.vLayouts[-1].addWidget(self.generalGroups[-1])
    self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
    self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    # need copy for MarkerMenu
    self.style['marker'] = self.style['fliers']['marker']
    self.generalLabels.append(QtWidgets.QLabel('Style'))
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])
    self.comboStyle = MarkerButton()
    self.comboStyle.setToolTip('Marker symbol of object')
    self.comboStyle.clicked.connect(partial(self.markerCallback, self.comboStyle))
    if(self.style[i]['marker'] in self.orderedstyles):
      self.comboStyle.setSymbol(self.style[i]['marker'])
    else:
      self.comboStyle.setSymbol(self.orderedstyles[0])
    self.comboStyle.setSymbolList(self.orderedstyles)
    self.comboStyle.setParent(self)
    self.comboStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.comboStyle)    

    # marker fillstyle group
    self.generalGroups.append(QWidgetMac())
    self.vLayouts[-1].addWidget(self.generalGroups[-1])
    self.hLayouts.append(QtWidgets.QHBoxLayout(self.generalGroups[-1]))
    self.hLayouts[-1].setContentsMargins(0, 0, 0, 0)
    self.hLayouts[-1].setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayouts[-1].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.generalLabels.append(QtWidgets.QLabel('Fillstyle'))
    self.generalLabels[-1].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.generalLabels[-1].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.generalLabels[-1])
    self.comboFillStyle = QComboBoxMac()
    self.comboFillStyle.setToolTip('Box outlier marker fill style of object')
    for entry in self.fillstyles:
      self.comboFillStyle.addItem(str(entry))
    if(self.style[i]['fillstyle'] in self.fillstyles):
      currindex = self.fillstyles.index(self.style[i]['fillstyle'])
    else:
      currindex = 0
    self.comboFillStyle.setCurrentIndex(currindex)
    self.comboFillStyle.activated.connect(partial(self.selectStyle, self.target, i, 'fillstyle', self.comboFillStyle))
    self.comboFillStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.comboFillStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayouts[-1].addWidget(self.comboFillStyle)

    self.markerAltColorButton = QPushButtonColor()
    self.markerAltColorButton.setToolTip('Box outlier marker alternate fill color of object')
    self.markerAltColorButton.setAutoFillBackground(False)
    colorvalue = [int(i * 255.0) for i in self.style[i]['markerfacecoloralt'][0:3]]
    self.markerAltColorButton.setMyColor(*colorvalue)
    self.markerAltColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerAltColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.markerAltColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.markerAltColorButton.clicked.connect(partial(self.setColor, self.target, i, 'markerfacecoloralt'))
    self.hLayouts[-1].addWidget(self.markerAltColorButton)

  def alterStyle(self, target=None, subtarget=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      if((subtarget != None) and (key in self.style[subtarget])):
        self.style[subtarget][key] = value
      elif(key in self.style):
        self.style[key] = value

      target.setViolinStyle(subtarget, key, value, redraw=True)

  def changeStyle(self, target=None, subtarget=None, key=None, entryfield=None, minval=0, maxval=1):
    if((target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      if((subtarget != None) and (key in self.style[subtarget])):
        self.style[subtarget][key] = value
      elif(key in self.style):
        self.style[key] = value

      target.setViolinStyle(subtarget, key, value, redraw=True)

  def toggleBoxFill(self, target=None, entryfield=None):
    if(target != None):
      value = entryfield.isChecked()
      target.setViolinStyle('boxes', 'boxFill', value, redraw=True)

  def setDisplay(self, target=None, subtarget=None, entryfield=None):
    if(target != None):
      value = entryfield.isChecked()
      target.setViolinStyle(subtarget, 'visible', value, redraw=True)

  def selectStyle(self, target=None, subtarget=None, key=None, entryfield=None):
    if((target != None) and (key != None)):
      value = str(entryfield.currentText())
      if(key == 'hatchMultiply'):
        value = int(value)
      if((subtarget != None) and (key in self.style[subtarget])):
        self.style[subtarget][key] = value
      elif(key in self.style):
        self.style[key] = value

      target.setViolinStyle(subtarget, key, value, redraw=True)

  def setColor(self, target=None, subtarget=None, key=None):
    if((target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.parent.togglePersist(True)
      # get current color
      if((subtarget != None) and (key in self.style[subtarget])):
        prevColor = [int(255 * i) for i in self.style[subtarget][key]]
        prevColor = QtGui.QColor(*prevColor)
      elif (key in self.style):
        prevColor = [int(255 * i) for i in self.style[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(subtarget == 'boxes'):
          if(key == 'edgecolor'):
            self.lineColorButtons[0].setMyColor(*value)
          elif(key == 'facecolor'):
            self.lineColorButtons[1].setMyColor(*value)
        elif((subtarget == 'whiskers') and (key == 'color')):
          self.lineColorButtons[2].setMyColor(*value)
        elif((subtarget == 'caps') and (key == 'color')):
          self.lineColorButtons[3].setMyColor(*value)
        elif((subtarget == 'medians') and (key == 'color')):
          self.lineColorButtons[4].setMyColor(*value)
        elif(subtarget == 'fliers'):
          if(key == 'markerfacecolor'):
            self.markerFaceColorButton.setMyColor(*value)
          elif(key == 'markeredgecolor'):
            self.markerEdgeColorButton.setMyColor(*value)
          elif(key == 'markerfacecoloralt'):
            self.markerAltColorButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        if(subtarget != None):
          self.style[subtarget][key] = value
        else:
          self.style[key] = value

        target.setViolinStyle(subtarget, key, value, redraw=True)

      # bring back the QMenu
      self.parent.togglePersist(False)

  def toggleFliersMarkerFill(self, target=None, entryfield=None):
    # toggles whether to fill fliers markers or not
    if(target != None):
      value = entryfield.isChecked()
      self.style['fliers']['doFill'] = value
      target.toggleFliersMarkerFill(value, redraw=True)

  def markerCallback(self, callButton=None):
    # opens menu to access advanced configuration options
    self.menu = MarkerMenu(self)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # bottom align position of QMenu
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)

  def setMarker(self, symbol='', alternate=False):
    # sets marker symbol to selected type
    if((symbol in self.orderedstyles) and (symbol != self.style['fliers']['marker'])):
      self.style['fliers']['marker'] = symbol
      self.comboStyle.setSymbol(symbol)

      self.target.setViolinStyle('fliers', 'marker', symbol, redraw=True)

class ConfigPlotMenu(KuhMenu):
  def __init__(self, parent=None, item=None, itemType=None):
    super(ConfigPlotMenu, self).__init__()
    self.parent = parent
    self.item = item
    self.itemType = itemType
    self.persist = False
    self.persistPos = None
    self.configFields = []
    self.configTitle = 'nada'
    self.advancedFields = []
    
    self.validFloat = MyValidFloat()
    self.validInt = MyValidInt()
    
    # initialize drag values
    self.startPos = None
    
    # initialize target fields
    self.initConfigFields()
      
    # set up GUI
    self.buildRessource()
    
  def togglePersist(self, state):
    # toggles QMenu persistence for click events that would close it, see below
    # when state True, save current menu coordinates
    # when state False, restore QMenu at previous coordinates
    self.persist = state
    if(state):
      self.persistPos = self.pos()
    elif(self.persistPos != None):
      self.popup(self.persistPos)
      self.persistPos = None

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(self.underMouse()):
      if(event.button() == QtCore.Qt.MouseButton.LeftButton):
        self.startPos = event.globalPosition().toPoint()
    else:
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def mouseMoveEvent(self, event):
    if((event.buttons() == QtCore.Qt.MouseButton.LeftButton) and (self.startPos != None)):
      currentPos = event.globalPosition().toPoint()
      menuPos = self.mapToGlobal(self.pos()) + (currentPos - self.startPos)
      menuPos = self.mapFromGlobal(menuPos)
      self.move(menuPos)
      self.startPos = currentPos
    
  def mouseReleaseEvent(self, event):
    # turn off tracking
    if(event.button() == QtCore.Qt.MouseButton.LeftButton):
      self.startPos = None

  def buildRessource(self):
    # build gui
    self.autoPushButton = None
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[2]*4)
    self.vLayout.setSpacing(2)
    title = QtWidgets.QLabel(self.configTitle)
    font = title.font()
    font.setBold(True)
    title.setFont(font)
    self.vLayout.addWidget(title)
    self.vLayout.addWidget(HLine())
    
    self.fieldVisibility, updateVisibility = {'linear': None, 'radial': None}, False
    index = 0
    layoutLine = None
    self.rememberThis = []
    for count, field in enumerate(self.configFields + self.advancedFields):
      if(count == len(self.configFields)):
        self.vLayout.addWidget(HLine())
      if((not len(field)) and (layoutLine != None)):
        layoutLine.addStretch()
      elif(field[1] in ['QLineEdit', 'QPushButtonColor', 'QDoubleSpinClick', 'QPushButtonCheckable', 'QPushButtonMac', 'QComboBoxMac', 'FontoBox', 'QLabel']):
        if(field[3] or (not index)):
          container = QtWidgets.QWidget(self)
          self.vLayout.addWidget(container)
          layoutContainer = QtWidgets.QHBoxLayout(container)
          layoutContainer.setContentsMargins(0, 0, 0, 0)
          layoutContainer.setSpacing(int(-2 + 8 * SCALEFONT))
          layoutContainer.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    
          label = QtWidgets.QLabel(field[0])
          label.setMinimumWidth(int(54 * SCALEFONT))
          label.setMaximumWidth(int(54 * SCALEFONT))
          if(field in self.advancedFields):
            label.setStyleSheet('QLabel {background-color: rgba' + UI_BRIGHT_STRING + ';}')
          layoutContainer.addWidget(label, index)
          
          lineContainer = QtWidgets.QWidget(self)
          layoutContainer.addWidget(lineContainer, index)
          layoutLine = QtWidgets.QHBoxLayout(lineContainer)
          layoutLine.setContentsMargins(0, 0, 0, 0)
          layoutLine.setSpacing(int(-2 + 8 * SCALEFONT))
          layoutLine.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
          index += 1
          if(field[2] == 'configCanvasGradientAngle'):
            self.fieldVisibility['linear'] = container
          elif(field[2] == 'configCanvasGradientCenterX'):
            self.fieldVisibility['radial'] = container
        elif(field[0] != ''):
          label = QtWidgets.QLabel(field[0])
          if(field[0] in ['height', 'top', 'right', 'Length', 'Angle']):
            label.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
          if(field in self.advancedFields):
            label.setStyleSheet('QLabel {color: rgba' + UI_BRIGHT_STRING + ';}')
          layoutLine.addWidget(label)

        if('$$' in field[2]):
          main, sub = field[2].split('$$')
          if(self.onSecond):
            target = self.parent.parent.graphicsarea2.__dict__[main][sub]
          else:
            target = self.parent.parent.graphicsarea.__dict__[main][sub]
        else:
          if(self.onSecond):
            target = self.parent.parent.graphicsarea2.__dict__[field[2]]
          else:
            target = self.parent.parent.graphicsarea.__dict__[field[2]]

        # copy toolTips from target
        if(hasattr(target, 'toolTip')):
          toolTip = target.toolTip()
        else:
          toolTip = ''
            
        if(field[1] == 'QLineEdit'):
          qLineEdit = QLineEditClick()
          qLineEdit.setText(target.text())
          # copy alignment from twinned control
          qLineEdit.setAlignment(target.alignment())
          self.rememberThis.append(target.text())
          if((('configTick' in field[2]) or ('configInnerTick' in field[2])) and ('Entry' in field[2])):
            qLineEdit.editingFinished.connect(partial(self.updateTargetQLineEditValues, qLineEdit, target, len(self.rememberThis) - 1))
          else:
            qLineEdit.editingFinished.connect(partial(self.updateTargetQLineEdit, qLineEdit, target, len(self.rememberThis) - 1))
          if(('Pad' in field[2]) or (field[2].endswith('Pos')) or (field[2] in ['configLegendX', 'configLegendY', 'configLegendLinespacing'])):
            qLineEdit.setValidator(self.validFloat)
          if(('RelativeLength' in field[2]) or ('ShadowOff' in field[2]) or ('Rounding' in field[2]) or ('MarkerScale' in field[2])\
              or ('AxisBound' in field[2]) or ('AxisPosition' in field[2]) or ('exportSize' in field[2]) or ('CanvasGradient' in field[2])\
              or field[2].startswith('configArrow')):
            qLineEdit.setValidator(self.validFloat)
          if('Precision' in field[2]):
            qLineEdit.setValidator(self.validInt)
          qLineEdit.setMaximumSize(QtCore.QSize(min(target.width(), int(200 * SCALEFONT)), int(BASE_SIZE)))
          qLineEdit.setMinimumSize(QtCore.QSize(min(target.width(), int(200 * SCALEFONT)), int(BASE_SIZE)))
          qLineEdit.setToolTip(toolTip)
          layoutLine.addWidget(qLineEdit)
        elif(field[1] == 'QPushButtonColor'):
          qPushButton = QPushButtonColor()
          qPushButton.clicked.connect(partial(self.updateTargetQPushButton, qPushButton, target))
          qPushButton.setAutoFillBackground(False)
          qPushButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
          qPushButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
          qPushButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
          qPushButton.setMyColor(*target.getMyColor())
          qPushButton.setToolTip(toolTip)
          layoutLine.addWidget(qPushButton)
        elif(field[1] == 'QPushButtonMac'):
          qPushButton = QPushButtonMac()
          qPushButton.setText(target.text())
          qPushButton.clicked.connect(partial(self.updateTargetQPushButton, qPushButton, target))
          if(field[0] == 'Reset'):
            qPushButton.setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
            qPushButton.setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
          qPushButton.setToolTip(toolTip)
          layoutLine.addWidget(qPushButton)
        elif(field[1] == 'QDoubleSpinClick'):
          qDoubleSpinClick = QDoubleSpinClick()
          qDoubleSpinClick.setMinimum(0.0)
          qDoubleSpinClick.setMaximum(100.0)
          if('Angle' in field[2]):
            qDoubleSpinClick.setMinimum(0.0)
            qDoubleSpinClick.setMaximum(360.0)
            qDoubleSpinClick.setSingleStep(10)
            qDoubleSpinClick.setWrapping(True)
            qDoubleSpinClick.setAdaptive(False)
          elif('DividerLineLength' in field[2]):
            qDoubleSpinClick.setMinimum(0.0)
            qDoubleSpinClick.setMaximum(2.0)
            qDoubleSpinClick.setSingleStep(.02)
            qDoubleSpinClick.setAdaptive(False)
          qDoubleSpinClick.setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
          qDoubleSpinClick.setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
          qDoubleSpinClick.setValue(target.value())
          self.rememberThis.append(target.value())
          qDoubleSpinClick.editingFinished.connect(partial(self.updateTargetQDoubleSpinClick, qDoubleSpinClick, target, len(self.rememberThis) - 1))
          qDoubleSpinClick.setToolTip(toolTip)
          layoutLine.addWidget(qDoubleSpinClick)
        elif(field[1] == 'QPushButtonCheckable'):
          qPushButtonCheckable = QPushButtonCheckable()
          qPushButtonCheckable.setMaximumSize(QtCore.QSize(max(target.width(), int(54 * SCALEFONT)), int(BASE_SIZE)))
          qPushButtonCheckable.setMinimumSize(QtCore.QSize(max(target.width(), int(54 * SCALEFONT)), int(BASE_SIZE)))
          if('Bold' in field[2]):
            qPushButtonCheckable.setText('B')
            font = qPushButtonCheckable.font()
            font.setBold(True)
            qPushButtonCheckable.setFont(font)
            qPushButtonCheckable.setMaximumSize(QtCore.QSize(min(target.width(), int(54 * SCALEFONT)), int(BASE_SIZE)))
            qPushButtonCheckable.setMinimumSize(QtCore.QSize(min(target.width(), int(54 * SCALEFONT)), int(BASE_SIZE)))
          elif('Italic' in field[2]):
            qPushButtonCheckable.setText('I')
            font = qPushButtonCheckable.font()
            font.setItalic(True)
            qPushButtonCheckable.setFont(font)
            qPushButtonCheckable.setMaximumSize(QtCore.QSize(min(target.width(), int(54 * SCALEFONT)), int(BASE_SIZE)))
            qPushButtonCheckable.setMinimumSize(QtCore.QSize(min(target.width(), int(54 * SCALEFONT)), int(BASE_SIZE)))
          elif('TickMark' in field[2]):
            if('$$' in field[2]):
              main, sub = field[2].split('$$')
            else:
              sub = 'Show'
            qPushButtonCheckable.setText(sub)
          elif('Grid' in field[2]):
            if('$$' in field[2]):
              main, sub = field[2].split('$$')
              if(main.endswith('2')):
                sub += ' min'
            else:
              sub = 'Show'
              if(field[2].endswith('2')):
                sub += ' min'
            qPushButtonCheckable.setText(sub + ' grid')
            if('Label3' in field[2]):
              qPushButtonCheckable.setText(sub + ' shading')
          elif('configAxisLabel' in field[2]):
            qPushButtonCheckable.setText('Axis')
          elif(field[2] in ['configXLabel', 'configYLabel', 'slavedXLabelShow']):
            qPushButtonCheckable.setText('Axis label')
          elif(field[2] in ['configTickXLabel', 'configTickYLabel', 'configInnerLabel', 'configTickResidYLabel']):
            qPushButtonCheckable.setText('Tick labels')
          elif(field[2].endswith('TrailZero')):
            qPushButtonCheckable.setText('trail 0s?')
          elif(field[2].endswith('Separator')):
            qPushButtonCheckable.setText('separate 1000s?')
          elif(field[2].endswith('Comma')):
            qPushButtonCheckable.setText('comma?')
          elif(field[2].endswith('AxesLabel')):
            qPushButtonCheckable.setText('axes')
          elif(field[2].endswith('InnerTickLabel') or field[2].endswith('SplitYTickLabel')):
            qPushButtonCheckable.setText('ticks')
          elif(field[2].endswith('FirstLabel')):
            qPushButtonCheckable.setText('1st')
            qPushButtonCheckable.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
            qPushButtonCheckable.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
          elif('BoundLabel' in field[2]):
            qPushButtonCheckable.setText('Active')
          elif('Auto' in field[2]):
            self.autoPushButton = qPushButtonCheckable
            qPushButtonCheckable.setText('auto')
          elif('configArrowFillColorLabel' in field[2]):
            qPushButtonCheckable.setText('Face')
          elif('configArrowLineColorLabel' in field[2]):
            qPushButtonCheckable.setText('Line')
          else:
            qPushButtonCheckable.setText('Show')
            qPushButtonCheckable.setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
            qPushButtonCheckable.setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
          qPushButtonCheckable.setChecked(target.isChecked())
          self.rememberThis.append(target.isChecked())
          qPushButtonCheckable.clicked.connect(partial(self.updateTargetQPushButtonCheckable, qPushButtonCheckable, target, len(self.rememberThis) - 1))
          qPushButtonCheckable.setToolTip(toolTip)
          layoutLine.addWidget(qPushButtonCheckable)
        elif(field[1] == 'QComboBoxMac'):
          qComboBoxMac = QComboBoxMac()
          qComboBoxMac.addItems([target.itemText(i) for i in range(target.count())])
          if(field[2].endswith('Font')):
            qComboBoxMac.setMaximumSize(QtCore.QSize(int(200 * SCALEFONT), int(BASE_SIZE)))
            qComboBoxMac.setMinimumSize(QtCore.QSize(int(200 * SCALEFONT), int(BASE_SIZE)))
          else:
            qComboBoxMac.setMaximumSize(QtCore.QSize(target.width(), int(BASE_SIZE)))
            qComboBoxMac.setMinimumSize(QtCore.QSize(target.width(), int(BASE_SIZE)))
          qComboBoxMac.setCurrentIndex(target.currentIndex())
          self.rememberThis.append(target.currentIndex())
          if('configCanvasGradientStyle' in field[2]):
            updateVisibility, currentVis = True, target.currentText()
            qComboBoxMac.activated.connect(partial(self.updateTargetQComboBoxMac, qComboBoxMac, target, len(self.rememberThis) - 1, True))
          else:
            qComboBoxMac.activated.connect(partial(self.updateTargetQComboBoxMac, qComboBoxMac, target, len(self.rememberThis) - 1, False))
          qComboBoxMac.setToolTip(toolTip)
          layoutLine.addWidget(qComboBoxMac)
        elif(field[1] == 'FontoBox'):
          qComboBoxMac = FontoBox()
          ###qComboBoxMac.addItems([target.itemText(i) for i in range(target.count())])
          qComboBoxMac.addItems(self.parent.parent.fontNames)
          qComboBoxMac.setMaximumSize(QtCore.QSize(int(200 * SCALEFONT), int(BASE_SIZE)))
          qComboBoxMac.setMinimumSize(QtCore.QSize(int(200 * SCALEFONT), int(BASE_SIZE)))
          qComboBoxMac.toggleActivate(False)
          # go via current text rather than index as there can be a disconnect between popup selector and main one
          qComboBoxMac.setMe(target.currentText())
          self.rememberThis.append(target.currentText())
          qComboBoxMac.activated.connect(partial(self.updateTargetFontoBox, qComboBoxMac, target, len(self.rememberThis) - 1))
          qComboBoxMac.setToolTip(toolTip)
          layoutLine.addWidget(qComboBoxMac)
          
    if(updateVisibility):
      self.updateFieldVisibility(active=currentVis)
    self.setFocus()
    self.focusNextChild()
    # advance one extra field in case of certain dialogs
    if(self.itemType in ['xlabel', 'ylabel', 'ylabel2'] or self.itemType.startswith('labliX_')\
       or self.itemType.startswith('labliY') or self.itemType.startswith('labliX2_')\
       or self.itemType.startswith('labliResid_')):
      self.focusNextChild()

  def updateTargetQLineEdit(self, source=None, target=None, original=None):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None) and (source.text() != self.rememberThis[original])):
      self.rememberThis[original] = source.text()
      target.setText(source.text())
      target.editingFinished.emit()

  def updateTargetQLineEditValues(self, source=None, target=None, original=None):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None) and (source.text() != self.rememberThis[original])):
      self.rememberThis[original] = source.text()
      target.setText(source.text())
      target.editingFinished.emit()
      if(self.autoPushButton != None):
        self.autoPushButton.setChecked(False)

  def updateTargetQPushButton(self, source=None, target=None):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None)):
      target.clicked.emit()

  def updateTargetQDoubleSpinClick(self, source=None, target=None, original=None):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None) and (source.value() != self.rememberThis[original])):
      self.rememberThis[original] = source.value()
      target.setValue(source.value())
      target.editingFinished.emit()

  def updateTargetQPushButtonCheckable(self, source=None, target=None, original=None):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None) and (source.isChecked() != self.rememberThis[original])):
      self.rememberThis[original] = source.isChecked()
      # prevent QMenu from vanishing
      ###self.togglePersist(True)
      target.setChecked(source.isChecked())
      target.clicked.emit()
      # bring back the QMenu
      ###self.togglePersist(False)
      # would only work if we waited for child dialog => rather don't do this

  def updateTargetFontoBox(self, source=None, target=None, original=None):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None) and (source.currentText() != self.rememberThis[original])):
      self.rememberThis[original] = source.currentText()
      target.setCurrentText(source.currentText())
      target.activated.emit(source.currentIndex())

  def updateTargetQComboBoxMac(self, source=None, target=None, original=None, updateVis=False):
    # updates config dialog on graphics tab and fires event
    if((source != None) and (target != None) and (source.currentIndex() != self.rememberThis[original])):
      self.rememberThis[original] = source.currentIndex()
      target.setCurrentIndex(source.currentIndex())
      target.activated.emit(source.currentIndex())
      if(updateVis):
        self.updateFieldVisibility(active=source.currentText())

  def updateFieldVisibility(self, active=None):
    # toggles visibility of fields for canvas gradient linear vs. radial
    for key, item in self.fieldVisibility.items():
      if(key == active):
        item.show()
      else:
        item.hide()
    self.adjustSize()

  def initConfigFields(self):
    # defines which entry fields and targets we need
    if(self.parent.parent.graphicsarea.advancedExport['advancedGraphics']):
      advanced = True
    else:
      advanced = False

    self.onSecond = False
    if(self.itemType in ['xlabel', 'ylabel', 'ylabel2']):
      if(self.itemType == 'xlabel'):
        axisLetter = 'x'
      else:
        axisLetter = 'y'
      self.configTitle = 'Configure ' + axisLetter + ' axis label'
      if(self.itemType.endswith('2')):
        self.onSecond = True
        self.configTitle = 'Configure ' + axisLetter + '2 axis label'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'config' + axisLetter.upper() + 'Label', True])
      self.configFields.append(['Text', 'QLineEdit', 'config' + axisLetter.upper() + 'Name', True])
      self.configFields.append(['Font', 'QPushButtonColor', 'config' + axisLetter.upper() + 'ColorButton', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'config' + axisLetter.upper() + 'Size', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'config' + axisLetter.upper() + 'Bold', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'config' + axisLetter.upper() + 'Italic', False])
      self.configFields.append([])
      self.configFields.append(['', 'QLabel', 'config' + axisLetter.upper() + 'Font', True])
      self.configFields.append(['', 'FontoBox', 'config' + axisLetter.upper() + 'Font', False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'config' + axisLetter.upper() + 'Angle', True])
        self.advancedFields.append(['Alignment', 'QComboBoxMac', 'config' + axisLetter.upper() + 'Alignment', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'config' + axisLetter.upper() + 'AlignmentVertical', False])
        self.advancedFields.append(['Linespacing', 'QLineEdit', 'config' + axisLetter.upper() + 'Linespacing', True])
        self.advancedFields.append(['Position', 'QLineEdit', 'config' + axisLetter.upper() + 'Pos', True])
        self.advancedFields.append(['Pad', 'QLineEdit', 'config' + axisLetter.upper() + 'Pad', False])
        self.advancedFields.append([])
    elif(self.itemType == 'slavedxlabel'):
      self.configTitle = 'Configure transformed x axis'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'slavedXLabelShow', True])
      self.configFields.append(['Text', 'QLineEdit', 'configSlavedXLabel', True])
      self.configFields.append(['Font', 'QPushButtonColor', 'slavedXColorButton', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'slavedXSize', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'slavedXBold', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'slavedXItalic', False])
      self.configFields.append([])
      self.configFields.append(['', 'QLabel', 'slavedXFont', True])
      self.configFields.append(['', 'FontoBox', 'slavedXFont', False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'slavedXAngle', True])
        self.advancedFields.append(['Alignment', 'QComboBoxMac', 'slavedXAlignment', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'slavedXAlignmentVertical', False])
        self.advancedFields.append(['Linespacing', 'QLineEdit', 'slavedXLinespacing', True])
        self.advancedFields.append(['Position', 'QLineEdit', 'slavedXPos', True])
        self.advancedFields.append(['Pad', 'QLineEdit', 'slavedXPad', False])
        self.advancedFields.append([])

        self.advancedFields.append(['Enable', 'QPushButtonCheckable', 'slavedXLabel', True])
        self.advancedFields.append(['Reset', 'QPushButtonMac', 'slavedXReset', True])
        self.advancedFields.append(['Operations', 'QPushButtonMac', 'slavedXPlus', True])
        self.advancedFields.append(['', 'QPushButtonMac', 'slavedXMinus', False])
        self.advancedFields.append(['', 'QPushButtonMac', 'slavedXTimes', False])
        self.advancedFields.append(['', 'QPushButtonMac', 'slavedXDivide', False])
        self.advancedFields.append(['', 'QPushButtonMac', 'slavedXPower', False])
        self.advancedFields.append(['', 'QLineEdit', 'slavedXNumber', False])
        self.advancedFields.append(['', 'QPushButtonMac', 'slavedXExponential', False])
        self.advancedFields.append(['', 'QPushButtonMac', 'slavedXNaturalLog', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('labliX_') or self.itemType.startswith('labliY')):
      if(self.itemType.startswith('labliX_')):
        axisLetter = 'x'
      else:
        axisLetter = 'y'
      self.configTitle = 'Configure ' + axisLetter + ' axis ticks'
      if(self.itemType.startswith('labliY2_')):
        self.configTitle = 'Configure y2 axis ticks'
        self.onSecond = True
      self.configFields.append(['Show', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'Label', True])
      self.configFields.append(['Values', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Entry', True])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'Auto', False])
      self.configFields.append(['Font', 'QPushButtonColor', 'configTick' + axisLetter.upper() + 'ColorButton', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'configTick' + axisLetter.upper() + 'Size', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'Bold', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'Italic', False])
      self.configFields.append([])
      self.configFields.append(['', 'QLabel', 'configTick' + axisLetter.upper() + 'Font', True])
      self.configFields.append(['', 'FontoBox', 'configTick' + axisLetter.upper() + 'Font', False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Minor', 'QPushButtonCheckable', 'configMinorTick' + axisLetter.upper() + 'Label', True])
        self.advancedFields.append(['Count', 'QComboBoxMac', 'configMinorTick' + axisLetter.upper(), False])
        self.advancedFields.append(['Rel. Length', 'QLineEdit', 'configMinorTick' + axisLetter.upper() + 'RelativeLength', False])
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configTick' + axisLetter.upper() + 'Angle', True])
        self.advancedFields.append(['Alignment', 'QComboBoxMac', 'configTick' + axisLetter.upper() + 'Alignment', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'configTick' + axisLetter.upper() + 'AlignmentVertical', False])
        self.advancedFields.append(['Pad', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Pad', True])
        self.advancedFields.append(['Pad2', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Pad2', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Format', 'QComboBoxMac', 'configTick' + axisLetter.upper() + 'Format', True])
        self.advancedFields.append(['Precision', 'QLineEdit', 'configTick' + axisLetter.upper() + 'FormatPrecision', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'FormatTrailZero', True])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'FormatSeparator', False])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'FormatComma', False])
        self.advancedFields.append(['', 'QLabel', 'configTick' + axisLetter.upper() + 'Prefix', True])
        self.advancedFields.append(['Prefix', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Prefix', False])
        self.advancedFields.append(['Postfix', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Postfix', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('labliSlavedX_')):
      axisLetter = 'x'
      self.configTitle = 'Configure transformed x axis ticks'
      self.configFields.append(['Transf.', 'QPushButtonCheckable', 'slavedXLabel', True])
      self.configFields.append(['Values', 'QLineEdit', 'slavedTickXEntry', True])
      self.configFields.append(['', 'QPushButtonCheckable', 'slavedTickXAuto', False])
      self.configFields.append(['Font', 'QPushButtonColor', 'slavedTickXColorButton', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'slavedTickXSize', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'slavedTickXBold', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'slavedTickXItalic', False])
      self.configFields.append([])
      self.configFields.append(['', 'QLabel', 'slavedTickXFont', True])
      self.configFields.append(['', 'FontoBox', 'slavedTickXFont', False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Minor', 'QPushButtonCheckable', 'slavedMinorTickXLabel', True])
        self.advancedFields.append(['Count', 'QComboBoxMac', 'slavedMinorTickX', False])
        self.advancedFields.append(['Rel. Length', 'QLineEdit', 'slavedMinorTickXRelativeLength', False])
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'slavedTickXAngle', True])
        self.advancedFields.append(['Alignment', 'QComboBoxMac', 'slavedTickXAlignment', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'slavedTickXAlignmentVertical', False])
        self.advancedFields.append(['Pad', 'QLineEdit', 'slavedTickXPad', True])
        self.advancedFields.append(['Pad2', 'QLineEdit', 'slavedTickXPad2', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Format', 'QComboBoxMac', 'slavedXFormat', True])
        self.advancedFields.append(['Precision', 'QLineEdit', 'slavedXFormatPrecision', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'slavedXFormatTrailZero', True])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'slavedXFormatSeparator', False])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'slavedXFormatComma', False])
        self.advancedFields.append(['', 'QLabel', 'configTick' + axisLetter.upper() + 'Prefix', True])
        self.advancedFields.append(['Prefix', 'QLineEdit', 'slavedXPrefix', False])
        self.advancedFields.append(['Postfix', 'QLineEdit', 'slavedXPostfix', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('labliX2_') or self.itemType.startswith('labliSlavedX2_')):
      self.configTitle = 'Configure split x axis ticks'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'configInnerLabel', True])
      self.configFields.append(['Values', 'QLineEdit', 'configInnerTickXEntry', True])
      self.configFields.append(['', 'QPushButtonCheckable', 'configInnerTickXAuto', False])
      self.configFields.append(['Font', 'QPushButtonColor', 'configTickXColorButton', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'configTickXSize', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTickXBold', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTickXItalic', False])
      self.configFields.append([])
      self.configFields.append(['', 'QLabel', 'configTickXFont', True])
      self.configFields.append(['', 'FontoBox', 'configTickXFont', False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Minor', 'QPushButtonCheckable', 'configInnerMinorTickLabel', True])
        self.advancedFields.append(['Count', 'QComboBoxMac', 'configInnerMinorTickX', False])
        self.advancedFields.append(['Rel. Length', 'QLineEdit', 'configMinorTickXRelativeLength', False])
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configTickXAngle', True])
        self.advancedFields.append(['Alignment', 'QComboBoxMac', 'configTickXAlignment', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'configTickXAlignmentVertical', False])
        self.advancedFields.append(['Linespacing', 'QLineEdit', 'configTickXLinespacing', True])
        self.advancedFields.append(['Pad', 'QLineEdit', 'configTickXPad', True])
        self.advancedFields.append(['Pad2', 'QLineEdit', 'configTickXPad2', False])
        self.advancedFields.append([])
        self.advancedFields.append([])
        self.advancedFields.append(['Format', 'QComboBoxMac', 'configInnerTickXFormat', True])
        self.advancedFields.append(['Precision', 'QLineEdit', 'configInnerTickXFormatPrecision', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configInnerTickXFormatTrailZero', True])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configInnerTickXFormatSeparator', False])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configInnerTickXFormatComma', False])
        self.advancedFields.append(['', 'QLabel', 'configInnerTickXPrefix', True])
        self.advancedFields.append(['Prefix', 'QLineEdit', 'configInnerTickXPrefix', False])
        self.advancedFields.append(['Postfix', 'QLineEdit', 'configInnerTickXPostfix', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Fraction', 'QLineEdit', 'xSplitFraction', True])
        self.advancedFields.append(['Pad', 'QLineEdit', 'xSplitPad', False])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configInnerAxesLabel', False])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configInnerTickLabel', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Divider', 'QPushButtonCheckable', 'configInnerDividerLine', True])
        self.advancedFields.append(['', 'QPushButtonColor', 'configInnerDividerLineColor', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configInnerDividerLine', True])
        self.advancedFields.append(['Length', 'QDoubleSpinClick', 'configInnerDividerLineLength', False])
        self.advancedFields.append(['Width', 'QDoubleSpinClick', 'configInnerDividerLineWidth', False])
        self.advancedFields.append(['', 'QComboBoxMac', 'configInnerDividerLineDashStyle', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configInnerDividerLine', True])
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configInnerDividerLineAngle', False])
        self.advancedFields.append(['Location', 'QComboBoxMac', 'configInnerDividerLineLocation', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('labliResid_')):
      self.configTitle = 'Configure residuals y axis ticks'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'configTickResidYLabel', True])
      self.configFields.append(['Values', 'QLineEdit', 'configTickResidYEntry', True])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTickResidYAuto', False])
      # duplicate controls from main y axis
      axisLetter = 'y'
      self.configFields.append(['Color', 'QPushButtonColor', 'configTick' + axisLetter.upper() + 'ColorButton', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'configTick' + axisLetter.upper() + 'Size', False])
      self.configFields.append([])
      self.configFields.append(['Style', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'Bold', True])
      self.configFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'Italic', False])
      self.configFields.append(['Font', 'FontoBox', 'configTick' + axisLetter.upper() + 'Font', True])
      if(advanced):
        self.advancedFields.append(['Minor', 'QPushButtonCheckable', 'configMinorTickResidLabel', True])
        self.advancedFields.append(['Count', 'QComboBoxMac', 'configMinorTickResid', False])
        self.advancedFields.append(['Rel. Length', 'QLineEdit', 'configMinorTickResidRelativeLength', False])
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configTick' + axisLetter.upper() + 'Angle', True])
        self.advancedFields.append(['Alignment', 'QComboBoxMac', 'configTick' + axisLetter.upper() + 'Alignment', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'configTick' + axisLetter.upper() + 'AlignmentVertical', False])
        self.advancedFields.append(['Pad', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Pad', True])
        self.advancedFields.append(['Pad2', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Pad2', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Format', 'QComboBoxMac', 'configTick' + axisLetter.upper() + 'Format', True])
        self.advancedFields.append(['Precision', 'QLineEdit', 'configTick' + axisLetter.upper() + 'FormatPrecision', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'FormatTrailZero', True])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'FormatSeparator', False])
        self.advancedFields.append(['', 'QPushButtonCheckable', 'configTick' + axisLetter.upper() + 'FormatComma', False])
        self.advancedFields.append(['', 'QLabel', 'configTick' + axisLetter.upper() + 'Prefix', True])
        self.advancedFields.append(['Prefix', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Prefix', False])
        self.advancedFields.append(['Postfix', 'QLineEdit', 'configTick' + axisLetter.upper() + 'Postfix', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('tickliX') or self.itemType.startswith('tickliSlavedX') or self.itemType.startswith('tickliY')):
      suffix = ''
      if(self.itemType.startswith('tickliX') or self.itemType.startswith('tickliSlavedX')):
        self.configTitle = 'Configure bottom/top axis tick marks'
        # bottom and top axes tick marks are linked => sufficient to do this for bottom axis
        axisLetter = 'bottom'
        self.configFields.append(['Show', 'QPushButtonCheckable', 'configTickMarkLabel$$bottom', True])
        self.configFields.append(['', 'QPushButtonCheckable', 'configTickMarkLabel$$top', False])
      else:
        if(self.itemType.startswith('tickliY2_')):
          self.configTitle = 'Configure left2/right2 axis tick marks'
          self.onSecond = True
          suffix = '2'
        else:
          self.configTitle = 'Configure left/right axis tick marks'
        axisLetter = 'left'
        self.configFields.append(['Show', 'QPushButtonCheckable', 'configTickMarkLabel$$left' + suffix, True])
        self.configFields.append(['', 'QPushButtonCheckable', 'configTickMarkLabel$$right' + suffix, False])
      self.configFields.append(['Direction', 'QComboBoxMac', 'configTickMarkDirection$$' + axisLetter + suffix, True])
      self.configFields.append(['Color', 'QPushButtonColor', 'configTickMarkColor$$' + axisLetter + suffix, True])
      self.configFields.append(['Width', 'QDoubleSpinClick', 'configTickMarkWidth$$' + axisLetter + suffix, True])
      self.configFields.append(['Length', 'QDoubleSpinClick', 'configTickMarkLength$$' + axisLetter + suffix, True])
      if(axisLetter == 'bottom'):
        self.configFields.append(['Label', 'QPushButtonCheckable', 'configTickMarkLabelShow$$bottom', True])
        self.configFields.append(['', 'QPushButtonCheckable', 'configTickMarkLabelShow$$top', False])
      else:
        self.configFields.append(['Label', 'QPushButtonCheckable', 'configTickMarkLabelShow$$left' + suffix, True])
        self.configFields.append(['', 'QPushButtonCheckable', 'configTickMarkLabelShow$$right' + suffix, False])
    elif(self.itemType.startswith('gridliX') or self.itemType.startswith('gridliY')):
      if(self.itemType.startswith('gridliX_')):
        axisLetter = 'x'
      elif(self.itemType.startswith('gridliX2_')):
        axisLetter = 'x2'
      elif(self.itemType.startswith('gridliY_')):
        axisLetter = 'y'
      else:
        axisLetter = 'y2'
      if(self.itemType.startswith('gridliY2_')):
        self.onSecond = True
      if(axisLetter == 'x2'):
        self.configTitle = 'Configure split x axis grid'
      else:
        self.configTitle = 'Configure ' + axisLetter + ' axis grid'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'configGridLabel$$' + axisLetter, True])
      self.configFields.append(['Order', 'QComboBoxMac', 'configGridOrder$$' + axisLetter, True])
      self.configFields.append(['Color', 'QPushButtonColor', 'configGridColor$$' + axisLetter, True])
      self.configFields.append(['Width', 'QDoubleSpinClick', 'configGridWidth$$' + axisLetter, True])
      self.configFields.append(['Style', 'QComboBoxMac', 'configGridStyle$$' + axisLetter, True])
      if(advanced):
        self.advancedFields.append(['Show', 'QPushButtonCheckable', 'configGridLabel3$$' + axisLetter, True])
        self.advancedFields.append(['Color', 'QPushButtonColor', 'configGridRectColor$$' + axisLetter, True])
    elif(self.itemType.startswith('minorGridliX') or self.itemType.startswith('minorGridliY')):
      if(self.itemType.startswith('minorGridliX_')):
        axisLetter = 'x'
      elif(self.itemType.startswith('minorGridliX2_')):
        axisLetter = 'x2'
      elif(self.itemType.startswith('minorGridliY_')):
        axisLetter = 'y'
      else:
        axisLetter = 'y2'
      if(self.itemType.startswith('minorGridliY2_')):
        self.onSecond = True
      if(axisLetter == 'x2'):
        self.configTitle = 'Configure split x axis minor grid'
      else:
        self.configTitle = 'Configure ' + axisLetter + ' axis minor grid'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'configGridLabel2$$' + axisLetter, True])
      self.configFields.append(['Order', 'QComboBoxMac', 'configGridOrder2$$' + axisLetter, True])
      self.configFields.append(['Color', 'QPushButtonColor', 'configGridColor2$$' + axisLetter, True])
      self.configFields.append(['Width', 'QDoubleSpinClick', 'configGridWidth2$$' + axisLetter, True])
      self.configFields.append(['Style', 'QComboBoxMac', 'configGridStyle2$$' + axisLetter, True])
    elif(self.itemType == 'legend'):
      self.configTitle = 'Configure legend'
      self.configFields.append(['Legend', 'QPushButtonCheckable', 'configLegendLabel', True])
      self.configFields.append(['', 'QComboBoxMac', 'configLegendPlacement', False])
      self.configFields.append([])
      for prop in ['face', 'edge']:
        self.configFields.append([prop.title(), 'QPushButtonCheckable', 'configLegendColorLabel$$' + prop, True])
        self.configFields.append(['', 'QPushButtonColor', 'configLegendColor$$' + prop, False])
      self.configFields.append(['Width', 'QDoubleSpinClick', 'configLegendEdgeWidth', False])
      self.configFields.append([])
      self.configFields.append(['Font', 'QPushButtonColor', 'configLegendLabelColor', True])
      self.configFields.append(['Size', 'QDoubleSpinClick', 'configLegendLabelSize', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configLegendLabelBold', False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configLegendLabelItalic', False])
      self.configFields.append([])
      self.configFields.append(['', 'QLabel', 'configLegendLabelFont', True])
      self.configFields.append(['', 'FontoBox', 'configLegendLabelFont', False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Position', 'QLabel', 'configLegendXLabel', True])
        self.advancedFields.append(['x', 'QLineEdit', 'configLegendX', False])
        self.advancedFields.append(['y', 'QLineEdit', 'configLegendY', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Linespacing', 'QLabel', 'configLegendLinespacingLabel', True])
        self.advancedFields.append(['', 'QLineEdit', 'configLegendLinespacing', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Shadow', 'QPushButtonCheckable', 'configLegendShadowLabel', True])
        self.advancedFields.append(['', 'QPushButtonColor', 'configLegendShadowColor', False])
        self.advancedFields.append(['OffX', 'QLineEdit', 'configLegendShadowOffX', False])
        self.advancedFields.append(['OffY', 'QLineEdit', 'configLegendShadowOffY', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Hatch', 'QComboBoxMac', 'legendComboHatchStyle', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'legendComboHatchMultiplyStyle', False])
        self.advancedFields.append(['# line', 'QDoubleSpinClick', 'legendHatchLineWidthSpin', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Rounding', 'QLineEdit', 'configLegendRounding', True])
        self.advancedFields.append(['NCol', 'QComboBoxMac', 'configLegendNCol', True])
        self.advancedFields.append(['Marker', 'QPushButtonCheckable', 'configLegendMarkerFirstLabel', True])
        self.advancedFields.append(['Scale', 'QLineEdit', 'configLegendMarkerScale', False])
        self.advancedFields.append(['Points', 'QComboBoxMac', 'configLegendMarkerNumPoints', False])
        self.advancedFields.append(['Pad Border', 'QLineEdit', 'configLegendPadBorder', True])
        self.advancedFields.append(['Row', 'QLineEdit', 'configLegendPadRow', False])
        self.advancedFields.append(['Col', 'QLineEdit', 'configLegendPadCol', False])
        self.advancedFields.append(['Label', 'QLineEdit', 'configLegendLabelPad', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('spine_')):
      main, sub = self.itemType.split('_')
      if(sub in ['bottom', 'top']):
        useAxis = 'x'
      else:
        useAxis = 'y'
      self.configTitle = 'Configure ' + sub + ' axis line'
      self.configFields.append(['Show', 'QPushButtonCheckable', 'configAxisLabel$$' + sub, True])
      self.configFields.append(['Style', 'QPushButtonColor', 'configAxisColor$$' + sub, True])
      self.configFields.append(['Width', 'QDoubleSpinClick', 'configAxisWidth$$' + sub, False])
      self.configFields.append(['', 'QComboBoxMac', 'configAxisStyle$$' + sub, False])
      self.configFields.append([])
      self.configFields.append(['Arrow ' + useAxis, 'QPushButtonCheckable', 'configArrowLabel$$' + useAxis, True])
      self.configFields.append(['', 'QPushButtonCheckable', 'configArrowFillColorLabel$$' + useAxis, False])
      self.configFields.append(['', 'QPushButtonColor', 'configArrowFillColor$$' + useAxis, False])
      self.configFields.append(['', 'QPushButtonCheckable', 'configArrowLineColorLabel$$' + useAxis, False])
      self.configFields.append(['', 'QPushButtonColor', 'configArrowLineColor$$' + useAxis, False])
      self.configFields.append(['', 'QDoubleSpinClick', 'configArrowLineWidth$$' + useAxis, False])
      self.configFields.append(['', 'QComboBoxMac', 'configArrowZ$$' + useAxis, False])
      self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['', 'QLabel', 'configArrowHeadLength$$' + useAxis, True])
        self.advancedFields.append(['Length', 'QLineEdit', 'configArrowHeadLength$$' + useAxis, False])
        self.advancedFields.append(['Width', 'QLineEdit', 'configArrowHeadWidth$$' + useAxis, False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configArrowOverhang$$' + useAxis, True])
        self.advancedFields.append(['Indent', 'QLineEdit', 'configArrowOverhang$$' + useAxis, False])
        self.advancedFields.append(['Offset', 'QLineEdit', 'configArrowOffset$$' + useAxis, False])
        self.advancedFields.append(['', 'QComboBoxMac', 'configArrowLocation$$' + useAxis, False])
        self.advancedFields.append([])
        self.advancedFields.append(['Dash', 'QComboBoxMac', 'configAxisDashStyle$$' + sub, True])
        self.advancedFields.append(['Boundary', 'QPushButtonCheckable', 'configAxisBoundLabel$$' + sub, True])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configAxisBoundLower$$' + sub, True])
        self.advancedFields.append(['Lower', 'QLineEdit', 'configAxisBoundLower$$' + sub, False])
        self.advancedFields.append(['Upper', 'QLineEdit', 'configAxisBoundUpper$$' + sub, False])
        self.advancedFields.append([])
        self.advancedFields.append(['Position', 'QComboBoxMac', 'configAxisPosition$$' + sub, True])
        self.advancedFields.append(['', 'QLineEdit', 'configAxisPositionValue$$' + sub, False])
        self.advancedFields.append(['', 'QPushButton', 'configAxisPositionReset$$' + sub, False])
    elif(self.itemType == 'canvas'):
      self.configTitle = 'Configure canvas and figure'
      self.configFields.append(['Figure', 'QPushButtonCheckable', 'configFigureLabel', True])
      self.configFields.append(['', 'QPushButtonColor', 'configFigureColorButton', False])
      self.configFields.append([])
      self.configFields.append(['Canvas', 'QPushButtonCheckable', 'configCanvasLabel', True])
      self.configFields.append(['', 'QPushButtonColor', 'configCanvasColorButton', False])
      self.configFields.append([])
      self.configFields.append(['Plot width', 'QLineEdit', 'exportSizeX', True])
      self.configFields.append(['height', 'QLineEdit', 'exportSizeY', False])
      self.configFields.append([])
      for index, pad in enumerate(['bottom', 'top', 'left', 'right']):
        if(not (index % 2)):
          leader = 'Pad '
          flag = True
        else:
          leader = ''
          flag = False
        self.configFields.append([leader + pad, 'QLineEdit', 'exportPadEntry$$' + pad, flag])
        if(not flag):
          self.configFields.append([])
      if(advanced):
        self.advancedFields.append(['Gradient', 'QPushButtonCheckable', 'configCanvasGradientLabel', True])
        self.advancedFields.append(['', 'QComboBoxMac', 'configCanvasGradientStyle', False])
        self.advancedFields.append(['', 'QPushButtonColor', 'configCanvasGradientColor1Button', False])
        self.advancedFields.append(['', 'QPushButtonColor', 'configCanvasGradientColor2Button', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configCanvasGradientAngle', True])
        self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configCanvasGradientAngle', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configCanvasGradientCenterX', True])
        self.advancedFields.append(['Center', 'QLineEdit', 'configCanvasGradientCenterX', False])
        self.advancedFields.append(['', 'QLineEdit', 'configCanvasGradientCenterY', False])
        self.advancedFields.append(['Width', 'QLineEdit', 'configCanvasGradientWidth', False])
        self.advancedFields.append([])
        self.advancedFields.append(['Frame', 'QPushButtonCheckable', 'configFrameLabel', True])
        self.advancedFields.append(['', 'QPushButtonColor', 'configFrameColor', False])
        self.advancedFields.append(['Width', 'QDoubleSpinClick', 'configFrameWidth', False])
        self.advancedFields.append([])
        self.advancedFields.append(['', 'QLabel', 'configFrameStyle', True])
        self.advancedFields.append(['Style', 'QComboBoxMac', 'configFrameStyle', False])
        self.advancedFields.append(['', 'QComboBoxMac', 'configFrameDashStyle', False])
        self.advancedFields.append([])
    elif(self.itemType.startswith('split') or self.itemType.startswith('divider')):
      if(self.itemType.endswith('X') or self.itemType.startswith('dividerX')):
        self.configTitle = 'Configure x axis split'
        self.configFields.append(['Fraction', 'QLineEdit', 'xSplitFraction', False])
        self.configFields.append(['Pad', 'QLineEdit', 'xSplitPad', False])
        self.configFields.append(['', 'QPushButtonCheckable', 'configInnerAxesLabel', False])
        self.configFields.append(['', 'QPushButtonCheckable', 'configInnerTickLabel', False])
        self.configFields.append([])
        # implement check to determine whether click was on divider lines
        if(advanced or self.itemType.startswith('dividerX')):
          self.advancedFields.append(['Divider', 'QPushButtonCheckable', 'configInnerDividerLine', True])
          self.advancedFields.append(['', 'QPushButtonColor', 'configInnerDividerLineColor', False])
          self.advancedFields.append([])
          self.advancedFields.append(['', 'QLabel', 'configInnerDividerLine', True])
          self.advancedFields.append(['Length', 'QDoubleSpinClick', 'configInnerDividerLineLength', False])
          self.advancedFields.append(['Width', 'QDoubleSpinClick', 'configInnerDividerLineWidth', False])
          self.advancedFields.append(['', 'QComboBoxMac', 'configInnerDividerLineDashStyle', False])
          self.advancedFields.append([])
          self.advancedFields.append(['', 'QLabel', 'configInnerDividerLine', True])
          self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configInnerDividerLineAngle', False])
          self.advancedFields.append(['Location', 'QComboBoxMac', 'configInnerDividerLineLocation', False])
          self.advancedFields.append([])
      else:
        self.configTitle = 'Configure y axis split'
        self.onSecond = True
        self.configFields.append(['Fraction', 'QLineEdit', 'configSplitYFraction', False])
        self.configFields.append(['Pad', 'QLineEdit', 'configSplitYPad', False])
        self.configFields.append(['', 'QPushButtonCheckable', 'configSplitYAxesLabel', False])
        self.configFields.append(['', 'QPushButtonCheckable', 'configSplitYTickLabel', False])
        self.configFields.append([])
        # implement check to determine whether click was on divider lines
        if(advanced or self.itemType.startswith('dividerY')):
          self.advancedFields.append(['Divider', 'QPushButtonCheckable', 'configSplitYDividerLine', True])
          self.advancedFields.append(['', 'QPushButtonColor', 'configSplitYDividerLineColor', False])
          self.advancedFields.append([])
          self.advancedFields.append(['', 'QLabel', 'configSplitYDividerLine', True])
          self.advancedFields.append(['Length', 'QDoubleSpinClick', 'configSplitYDividerLineLength', False])
          self.advancedFields.append(['Width', 'QDoubleSpinClick', 'configSplitYDividerLineWidth', False])
          self.advancedFields.append(['', 'QComboBoxMac', 'configSplitYDividerLineDashStyle', False])
          self.advancedFields.append([])
          self.advancedFields.append(['', 'QLabel', 'configSplitYDividerLine', True])
          self.advancedFields.append(['Angle', 'QDoubleSpinClick', 'configSplitYDividerLineAngle', False])
          self.advancedFields.append(['Location', 'QComboBoxMac', 'configSplitYDividerLineLocation', False])
          self.advancedFields.append([])
    else:
      pass

class ConfigMenuMyMaster(KuhMenu):
  def __init__(self, parent=None, kind='data'):
    super(ConfigMenuMyMaster, self).__init__()
    self.parent = parent
    self.kind = kind
    self.validFloat = MyValidFloat()
    self.persist = False
    self.persistPos = None
    
    # initialize drag values
    self.startPos = None
      
    # set up GUI
    self.buildRessource()
    
  def closeEvent(self, event):
    # hijack close event to update settings
    self.parent.variationStyle.update(self.style)
    # need to accept to really close the QMenu
    event.accept()
    
  def togglePersist(self, state):
    # toggles QMenu persistence for click events that would close it, see below
    # when state True, save current menu coordinates
    # when state False, restore QMenu at previous coordinates
    self.persist = state
    if(state):
      self.persistPos = self.pos()
    elif(self.persistPos != None):
      self.popup(self.persistPos)
      self.persistPos = None

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(self.underMouse()):
      if(event.button() == QtCore.Qt.MouseButton.LeftButton):
        self.startPos = event.globalPosition().toPoint()
    else:
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def mouseMoveEvent(self, event):
    if((event.buttons() == QtCore.Qt.MouseButton.LeftButton) and (self.startPos != None)):
      currentPos = event.globalPosition().toPoint()
      menuPos = self.mapToGlobal(self.pos()) + (currentPos - self.startPos)
      menuPos = self.mapFromGlobal(menuPos)
      self.move(menuPos)
      self.startPos = currentPos
    
  def mouseReleaseEvent(self, event):
    # turn off tracking
    if(event.button() == QtCore.Qt.MouseButton.LeftButton):
      self.startPos = None

  def buildRessource(self):
    # set a few variables
    self.itemOptions = 'line,marker,mark-line'.split(',')
    self.itemOptions2 = ['line']
    if(self.kind != 'fit'):
      self.itemOptions += 'bar,stack'.split(',')
      self.itemOptions2 += 'bar,stack'.split(',')
    self.colormaps = sorted(plt.colormaps(), key=str.casefold)
    self.linestyles = 'solid,dashed,dashdot,dotted'.split(',')
    self.markerstyles = []
    self.markerstyles.extend(matplotlib.lines.Line2D.markers)
    orderedstyles = ['.', 'o', 'v', '^', '<', '>', '1', '2', '3', '4', '8', 's', 'p', '*', 'h', 'H', '+',
                     'x', 'D', 'd', '|', '_', 'P', 'X', 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    self.markerstyles = [i for i in orderedstyles if (i in self.markerstyles)]
    self.style = {'varyItem': 'line', 'varyItem2': 'line', 'varyCycle': 10, 'varyCycleOffset': 0, 'varyCmap': 'jet',
                  'varyLinestyle': 2, 'varyLinestyleOffset': 0, 'varyMarkerstyle': 4, 'varyMarkerstyleOffset': 0, 'visOnly': True}
    # read out previous settings
    self.style.update(self.parent.variationStyle)
    
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[4]*4)

    # heading
    itemType = {'data': 'data', 'fit': 'curve', 'resid': 'residuals'}
    self.varyStyleLabel = QtWidgets.QLabel()
    self.varyStyleLabel.setText('Vary ' + itemType[self.kind] + ' items')
    font = self.varyStyleLabel.font()
    font.setBold(True)
    self.varyStyleLabel.setFont(font)
    self.vLayout.addWidget(self.varyStyleLabel)
    
    # vary color via cmap
    self.varyColorGroup = QWidgetMac()
    self.vLayout.addWidget(self.varyColorGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.varyColorGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.varyColorButton = QPushButtonMac()
    self.varyColorButton.setText('Color')
    self.varyColorButton.setToolTip('Vary color of objects by color map')
    self.varyColorButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyColorButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyColorButton.clicked.connect(self.doVariationColor)
    self.hLayout.addWidget(self.varyColorButton)

    offsetLabel = QtWidgets.QLabel('offset')
    offsetLabel.setMaximumHeight(int(BASE_SIZE))
    offsetLabel.setMinimumHeight(int(BASE_SIZE))
    self.hLayout.addWidget(offsetLabel)
    
    self.varyColorOffsetEntry = QSpinClick()
    self.varyColorOffsetEntry.setToolTip('Offset color cycle by how many steps')
    self.varyColorOffsetEntry.setMinimum(0)
    self.varyColorOffsetEntry.setMaximum(20)
    self.varyColorOffsetEntry.setValue(self.style['varyCycleOffset'])
    self.varyColorOffsetEntry.editingFinished.connect(partial(self.updateSpinClick, 'varyCycleOffset'))
    self.varyColorOffsetEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.varyColorOffsetEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.varyColorOffsetEntry)

    stepsLabel = QtWidgets.QLabel('steps')
    stepsLabel.setMaximumHeight(int(BASE_SIZE))
    stepsLabel.setMinimumHeight(int(BASE_SIZE))
    self.hLayout.addWidget(stepsLabel)

    self.varyColorEntry = QSpinClick()
    self.varyColorEntry.setToolTip('Number of steps for color cycle')
    self.varyColorEntry.setMinimum(1)
    self.varyColorEntry.setMaximum(20)
    self.varyColorEntry.setValue(self.style['varyCycle'])
    self.varyColorEntry.editingFinished.connect(partial(self.updateSpinClick, 'varyCycle'))
    self.varyColorEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.varyColorEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.varyColorEntry)

    self.whichItem = QComboBoxMac()
    self.whichItem.setToolTip('Which item to vary?')
    for entry in self.itemOptions:
      self.whichItem.addItem(entry)
    if(self.style['varyItem'] in self.itemOptions):
      currindex = self.itemOptions.index(self.style['varyItem'])
    else:
      currindex = 0
    self.whichItem.setCurrentIndex(currindex)
    self.whichItem.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.whichItem.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.whichItem.currentIndexChanged.connect(partial(self.updateComboBox, 'varyItem'))
    self.hLayout.addWidget(self.whichItem)

    # variable color map
    self.varyColorMapBox = CmapButton()
    self.varyColorMapBox.setToolTip('Map for color cycle')
    self.varyColorMapBox.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.varyColorMapBox.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    if(self.style['varyCmap'] in self.colormaps):
      self.varyColorMapBox.setCmap(self.style['varyCmap'])
    else:
      self.varyColorMapBox.setCmap(self.colormaps[0])
    self.varyColorMapBox.setParent(self)
    self.varyColorMapBox.setKey('varyCmap')
    self.varyColorMapBox.setCmapList(self.colormaps)
    self.varyColorMapBox.clicked.connect(partial(self.varyCallback, self.varyColorMapBox, 'varyCmap'))
    self.hLayout.addWidget(self.varyColorMapBox)

    self.hLayout.addStretch()

    # vary linestyles
    self.varyLinestyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.varyLinestyleGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.varyLinestyleGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.varyLinestyleButton = QPushButtonMac()
    self.varyLinestyleButton.setText('Style')
    self.varyLinestyleButton.setToolTip('Vary linestyle of objects')
    self.varyLinestyleButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyLinestyleButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyLinestyleButton.clicked.connect(self.doVariationLinestyle)
    self.hLayout2.addWidget(self.varyLinestyleButton)

    offsetLabel = QtWidgets.QLabel('offset')
    offsetLabel.setMaximumHeight(int(BASE_SIZE))
    offsetLabel.setMinimumHeight(int(BASE_SIZE))
    self.hLayout2.addWidget(offsetLabel)
    
    self.varyLinestyleOffsetEntry = QSpinClick()
    self.varyLinestyleOffsetEntry.setToolTip('Offset linestyle cycle by how many steps')
    self.varyLinestyleOffsetEntry.setMinimum(0)
    self.varyLinestyleOffsetEntry.setMaximum(len(self.linestyles) - 1)
    self.varyLinestyleOffsetEntry.setValue(self.style['varyLinestyleOffset'])
    self.varyLinestyleOffsetEntry.editingFinished.connect(partial(self.updateSpinClick, 'varyLinestyleOffset'))
    self.varyLinestyleOffsetEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.varyLinestyleOffsetEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.varyLinestyleOffsetEntry)

    stepsLabel = QtWidgets.QLabel('steps')
    stepsLabel.setMaximumHeight(int(BASE_SIZE))
    stepsLabel.setMinimumHeight(int(BASE_SIZE))
    self.hLayout2.addWidget(stepsLabel)

    self.varyLinestyleEntry = QSpinClick()
    self.varyLinestyleEntry.setToolTip('Number of steps for linestyle cycle')
    self.varyLinestyleEntry.setMinimum(1)
    self.varyLinestyleEntry.setMaximum(len(self.linestyles))
    self.varyLinestyleEntry.setValue(self.style['varyLinestyle'])
    self.varyLinestyleEntry.editingFinished.connect(partial(self.updateSpinClick, 'varyLinestyle'))
    self.varyLinestyleEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.varyLinestyleEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.varyLinestyleEntry)

    self.whichItem2 = QComboBoxMac()
    self.whichItem2.setToolTip('Which item to vary?')
    for entry in self.itemOptions2:
      self.whichItem2.addItem(entry)
    if(self.style['varyItem2'] in self.itemOptions2):
      currindex = self.itemOptions2.index(self.style['varyItem2'])
    else:
      currindex = 0
    self.whichItem2.setCurrentIndex(currindex)
    self.whichItem2.currentIndexChanged.connect(partial(self.updateComboBox, 'varyItem2'))
    self.whichItem2.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.whichItem2.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.whichItem2)
    self.hLayout2.addStretch()

    # vary markerstyles
    self.varyMarkerstyleGroup = QWidgetMac()
    self.vLayout.addWidget(self.varyMarkerstyleGroup)
    self.hLayout25 = QtWidgets.QHBoxLayout(self.varyMarkerstyleGroup)
    self.hLayout25.setContentsMargins(0, 0, 0, 0)
    self.hLayout25.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout25.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.varyMarkerstyleButton = QPushButtonMac()
    self.varyMarkerstyleButton.setText('Marker')
    self.varyMarkerstyleButton.setToolTip('Vary markerstyle of objects')
    self.varyMarkerstyleButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyMarkerstyleButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyMarkerstyleButton.clicked.connect(self.doVariationMarkerstyle)
    self.hLayout25.addWidget(self.varyMarkerstyleButton)

    offsetLabel = QtWidgets.QLabel('offset')
    offsetLabel.setMaximumHeight(int(BASE_SIZE))
    offsetLabel.setMinimumHeight(int(BASE_SIZE))
    self.hLayout25.addWidget(offsetLabel)
    
    self.varyMarkerstyleOffsetEntry = QSpinClick()
    self.varyMarkerstyleOffsetEntry.setToolTip('Offset markerstyle cycle by how many steps')
    self.varyMarkerstyleOffsetEntry.setMinimum(0)
    self.varyMarkerstyleOffsetEntry.setMaximum(len(self.markerstyles) - 1)
    self.varyMarkerstyleOffsetEntry.setValue(self.style['varyMarkerstyleOffset'])
    self.varyMarkerstyleOffsetEntry.editingFinished.connect(partial(self.updateSpinClick, 'varyMarkerstyleOffset'))
    self.varyMarkerstyleOffsetEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.varyMarkerstyleOffsetEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout25.addWidget(self.varyMarkerstyleOffsetEntry)

    stepsLabel = QtWidgets.QLabel('steps')
    stepsLabel.setMaximumHeight(int(BASE_SIZE))
    stepsLabel.setMinimumHeight(int(BASE_SIZE))
    self.hLayout25.addWidget(stepsLabel)

    self.varyMarkerstyleEntry = QSpinClick()
    self.varyMarkerstyleEntry.setToolTip('Number of steps for markerstyle cycle')
    self.varyMarkerstyleEntry.setMinimum(1)
    self.varyMarkerstyleEntry.setMaximum(len(self.markerstyles))
    self.varyMarkerstyleEntry.setValue(self.style['varyMarkerstyle'])
    self.varyMarkerstyleEntry.editingFinished.connect(partial(self.updateSpinClick, 'varyMarkerstyle'))
    self.varyMarkerstyleEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.varyMarkerstyleEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout25.addWidget(self.varyMarkerstyleEntry)
    self.hLayout25.addStretch()

    # apply to visible items only?
    self.varyVisibleGroup = QWidgetMac()
    self.vLayout.addWidget(self.varyVisibleGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.varyVisibleGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.varyVisibleLabel = QPushButtonCheckable()
    self.varyVisibleLabel.setText('vis. only')
    self.varyVisibleLabel.setToolTip('Apply variation to visible objects only?')
    self.varyVisibleLabel.setChecked(self.style['visOnly'])
    self.varyVisibleLabel.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyVisibleLabel.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.varyVisibleLabel.clicked.connect(self.updateVisibleLabel)
    self.hLayout3.addWidget(self.varyVisibleLabel)
    self.hLayout3.addStretch()

  def updateSpinClick(self, style='varyCycle'):
    if(style == 'varyCycle'):
      self.style['varyCycle'] = self.varyColorEntry.value()
    elif(style == 'varyCycleOffset'):
      self.style['varyCycleOffset'] = self.varyColorOffsetEntry.value()
    elif(style == 'varyLinestyle'):
      self.style['varyLinestyle'] = self.varyLinestyleEntry.value()
    elif(style == 'varyLinestyleOffset'):
      self.style['varyLinestyleOffset'] = self.varyLinestyleOffsetEntry.value()
    elif(style == 'varyMarkerstyle'):
      self.style['varyMarkerstyle'] = self.varyMarkerstyleEntry.value()
    elif(style == 'varyMarkerstyleOffset'):
      self.style['varyMarkerstyleOffset'] = self.varyMarkerstyleOffsetEntry.value()

  def updateComboBox(self, style='varyItem'):
    if(style == 'varyItem'):
      self.style['varyItem'] = self.itemOptions[self.whichItem.currentIndex()]
    elif(style == 'varyItem2'):
      self.style['varyItem2'] = self.itemOptions2[self.whichItem2.currentIndex()]

  def updateVisibleLabel(self):
    self.style['visOnly'] = self.varyVisibleLabel.isChecked()

  def doVariationMarkerstyle(self):
    steps = self.style['varyMarkerstyle']
    redraw = True
    offset = self.style['varyMarkerstyleOffset']

    if(self.kind == 'data'):
      if(self.varyVisibleLabel.isChecked()):
        useData = [i for i in self.parent.parent.data if i.visibility]
      else:
        useData = self.parent.parent.data
      if(not len(useData)):
        redraw = False
      for index, data in enumerate(useData):
        useThis = (index % steps) + offset
        useThis = useThis % len(self.markerstyles)
        markerstyle = self.markerstyles[useThis]
        data.setStyle('marker', markerstyle, redraw=False)
    elif(self.kind == 'resid'):
      if(self.varyVisibleLabel.isChecked()):
        useData = [i for i in self.parent.parent.data if i.visibilityResid]
      else:
        useData = self.parent.parent.data
      if(not len(useData)):
        redraw = False
      for index, data in enumerate(useData):
        useThis = (index % steps) + offset
        useThis = useThis % len(self.markerstyles)
        markerstyle = self.markerstyles[useThis]
        data.setResidStyle('marker', markerstyle, redraw=False)
    elif(self.kind == 'fit'):
      if(self.varyVisibleLabel.isChecked()):
        useFit = [i for i in self.parent.parent.fit if i.visibility]
      else:
        useFit = self.parent.parent.fit
      if(not len(useFit)):
        redraw = False
      for index, fit in enumerate(useFit):
        useThis = (index % steps) + offset
        useThis = useThis % len(self.markerstyles)
        markerstyle = self.markerstyles[useThis]
        fit.setStyle('marker', markerstyle, redraw=False)

    if(redraw):
      self.updateLegend(redraw=redraw)

  def doVariationLinestyle(self):
    item = self.style['varyItem2']
    steps = self.style['varyLinestyle']
    redraw = True
    offset = self.style['varyLinestyleOffset']

    if(self.kind == 'data'):
      if(self.varyVisibleLabel.isChecked()):
        useData = [i for i in self.parent.parent.data if i.visibility]
      else:
        useData = self.parent.parent.data
      if(not len(useData)):
        redraw = False
      for index, data in enumerate(useData):
        useThis = (index % steps) + offset
        useThis = useThis % len(self.linestyles)
        linestyle = self.linestyles[useThis]
        if(item == 'line'):
          data.setStyle('linestyle', linestyle, redraw=False)
        elif(item == 'bar'):
          data.setBarStyle('linestyle', linestyle, redraw=False)
        elif(item == 'stack'):
          data.setStackStyle('linestyle', linestyle, redraw=False)
    elif(self.kind == 'resid'):
      if(self.varyVisibleLabel.isChecked()):
        useData = [i for i in self.parent.parent.data if i.visibilityResid]
      else:
        useData = self.parent.parent.data
      if(not len(useData)):
        redraw = False
      for index, data in enumerate(useData):
        useThis = (index % steps) + offset
        useThis = useThis % len(self.linestyles)
        linestyle = self.linestyles[useThis]
        if(item == 'line'):
          data.setResidStyle('linestyle', linestyle, redraw=False)
        elif(item == 'bar'):
          data.setResidBarStyle('linestyle', linestyle, redraw=False)
        elif(item == 'stack'):
          data.setResidStackStyle('linestyle', linestyle, redraw=False)
    elif(self.kind == 'fit'):
      if(self.varyVisibleLabel.isChecked()):
        useFit = [i for i in self.parent.parent.fit if i.visibility]
      else:
        useFit = self.parent.parent.fit
      if(not len(useFit)):
        redraw = False
      for index, fit in enumerate(useFit):
        useThis = (index % steps) + offset
        useThis = useThis % len(self.linestyles)
        linestyle = self.linestyles[useThis]
        if(item == 'line'):
          fit.setStyle('linestyle', linestyle, redraw=False)

    if(redraw):
      self.updateLegend(redraw=redraw)

  def doVariationColor(self):
    item = self.style['varyItem']
    steps = self.style['varyCycle']
    offset = self.style['varyCycleOffset']
    numbers = np.linspace(0, 1, steps)
    redraw = True

    # select map
    cmap = self.style['varyCmap']
    try:
      cmap = matplotlib.cm.__dict__[cmap]
    except:
      self.parent.parent.statusbar.showMessage('Warning, no color map named ' + cmap + ' found, switching to jet.', self.parent.STATUS_TIME, color='blue')
      cmap = matplotlib.cm.jet
    colors = [cmap(i) for i in numbers]
    
    if(self.kind == 'data'):
      if(self.varyVisibleLabel.isChecked()):
        useData = [i for i in self.parent.parent.data if i.visibility]
      else:
        useData = self.parent.parent.data
      if(not len(useData)):
        redraw = False
      for index, data in enumerate(useData):
        color = colors[(index + offset) % steps]
        if(item == 'line'):
          data.setStyle('color', color, redraw=False)
        elif(item == 'marker'):
          data.setStyle('markerfacecolor', color, redraw=False)
        elif(item == 'mark-line'):
          data.setStyle('markeredgecolor', color, redraw=False)
        elif(item == 'bar'):
          data.setBarStyle('facecolor', color, redraw=False)
        elif(item == 'stack'):
          data.setStackStyle('facecolor', color, redraw=False)
    elif(self.kind == 'resid'):
      if(self.varyVisibleLabel.isChecked()):
        useData = [i for i in self.parent.parent.data if i.visibilityResid]
      else:
        useData = self.parent.parent.data
      if(not len(useData)):
        redraw = False
      for index, data in enumerate(self.parent.parent.data):
        color = colors[(index + offset) % steps]
        if(item == 'line'):
          data.setResidStyle('color', color, redraw=False)
        elif(item == 'marker'):
          data.setResidStyle('markerfacecolor', color, redraw=False)
        elif(item == 'mark-line'):
          data.setResidStyle('markeredgecolor', color, redraw=False)
        elif(item == 'bar'):
          data.setResidBarStyle('facecolor', color, redraw=False)
        elif(item == 'stack'):
          data.setResidStackStyle('facecolor', color, redraw=False)
    elif(self.kind == 'fit'):
      if(self.varyVisibleLabel.isChecked()):
        useFit = [i for i in self.parent.parent.fit if i.visibility]
      else:
        useFit = self.parent.parent.fit
      if(not len(useFit)):
        redraw = False
      for index, fit in enumerate(useFit):
        color = colors[(index + offset) % steps]
        if(item == 'line'):
          fit.setStyle('color', color, redraw=False)
        elif(item == 'marker'):
          fit.setStyle('markerfacecolor', color, redraw=False)
        elif(item == 'mark-line'):
          fit.setStyle('markeredgecolor', color, redraw=False)

    if(redraw):
      self.updateLegend(redraw=redraw)

  def varyCallback(self, callButton=None, key=''):
    # opens menu to access cmap dialog
    self.menu = CmapMenu(parent=self, key=key)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # bottom align position of QMenu
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)

  def setCmap(self, cmap='', key=''):
    # sets scatter cmap to selected one
    if((cmap in self.colormaps) and (key in self.style) and (cmap != self.style[key])):
      self.style[key] = cmap
      if(key == 'varyCmap'):
        self.varyColorMapBox.setCmap(cmap)

  def updateLegend(self, redraw=True):
    # update plot
    if(self.kind == 'resid'):
      if(redraw):
        self.parent.parent.plotArea.residplotwidget.myRefresh()
    else:
      # does legend need to be updated?
      value = self.parent.parent.plotArea.legendVisible
      if(value or redraw):
        self.parent.parent.plotArea.setLegend(value=value, redraw=redraw)

class ConfigMenu(KuhMenu):
  def __init__(self, parent = None, target = None, residMode = False, residZero = False, displayName=False):
    super(ConfigMenu, self).__init__()
    self.parent = parent
    self.target = target
    self.residMode = residMode
    self.residZero = residZero
    self.displayName = displayName
    self.validFloat = MyValidFloat()
    self.persist = False
    self.persistPos = None
    
    # initialize drag values
    self.startPos = None
      
    # set up GUI
    self.buildRessource()
    
  def togglePersist(self, state):
    # toggles QMenu persistence for click events that would close it, see below
    # when state True, save current menu coordinates
    # when state False, restore QMenu at previous coordinates
    self.persist = state
    if(state):
      self.persistPos = self.pos()
    elif(self.persistPos != None):
      self.popup(self.persistPos)
      self.persistPos = None

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(self.underMouse()):
      if(self.displayName and (event.button() == QtCore.Qt.MouseButton.LeftButton)):
        self.startPos = event.globalPosition().toPoint()
    else:
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def mouseMoveEvent(self, event):
    if((event.buttons() == QtCore.Qt.MouseButton.LeftButton) and (self.startPos != None)):
      currentPos = event.globalPosition().toPoint()
      menuPos = self.mapToGlobal(self.pos()) + (currentPos - self.startPos)
      menuPos = self.mapFromGlobal(menuPos)
      self.move(menuPos)
      self.startPos = currentPos
    
  def mouseReleaseEvent(self, event):
    # turn off tracking
    if(event.button() == QtCore.Qt.MouseButton.LeftButton):
      self.startPos = None

  def buildRessource(self):
    # build gui
    self.vLayout = QtWidgets.QVBoxLayout(self)
    self.vLayout.setContentsMargins(*[4]*4)
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    # valid hatch styles
    self.hatchstyles = ['None', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*'] # unfortunately none is not interpreted correctly
    self.hatchstyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
    self.hatchMultiplystyles = [str(i) for i in range(1, 11)]
    
    # display name of item?
    if(self.displayName):
      self.topContainer = QWidgetMac(self)
      self.topContainer.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Fixed)
      self.vLayout.addWidget(self.topContainer)
      self.LayoutTopContainer = QtWidgets.QHBoxLayout(self.topContainer)
      self.LayoutTopContainer.setContentsMargins(0, 0, 0, 0)
      self.LayoutTopContainer.setSpacing(int(-2 + 8 * SCALEFONT))

      if(self.target in self.parent.parent.data):
        index = self.parent.parent.data.index(self.target)
        if(self.residMode):
          leader = 'Resid ' + str(index + 1)
        else:
          leader = 'Data ' + str(index + 1)
      elif(self.target in self.parent.parent.fit):
        index = self.parent.parent.fit.index(self.target)
        leader = 'Curve ' + str(index + 1)
      elif(self.residZero):
        index = 0
        leader = 'Zero Line'
      else:
        leader = 'unknown'
      self.targetLabel = QtWidgets.QLabel()
      self.targetLabel.setText(leader)
      font = self.targetLabel.font()
      font.setBold(True)
      self.targetLabel.setFont(font)
      self.LayoutTopContainer.addWidget(self.targetLabel)
      # create entry field to edit name
      if(self.residMode and (hasattr(self.target, 'nameResid'))):
        name = self.target.nameResid
        nameTag = 'residuals object ' + str(index + 1)
      elif(hasattr(self.target, 'name')):
        name = self.target.name
        if(self.target in self.parent.parent.data):
          nameTag = 'data object ' + str(index + 1)
        else:
          nameTag = 'curve object ' + str(index + 1)
      else:
        name = None
        nameTag = ''
      
      if(name != None):
        self.entryField = QLineEditClick(name)
        self.entryField.setToolTip('Name of ' + nameTag)
        self.entryField.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        ###self.entryField.setMaximumSize(QtCore.QSize(int(150 * SCALEFONT), int(BASE_SIZE)))
        self.entryField.setMinimumSize(QtCore.QSize(int(150 * SCALEFONT), int(BASE_SIZE)))
        self.entryField.editingFinished.connect(partial(self.setField, leader, index))
        self.LayoutTopContainer.addWidget(self.entryField)
     
      # put controls for altering active item
      ###self.LayoutTopContainer.addStretch()
      if(leader.startswith('Data') or leader.startswith('Curve')):
        self.activeButton = QPushButtonCheckable()
        self.activeButton.setText('active')
        self.activeButton.setToolTip('Make ' + nameTag + ' active')
        self.activeButton.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.activeButton.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        # check whether item is already active
        if((leader.startswith('Data') and (index == self.parent.parent.activeData)) or \
           (leader.startswith('Curve') and (index == self.parent.parent.activeFit))):
          self.activeButton.setChecked(True)
          self.activeButton.setEnabled(False)
        self.activeButton.clicked.connect(partial(self.makeActive, leader, index))
        self.LayoutTopContainer.addWidget(self.activeButton)
      
      # put controls for altering z position
      self.zLabel = QtWidgets.QLabel('z')
      self.LayoutTopContainer.addWidget(self.zLabel)

      self.innerContainer = QWidgetMac(self)
      self.LayoutTopContainer.addWidget(self.innerContainer)
      self.LayoutInnerContainer = QtWidgets.QHBoxLayout(self.innerContainer)
      self.LayoutInnerContainer.setContentsMargins(0, 0, 0, 0)
      self.LayoutInnerContainer.setSpacing(0)

      self.zDownButton = QPushButtonMac()
      self.zDownButton.setText('\u25be')
      self.zDownButton.setToolTip('Lower z order')
      self.zDownButton.setAutoRepeat(True)
      self.zDownButton.setMaximumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zDownButton.setMinimumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zDownButton.clicked.connect(partial(self.alterZ, leader, index, -1))
      self.LayoutInnerContainer.addWidget(self.zDownButton)

      self.zUpButton = QPushButtonMac()
      self.zUpButton.setText('\u25b4')
      self.zUpButton.setToolTip('Raise z order')
      self.zUpButton.setAutoRepeat(True)
      self.zUpButton.setMaximumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zUpButton.setMinimumSize(QtCore.QSize(int(15 * SCALEFONT), int(BASE_SIZE)))
      self.zUpButton.clicked.connect(partial(self.alterZ, leader, index, 1))
      self.LayoutInnerContainer.addWidget(self.zUpButton)

      # call alterZ to initiate button disabling
      self.alterZ(leader, index, 0)
     
      blah = HLine()
      self.vLayout.addWidget(blah)

    self.upperRow = QWidgetMac()
    
    # populate menu with items
    # set up line style configurator
    if(self.target in self.parent.parent.data + self.parent.parent.fit):
      # generate top row
      self.displayLineButton = QPushButtonCheckable()
      self.displayLineButton.setText('Configure Line and Marker \u25be')
      self.displayLineButton.setChecked(self.parent.displayLineConfig)
      self.vLayout.addWidget(self.displayLineButton)
      self.vLayout.addWidget(self.upperRow)

      self.displayLineButton.clicked.connect(partial(self.toggleDisplay, self.displayLineButton, self.upperRow, 'displayLineConfig', start=-1))
      self.toggleDisplay(button=self.displayLineButton, target=self.upperRow, flag='displayLineConfig', start=-1)
    else:
      self.vLayout.addWidget(self.upperRow)
      
    self.hLayout = QtWidgets.QHBoxLayout(self.upperRow)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.lineStyleMenu = lineStyleMenu(self, self.target, self.residMode, self.residZero)
    self.hLayout.addWidget(self.lineStyleMenu)
    
    if(not self.residZero):
      # set up marker style configurator
      self.hLayout.addWidget(VLine())
      self.markerStyleMenu = markerStyleMenu(self, self.target, self.residMode)
      self.hLayout.addWidget(self.markerStyleMenu)
      self.hLayout.addStretch()

    if(self.target in self.parent.parent.data):
      # generate scatter style menu
      self.displayScatterButton = QPushButtonCheckable()
      self.displayScatterButton.setText('Configure Scatter \u25be')
      self.displayScatterButton.setChecked(self.parent.displayScatterConfig)
      self.vLayout.addWidget(self.displayScatterButton)

      self.scatterRow = QWidgetMac()
      self.vLayout.addWidget(self.scatterRow)
      self.hLayout9 = QtWidgets.QHBoxLayout(self.scatterRow)
      self.hLayout9.setContentsMargins(0, 0, 0, 0)
      self.displayScatterButton.clicked.connect(partial(self.toggleDisplay, self.displayScatterButton, self.scatterRow, 'displayScatterConfig', start=-1))
      self.toggleDisplay(button=self.displayScatterButton, target=self.scatterRow, flag='displayScatterConfig', start=-1)
      
      self.scatterStyleMenu = scatterStyleMenu(self, self.target, self.residMode)
      self.hLayout9.addWidget(self.scatterStyleMenu)

      # generate lower row
      self.displayBarButton = QPushButtonCheckable()
      self.displayBarButton.setText('Configure Bar and Stack \u25be')
      self.displayBarButton.setChecked(self.parent.displayBarConfig)
      self.vLayout.addWidget(self.displayBarButton)

      self.lowerRow = QWidgetMac()
      self.vLayout.addWidget(self.lowerRow)
      self.hLayout2 = QtWidgets.QHBoxLayout(self.lowerRow)
      self.hLayout2.setContentsMargins(0, 0, 0, 0)
      self.displayBarButton.clicked.connect(partial(self.toggleDisplay, self.displayBarButton, self.lowerRow, 'displayBarConfig', start=-1))
      self.toggleDisplay(button=self.displayBarButton, target=self.lowerRow, flag='displayBarConfig', start=-1)
      
      # set up bar style configurator
      self.barStyleMenu = barStyleMenu(self, self.target, self.residMode)
      self.hLayout2.addWidget(self.barStyleMenu)
  
      # set up stack style configurator
      self.hLayout2.addWidget(VLine())
      self.stackStyleMenu = stackStyleMenu(self, self.target, self.residMode)
      self.hLayout2.addWidget(self.stackStyleMenu)
      self.hLayout2.addStretch()
    
      if(not self.residMode):
        # set up errorbar configurator
        self.displayErrorButton = QPushButtonCheckable()
        self.displayErrorButton.setText('Configure Error Bar \u25be')
        self.displayErrorButton.setChecked(self.parent.displayErrorConfig)
        self.vLayout.addWidget(self.displayErrorButton)

        self.lowestRow = QWidgetMac()
        self.vLayout.addWidget(self.lowestRow)
        self.hLayout3 = QtWidgets.QHBoxLayout(self.lowestRow)
        self.hLayout3.setContentsMargins(0, 0, 0, 0)
        self.displayErrorButton.clicked.connect(partial(self.toggleDisplay, self.displayErrorButton, self.lowestRow, 'displayErrorConfig', start=-1))
        self.toggleDisplay(button=self.displayErrorButton, target=self.lowestRow, flag='displayErrorConfig', start=-1)

        self.errorStyleMenu = errorStyleMenu(self, self.target, self.residMode)
        self.hLayout3.addWidget(self.errorStyleMenu)

      # set up violin and box plot configurator
      if(not self.residMode):
        self.displayViolinButton = QPushButtonCheckable()
        self.displayViolinButton.setText('Configure Violin and Box Plots \u25be')
        self.displayViolinButton.setChecked(self.parent.displayViolinConfig)
        self.vLayout.addWidget(self.displayViolinButton)

        self.violinRow = QWidgetMac()
        self.vLayout.addWidget(self.violinRow)
        self.hLayout5 = QtWidgets.QHBoxLayout(self.violinRow)
        self.hLayout5.setContentsMargins(0, 0, 0, 0)
        self.displayViolinButton.clicked.connect(partial(self.toggleDisplay, self.displayViolinButton, self.violinRow, 'displayViolinConfig', start=-1))
        self.toggleDisplay(button=self.displayViolinButton, target=self.violinRow, flag='displayViolinConfig', start=-1)
        
        self.violinBoxMenu = violinBoxMenu(self, self.target)
        self.hLayout5.addWidget(self.violinBoxMenu)

      # generate lowest row
      self.displayTextButton = QPushButtonCheckable()
      self.displayTextButton.setText('Configure Text Labels \u25be')
      self.displayTextButton.setChecked(self.parent.displayTextConfig)
      self.vLayout.addWidget(self.displayTextButton)

      self.lowestRow2 = QWidgetMac()
      self.vLayout.addWidget(self.lowestRow2)
      self.hLayout4 = QtWidgets.QHBoxLayout(self.lowestRow2)
      self.hLayout4.setContentsMargins(0, 0, 0, 0)
      self.displayTextButton.clicked.connect(partial(self.toggleDisplay, self.displayTextButton, self.lowestRow2, 'displayTextConfig', start=-1))
      self.toggleDisplay(button=self.displayTextButton, target=self.lowestRow2, flag='displayTextConfig', start=-1)
      
      # set up text style configurator
      self.textStyleMenu = textStyleMenu(self, self.target, self.residMode)
      self.hLayout4.addWidget(self.textStyleMenu)

      # put declustering control
      if(not self.residMode):
        index = self.parent.parent.data.index(self.target)
        self.displayDeclusterButton = QPushButtonCheckable()
        self.displayDeclusterButton.setText('Data Operations \u25be')
        self.displayDeclusterButton.setChecked(self.parent.displayDeclusterConfig)
        self.vLayout.addWidget(self.displayDeclusterButton)

        self.declusterRow = QWidgetMac()
        self.vLayout2 = QtWidgets.QVBoxLayout(self.declusterRow)
        self.vLayout2.setContentsMargins(0, 0, 0, 0)
        self.vLayout.addWidget(self.declusterRow)
        self.displayDeclusterButton.clicked.connect(partial(self.toggleDisplay, self.displayDeclusterButton, self.declusterRow, 'displayDeclusterConfig', start=-1))
        self.toggleDisplay(button=self.displayDeclusterButton, target=self.declusterRow, flag='displayDeclusterConfig', start=-1)

        self.declusterInnerRow4 = QWidgetMac()
        self.vLayout2.addWidget(self.declusterInnerRow4)
        self.hLayoutI = QtWidgets.QHBoxLayout(self.declusterInnerRow4)
        self.hLayoutI.setContentsMargins(0, 0, 0, 0)
        self.interpolateButton = QPushButtonMac()
        self.interpolateButton.setText('Interpolate')
        self.interpolateButton.setToolTip('Interpolate data by smoothed curve')
        self.interpolateButton.setMaximumWidth(int(BASE_SIZE))
        self.interpolateButton.setMinimumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.interpolateButton.clicked.connect(partial(self.interpolateData, index))
        self.hLayoutI.addWidget(self.interpolateButton)

        self.interpolateSelector = QComboBoxMac()
        self.interpolateSelector.setToolTip('Mode for interpolating data')
        self.hLayoutI.addWidget(self.interpolateSelector)
        self.interpolateSelector.setMaximumHeight(int(BASE_SIZE))
        self.interpolateSelector.setMinimumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
        items = ['linear', 'quadratic', 'cubic', 'univariate']
        for item in items:
          self.interpolateSelector.addItem(item)
        if(self.parent.interpolateMode in items):
          self.interpolateSelector.setCurrentIndex(items.index(self.parent.interpolateMode))
        else:
          self.interpolateSelector.setCurrentIndex(2)
        self.interpolateSelector.activated.connect(self.changeSmoothMode)

        self.interpolateSmoothLabel = QPushButtonCheckable()
        self.interpolateSmoothLabel.setText('smooth')
        self.interpolateSmoothLabel.setMaximumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
        self.interpolateSmoothLabel.setMinimumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
        self.interpolateSmoothLabel.setChecked(self.parent.interpolateCheck)
        self.interpolateSmoothLabel.setToolTip('Apply smoothing factor for univariate spline?')
        self.interpolateSmoothLabel.clicked.connect(self.changeSmoothCheck)
        self.hLayoutI.addWidget(self.interpolateSmoothLabel)
        self.interpolateSmoothEntry = QLineEditClick()
        self.interpolateSmoothEntry.setToolTip('Smoothing factor for univariate spline')
        self.interpolateSmoothEntry.setText(str(self.parent.interpolateSmooth))
        self.interpolateSmoothEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
        self.interpolateSmoothEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
        self.interpolateSmoothEntry.editingFinished.connect(partial(self.changeSmooth, 0, 10))
        self.interpolateSmoothEntry.setValidator(self.validFloat)
        self.hLayoutI.addWidget(self.interpolateSmoothEntry)
        self.hLayoutI.addStretch()
        
        # update controls based on current interpolation mode
        if(self.interpolateSelector.currentText() != 'univariate'):
          self.interpolateSmoothLabel.setEnabled(False)
          self.interpolateSmoothEntry.setEnabled(False)

        blah = HLine()
        self.vLayout2.addWidget(blah)
        
        self.declusterInnerRow3 = QWidgetMac()
        self.vLayout2.addWidget(self.declusterInnerRow3)
        self.hLayout7 = QtWidgets.QHBoxLayout(self.declusterInnerRow3)
        self.hLayout7.setContentsMargins(0, 0, 0, 0)
        self.avgButton = QPushButtonMac()
        self.avgButton.setText('Average')
        self.avgButton.setToolTip('Smooth data by averaging')
        self.avgButton.setMaximumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.avgButton.setMinimumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.avgButton.clicked.connect(partial(self.averageData, index))
        self.hLayout7.addWidget(self.avgButton)
        
        self.avgMoveLabel = QPushButtonCheckable()
        self.avgMoveLabel.setText('moving')
        self.avgMoveLabel.setMaximumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
        self.avgMoveLabel.setMinimumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
        self.avgMoveLabel.setChecked(self.parent.avgMovingCheck)
        self.avgMoveLabel.setToolTip('Moving average?')
        self.avgMoveLabel.clicked.connect(self.changeAvgMovingCheck)
        self.hLayout7.addWidget(self.avgMoveLabel)

        self.avgNumberEntry = QSpinClick()
        self.avgNumberEntry.setToolTip('Number of points to average')
        self.avgNumberEntry.setMinimum(2)
        self.avgNumberEntry.setMaximum(500)
        self.avgNumberEntry.setValue(self.parent.avgNumberPoints)
        self.avgNumberEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.avgNumberEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.avgNumberEntry.editingFinished.connect(partial(self.alterAvgNumber, self.avgNumberEntry, True))
        self.hLayout7.addWidget(self.avgNumberEntry)
        self.hLayout7.addStretch()

        blah = HLine()
        self.vLayout2.addWidget(blah)

        self.declusterInnerRow3 = QWidgetMac()
        self.vLayout2.addWidget(self.declusterInnerRow3)
        self.hLayoutT = QtWidgets.QHBoxLayout(self.declusterInnerRow3)
        self.hLayoutT.setContentsMargins(0, 0, 0, 0)
        self.transformerButton = QPushButtonMac()
        self.transformerButton.setText('Transform')
        self.transformerButton.setToolTip('Transform data points and errors')
        self.transformerButton.setMaximumWidth(int(BASE_SIZE))
        self.transformerButton.setMinimumSize(QtCore.QSize(int(72 * SCALEFONT), int(2 * BASE_SIZE)))
        self.transformerButton.clicked.connect(partial(self.transformerData, index))
        self.hLayoutT.addWidget(self.transformerButton)
        
        self.declusterInnerColT = QWidgetMac()
        self.hLayoutT.addWidget(self.declusterInnerColT)
        self.vLayoutT = QtWidgets.QVBoxLayout(self.declusterInnerColT)
        self.vLayoutT.setContentsMargins(0, 0, 0, 0)
        self.vLayoutT.setSpacing(1)
        
        self.dataTransformX = QWidgetMac()
        self.dataTransformX.setContentsMargins(0, 0, 0, 0)
        self.vLayoutT.addWidget(self.dataTransformX)
        self.dataTransformXLayout = QtWidgets.QHBoxLayout(self.dataTransformX)
        self.dataTransformXLayout.setContentsMargins(0, 0, 0, 0)
        
        self.dataTransformXLabel = QtWidgets.QLabel('x = ')
        self.dataTransformXLabel.setMaximumSize(int(20 * SCALEFONT), int(BASE_SIZE))
        self.dataTransformXLabel.setMinimumSize(int(20 * SCALEFONT), int(BASE_SIZE))
        self.dataTransformXLayout.addWidget(self.dataTransformXLabel)
    
        self.dataTransformXEntry = QLineEditClick()
        self.dataTransformXEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.dataTransformXEntry.setMaximumHeight(int(BASE_SIZE))
        self.dataTransformXEntry.setMinimumSize(int(100 * SCALEFONT), int(BASE_SIZE))
        self.dataTransformXEntry.setText(self.parent.formulaX)
        self.dataTransformXEntry.setPlaceholderText('x')
        self.dataTransformXEntry.setToolTip('Formula for transforming x values')
        self.dataTransformXEntry.editingFinished.connect(partial(self.changeFormula, 'x'))
        self.dataTransformXLayout.addWidget(self.dataTransformXEntry)

        self.dataTransformY = QWidgetMac()
        self.dataTransformY.setContentsMargins(0, 0, 0, 0)
        self.vLayoutT.addWidget(self.dataTransformY)
        self.dataTransformYLayout = QtWidgets.QHBoxLayout(self.dataTransformY)
        self.dataTransformYLayout.setContentsMargins(0, 0, 0, 0)
        
        self.dataTransformYLabel = QtWidgets.QLabel('y = ')
        self.dataTransformYLabel.setMaximumSize(int(20 * SCALEFONT), int(BASE_SIZE))
        self.dataTransformYLabel.setMinimumSize(int(20 * SCALEFONT), int(BASE_SIZE))
        self.dataTransformYLayout.addWidget(self.dataTransformYLabel)
    
        self.dataTransformYEntry = QLineEditClick()
        self.dataTransformYEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.dataTransformYEntry.setMaximumHeight(int(BASE_SIZE))
        self.dataTransformYEntry.setMinimumSize(int(100 * SCALEFONT), int(BASE_SIZE))
        self.dataTransformYEntry.setText(self.parent.formulaY)
        self.dataTransformYEntry.setPlaceholderText('y')
        self.dataTransformYEntry.setToolTip('Formula for transforming y values')
        self.dataTransformYEntry.editingFinished.connect(partial(self.changeFormula, 'y'))
        self.dataTransformYLayout.addWidget(self.dataTransformYEntry)

        blah = HLine()
        self.vLayout2.addWidget(blah)

        self.declusterInnerRow = QWidgetMac()
        self.vLayout2.addWidget(self.declusterInnerRow)
        self.hLayout5 = QtWidgets.QHBoxLayout(self.declusterInnerRow)
        self.hLayout5.setContentsMargins(0, 0, 0, 0)
        self.declusterButton = QPushButtonMac()
        self.declusterButton.setText('Decluster')
        self.declusterButton.setToolTip('Move apart data points (swarm plot)')
        self.declusterButton.setMaximumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.declusterButton.setMinimumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.declusterButton.clicked.connect(partial(self.declusterData, index))
        self.hLayout5.addWidget(self.declusterButton)
        
        self.declusterTolXLabel = QtWidgets.QLabel('Tol. ' + u'\N{GREEK CAPITAL LETTER DELTA}' + 'x')
        self.declusterTolXLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.declusterTolXLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout5.addWidget(self.declusterTolXLabel)
        self.declusterTolXEntry = QLineEditClick()
        self.declusterTolXEntry.setToolTip('Tolerance in x for data declustering')
        self.declusterTolXEntry.setText(str(self.parent.declusterTolX))
        self.declusterTolXEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
        self.declusterTolXEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
        self.declusterTolXEntry.editingFinished.connect(partial(self.changeTol, self.declusterTolXEntry, 1e-9, 1e9))
        self.declusterTolXEntry.setValidator(self.validFloat)
        self.hLayout5.addWidget(self.declusterTolXEntry)
  
        self.declusterTolYLabel = QtWidgets.QLabel(u'\N{GREEK CAPITAL LETTER DELTA}' + 'y')
        self.declusterTolYLabel.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
        self.declusterTolYLabel.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout5.addWidget(self.declusterTolYLabel)
        self.declusterTolYEntry = QLineEditClick()
        self.declusterTolYEntry.setToolTip('Tolerance in y for data declustering')
        self.declusterTolYEntry.setText(str(self.parent.declusterTolY))
        self.declusterTolYEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
        self.declusterTolYEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
        self.declusterTolYEntry.editingFinished.connect(partial(self.changeTol, self.declusterTolYEntry, 1e-9, 1e9))
        self.declusterTolYEntry.setValidator(self.validFloat)
        self.hLayout5.addWidget(self.declusterTolYEntry)

        self.declusterMaxXLabel = QtWidgets.QLabel('n')
        self.declusterMaxXLabel.setMaximumSize(QtCore.QSize(int(10 * SCALEFONT), int(BASE_SIZE)))
        self.declusterMaxXLabel.setMinimumSize(QtCore.QSize(int(10 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout5.addWidget(self.declusterMaxXLabel)
        self.declusterMaxXEntry = QSpinClick()
        self.declusterMaxXEntry.setToolTip('Limit lateral displacement to n steps\n(0 means no limitation)')
        self.declusterMaxXEntry.setMinimum(0)
        self.declusterMaxXEntry.setMaximum(50)
        self.declusterMaxXEntry.setValue(self.parent.declusterMaxX)
        self.declusterMaxXEntry.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.declusterMaxXEntry.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.declusterMaxXEntry.editingFinished.connect(partial(self.alterToln, self.declusterMaxXEntry, True))
        self.hLayout5.addWidget(self.declusterMaxXEntry)
        self.hLayout5.addStretch()

        self.guessDeclusterTol(index)

        blah = HLine()
        self.vLayout2.addWidget(blah)
        
        self.declusterInnerRow2 = QWidgetMac()
        self.vLayout2.addWidget(self.declusterInnerRow2)
        self.hLayout6 = QtWidgets.QHBoxLayout(self.declusterInnerRow2)
        self.hLayout6.setContentsMargins(0, 0, 0, 0)
        self.mergeButton = QPushButtonMac()
        self.mergeButton.setText('Merge with')
        self.mergeButton.setToolTip('Combine current and selected data sets into new merged one')
        self.mergeButton.setMaximumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.mergeButton.setMinimumSize(QtCore.QSize(int(72 * SCALEFONT), int(BASE_SIZE)))
        self.mergeButton.clicked.connect(partial(self.mergeData, index))
        self.hLayout6.addWidget(self.mergeButton)
        
        self.dataSetSelector = QComboBoxMac()
        self.dataSetSelector.setToolTip('Data set to be merged')
        self.hLayout6.addWidget(self.dataSetSelector)
        self.dataSetSelector.setMaximumHeight(int(BASE_SIZE))
        self.dataSetSelector.setMinimumSize(QtCore.QSize(int(100 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout6.addStretch()
        
        self.updateDataSetSelector(targetIndex=index)

    # set plot interval configuration and bootstrap config as needed
    if(self.target in self.parent.parent.fit):
      # generate row
      if((self.target.bootType and len(self.target.bootFpar)) or ((not self.target.bootType) and ('xdata' in self.target.lastFitResults))):
        self.bootStyle = self.target.bootStyle

        self.displayBootButton = QPushButtonCheckable()
        self.displayBootButton.setText('Configure Confidence Band \u25be')
        self.displayBootButton.setChecked(self.parent.displayBootConfig)
        self.vLayout.addWidget(self.displayBootButton)
  
        self.bootRow = QWidgetMac()
        self.vLayout.addWidget(self.bootRow)
        self.vLayout8 = QtWidgets.QVBoxLayout(self.bootRow)
        self.vLayout8.setContentsMargins(0, 0, 0, 0)
  
        self.displayBootButton.clicked.connect(partial(self.toggleDisplay, self.displayBootButton, self.bootRow, 'displayBootConfig', start=-1))
        self.toggleDisplay(button=self.displayBootButton, target=self.bootRow, flag='displayBootConfig', start=-1)

        self.displayBootGroup = QWidgetMac()
        self.vLayout8.addWidget(self.displayBootGroup)
        self.hLayout8 = QtWidgets.QHBoxLayout(self.displayBootGroup)
        self.hLayout8.setContentsMargins(0, 0, 0, 0)
        self.hLayout8.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout8.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.bootShowLabel = QtWidgets.QLabel()
        self.bootShowLabel.setText('Conf. band')
        font = self.bootShowLabel.font()
        font.setBold(True)
        self.bootShowLabel.setFont(font)
        self.bootShowLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.bootShowLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout8.addWidget(self.bootShowLabel)
    
        self.displayBootCheck = QPushButtonCheckable()
        self.displayBootCheck.setText('Show?')
        self.displayBootCheck.setToolTip('Show bootstrap confidence band of curve')
        self.displayBootCheck.setChecked(self.bootStyle['visible'])
        self.displayBootCheck.clicked.connect(self.setBootDisplay)
        self.displayBootCheck.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.displayBootCheck.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout8.addWidget(self.displayBootCheck)

        # line width
        self.lineWidthBootGroup = QWidgetMac()
        self.vLayout8.addWidget(self.lineWidthBootGroup)
        self.hLayout81 = QtWidgets.QHBoxLayout(self.lineWidthBootGroup)
        self.hLayout81.setContentsMargins(0, 0, 0, 0)
        self.hLayout81.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout81.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.lineWidthBootLabel = QtWidgets.QLabel('Linewidth')
        self.lineWidthBootLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.lineWidthBootLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout81.addWidget(self.lineWidthBootLabel)
        # shady line width spin box
        self.lineWidthBootSpin = QDoubleSpinClick()
        self.hLayout81.addWidget(self.lineWidthBootSpin)
        self.lineWidthBootSpin.setMinimum(0.0)
        self.lineWidthBootSpin.setMaximum(100.0)
        self.lineWidthBootSpin.setValue(self.bootStyle['linewidth'])
        self.lineWidthBootSpin.setToolTip('Confidence band line width of curve')
        self.lineWidthBootSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.lineWidthBootSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.lineWidthBootSpin.editingFinished.connect(partial(self.alterBootStyle, 'linewidth', self.lineWidthBootSpin))
    
        # line color
        self.lineColorBootGroup = QWidgetMac()
        self.vLayout8.addWidget(self.lineColorBootGroup)
        self.hLayout82 = QtWidgets.QHBoxLayout(self.lineColorBootGroup)
        self.hLayout82.setContentsMargins(0, 0, 0, 0)
        self.hLayout82.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout82.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.lineColorBootLabel = QtWidgets.QLabel('Linecolor')
        self.lineColorBootLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.lineColorBootLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout82.addWidget(self.lineColorBootLabel)
        
        self.lineColorBootButton = QPushButtonColor()
        self.lineColorBootButton.setToolTip('Confidence band shading line color of curve')
        self.lineColorBootButton.setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.bootStyle['edgecolor'][0:3]]
        self.lineColorBootButton.setMyColor(*colorvalue)
        self.lineColorBootButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.lineColorBootButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.lineColorBootButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.lineColorBootButton.clicked.connect(partial(self.setBootColor, key='edgecolor'))
        self.hLayout82.addWidget(self.lineColorBootButton)

        # line style
        self.bootlinestyles = [i for i in self.linestyles if (i != 'None')]
        self.lineStyleBootGroup = QWidgetMac()
        self.vLayout8.addWidget(self.lineStyleBootGroup)
        self.hLayout83 = QtWidgets.QHBoxLayout(self.lineStyleBootGroup)
        self.hLayout83.setContentsMargins(0, 0, 0, 0)
        self.hLayout83.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout83.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.lineStyleBootLabel = QtWidgets.QLabel('Linestyle')
        self.lineStyleBootLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.lineStyleBootLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout83.addWidget(self.lineStyleBootLabel)
        self.comboBootStyle = QComboBoxMac()
        self.comboBootStyle.setToolTip('Confidence band shading line style of curve')
        for entry in self.bootlinestyles:
          self.comboBootStyle.addItem(entry)
        if(self.bootStyle['linestyle'] in self.bootlinestyles):
          currindex = self.bootlinestyles.index(self.bootStyle['linestyle'])
        else:
          currindex = 0
        self.comboBootStyle.setCurrentIndex(currindex)
        self.comboBootStyle.activated.connect(partial(self.selectBootStyle, 'linestyle', self.comboBootStyle))
        self.comboBootStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.comboBootStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout83.addWidget(self.comboBootStyle)

        # fill color
        self.fillColorBootGroup = QWidgetMac()
        self.vLayout8.addWidget(self.fillColorBootGroup)
        self.hLayout84 = QtWidgets.QHBoxLayout(self.fillColorBootGroup)
        self.hLayout84.setContentsMargins(0, 0, 0, 0)
        self.hLayout84.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout84.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.fillColorBootLabel = QPushButtonCheckable()
        self.fillColorBootLabel.setText('Fillcolor')
        self.fillColorBootLabel.setToolTip('Show fill of confidence band shading of curve')
        self.fillColorBootLabel.setChecked(self.bootStyle['doFill'])
        self.fillColorBootLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.fillColorBootLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.fillColorBootLabel.clicked.connect(self.toggleBootFill)
        self.hLayout84.addWidget(self.fillColorBootLabel)
          
        self.fillColorBootButton = QPushButtonColor()
        self.fillColorBootButton.setToolTip('Confidence band shading fill color of curve')
        self.fillColorBootButton.setAutoFillBackground(False)
        colorvalue = [int(i * 255.0) for i in self.bootStyle['facecolor'][0:3]]
        self.fillColorBootButton.setMyColor(*colorvalue)
        self.fillColorBootButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.fillColorBootButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.fillColorBootButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.fillColorBootButton.clicked.connect(partial(self.setBootColor, key='facecolor'))
        self.hLayout84.addWidget(self.fillColorBootButton)

        # hatch style
        self.hatchStyleBootGroup = QWidgetMac()
        self.vLayout8.addWidget(self.hatchStyleBootGroup)
        self.hLayout85 = QtWidgets.QHBoxLayout(self.hatchStyleBootGroup)
        self.hLayout85.setContentsMargins(0, 0, 0, 0)
        self.hLayout85.setSpacing(int(-2 + 8 * SCALEFONT))
        self.hLayout85.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        self.hatchStyleBootLabel = QtWidgets.QLabel('Hatch')
        self.hatchStyleBootLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hatchStyleBootLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout85.addWidget(self.hatchStyleBootLabel)
        self.comboHatchStyleBoot = QComboBoxMac()
        self.comboHatchStyleBoot.setToolTip('Confidence band hatch style of curve')
        for entry in self.hatchstyles:
          self.comboHatchStyleBoot.addItem(entry)
        if(self.bootStyle['hatch'] in self.hatchstyles):
          currindex = self.hatchstyles.index(self.bootStyle['hatch'])
        else:
          currindex = 0
        self.comboHatchStyleBoot.setCurrentIndex(currindex)
        self.comboHatchStyleBoot.activated.connect(partial(self.selectBootStyle, 'hatch', self.comboHatchStyleBoot))
        self.comboHatchStyleBoot.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.comboHatchStyleBoot.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout85.addWidget(self.comboHatchStyleBoot)
        self.comboHatchMultiplyStyleBoot = QComboBoxMac()
        self.comboHatchMultiplyStyleBoot.setToolTip('Confidence band hatch repeat of curve')
        for entry in self.hatchMultiplystyles:
          self.comboHatchMultiplyStyleBoot.addItem(str(entry))
        if(str(self.bootStyle['hatchMultiply']) in self.hatchMultiplystyles):
          currindex = self.hatchMultiplystyles.index(str(self.bootStyle['hatchMultiply']))
        else:
          currindex = 0
        self.comboHatchMultiplyStyleBoot.setCurrentIndex(currindex)
        self.comboHatchMultiplyStyleBoot.activated.connect(partial(self.selectBootStyle, 'hatchMultiply', self.comboHatchMultiplyStyleBoot))
        self.comboHatchMultiplyStyleBoot.setMaximumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.comboHatchMultiplyStyleBoot.setMinimumSize(QtCore.QSize(int(31 * SCALEFONT), int(BASE_SIZE)))
        self.hLayout85.addWidget(self.comboHatchMultiplyStyleBoot)

      # generate row
      self.displayIntervalButton = QPushButtonCheckable()
      self.displayIntervalButton.setText('Configure Plot Interval \u25be')
      self.displayIntervalButton.setChecked(self.parent.displayIntervalConfig)
      self.vLayout.addWidget(self.displayIntervalButton)

      self.intervalRow = QWidgetMac()
      self.vLayout.addWidget(self.intervalRow)
      self.hLayout6 = QtWidgets.QHBoxLayout(self.intervalRow)
      self.hLayout6.setContentsMargins(0, 0, 0, 0)

      self.displayIntervalButton.clicked.connect(partial(self.toggleDisplay, self.displayIntervalButton, self.intervalRow, 'displayIntervalConfig', start=-1))
      self.toggleDisplay(button=self.displayIntervalButton, target=self.intervalRow, flag='displayIntervalConfig', start=-1)

      # controls for limiting plot interval
      restrictMeLow, restrictMeHigh, restrictLow, restrictHigh = self.target.getRestricted()
      if(not restrictMeLow):
        if(self.parent.parent.plotArea.splitShow):
          restrictLow = np.min((self.parent.parent.plotArea.minX, self.parent.parent.plotArea.maxX, self.parent.parent.plotArea.minX_div, self.parent.parent.plotArea.maxX_div))
        else:
          restrictLow = np.min((self.parent.parent.plotArea.minX, self.parent.parent.plotArea.maxX))
      if(not restrictMeHigh):
        if(self.parent.parent.plotArea.splitShow):
          restrictHigh = np.max((self.parent.parent.plotArea.minX, self.parent.parent.plotArea.maxX, self.parent.parent.plotArea.minX_div, self.parent.parent.plotArea.maxX_div))
        else:
          restrictHigh = np.max((self.parent.parent.plotArea.minX, self.parent.parent.plotArea.maxX))
      self.labelDisplayIntervalLow = QPushButtonCheckable()
      self.labelDisplayIntervalLow.setText('Limit min?')
      self.labelDisplayIntervalLow.setToolTip('Restrict lower boundary of interval over which curve is plotted')
      self.labelDisplayIntervalLow.setChecked(restrictMeLow)
      self.labelDisplayIntervalLow.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.labelDisplayIntervalLow.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.labelDisplayIntervalLow.clicked.connect(partial(self.toggleRestricted, False))
      self.hLayout6.addWidget(self.labelDisplayIntervalLow)
      self.entryDisplayIntervalLow = QLineEditClick()
      self.entryDisplayIntervalLow.setToolTip('Lower limit of plot interval')
      self.entryDisplayIntervalLow.setText(str(restrictLow))
      self.entryDisplayIntervalLow.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.entryDisplayIntervalLow.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.entryDisplayIntervalLow.editingFinished.connect(partial(self.changeRestricted, 'restrictLow', self.entryDisplayIntervalLow, None, None))
      self.hLayout6.addWidget(self.entryDisplayIntervalLow)

      self.labelDisplayIntervalHigh = QPushButtonCheckable()
      self.labelDisplayIntervalHigh.setText('Limit max?')
      self.labelDisplayIntervalHigh.setToolTip('Restrict upper boundary of interval over which curve is plotted')
      self.labelDisplayIntervalHigh.setChecked(restrictMeHigh)
      self.labelDisplayIntervalHigh.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.labelDisplayIntervalHigh.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.labelDisplayIntervalHigh.clicked.connect(partial(self.toggleRestricted, True))
      self.hLayout6.addWidget(self.labelDisplayIntervalHigh)
      self.entryDisplayIntervalHigh = QLineEditClick()
      self.entryDisplayIntervalHigh.setToolTip('Upper limit of plot interval')
      self.entryDisplayIntervalHigh.setText(str(restrictHigh))
      self.entryDisplayIntervalHigh.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.entryDisplayIntervalHigh.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.entryDisplayIntervalHigh.editingFinished.connect(partial(self.changeRestricted, 'restrictHigh', self.entryDisplayIntervalHigh, None, None))
      self.hLayout6.addWidget(self.entryDisplayIntervalHigh)
      self.hLayout6.addStretch()
  
      self.updateRestrictedFields(restrictMeLow, high=False)
      self.updateRestrictedFields(restrictMeHigh, high=True)
      
      # generate row
      self.displayAreaButton = QPushButtonCheckable()
      self.displayAreaButton.setText('Add Area Enclosed by Curve \u25be')
      self.displayAreaButton.setChecked(self.parent.displayAreaConfig)
      self.vLayout.addWidget(self.displayAreaButton)

      self.areaRow = QWidgetMac()
      self.vLayout.addWidget(self.areaRow)
      self.hLayout5 = QtWidgets.QHBoxLayout(self.areaRow)
      self.hLayout5.setContentsMargins(0, 0, 0, 0)

      self.displayAreaButton.clicked.connect(partial(self.toggleDisplay, self.displayAreaButton, self.areaRow, 'displayAreaConfig', start=-1))
      self.toggleDisplay(button=self.displayAreaButton, target=self.areaRow, flag='displayAreaConfig', start=-1)

      self.drawAreaButton = QPushButtonMac()
      self.drawAreaButton.setText('Create area')
      self.drawAreaButton.setToolTip('Draw area between this function and selected axis/function')
      self.drawAreaButton.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.drawAreaButton.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.drawAreaButton.clicked.connect(self.drawArea)
      self.hLayout5.addWidget(self.drawAreaButton)

      self.targetComboBox = QComboBoxMac()
      self.targetComboBox.setToolTip('Select target axis/function for area')
      self.targetComboBox.setMinimumSize(QtCore.QSize(int(120 * SCALEFONT), int(BASE_SIZE)))
      self.targetComboBox.setMaximumSize(QtCore.QSize(int(120 * SCALEFONT), int(BASE_SIZE)))
      self.targetComboBox.addItem('0 - x axis')
      self.targetCurves, count = {}, 1
      for index2, entry2 in enumerate(self.parent.parent.fit):
        if(self.target != entry2):
          self.targetComboBox.addItem(str(index2 + 1) + ' - ' + entry2.name)
          self.targetCurves[count] = index2
          count += 1
      self.targetComboBox.setCurrentIndex(0)
      self.hLayout5.addWidget(self.targetComboBox)
      self.hLayout5.addStretch()

    # set notes
    if((not self.residMode) and hasattr(self.target, 'notes')):
      # generate row
      self.displayNotesButton = QPushButtonCheckable()
      self.displayNotesButton.setText('Edit Notes \u25be')
      self.displayNotesButton.setChecked(self.parent.displayNotesConfig)
      self.vLayout.addWidget(self.displayNotesButton)

      self.notesRow = QWidgetMac()
      self.vLayout.addWidget(self.notesRow)
      self.hLayout7 = QtWidgets.QHBoxLayout(self.notesRow)
      self.hLayout7.setContentsMargins(0, 0, 0, 0)

      self.displayNotesButton.clicked.connect(partial(self.toggleDisplay, self.displayNotesButton, self.notesRow, 'displayNotesConfig', start=-1))
      self.toggleDisplay(button=self.displayNotesButton, target=self.notesRow, flag='displayNotesConfig', start=-1)
      
      self.notesEntry = myQTextEditBase()
      self.notesEntry.setLineWrapMode(QtWidgets.QTextEdit.LineWrapMode.NoWrap)
      ###self.notesEntry.setGeometry(QtCore.QRect(0, 0, int(500 * SCALEFONT), 600))
      self.notesEntry.setReadOnly(False)
      self.notesEntry.setText(self.target.notes)
      self.notesEntry.setToolTip('Add notes on this object')
      self.notesEntry.setAcceptRichText(False)
      self.notesEntry.setPlaceholderText('Type description of object to be saved and exported.')
      self.hLayout7.addWidget(self.notesEntry)
      self.notesEntry.textChanged.connect(self.updateNotes)

    self.vLayout.addStretch()
    self.setFocus()    
    self.focusNextChild()

  def interpolateData(self, sourceIndex):
    # interpolates selected data by smoothed curve
    x, y = self.target.x, self.target.y
    minPoints = 2
    if(len(x) < minPoints):
      self.parent.parent.statusbar.showMessage('Data must contain at least ' + str(minPoints) + ' data points to be interpolated.', self.parent.parent.STATUS_TIME)
    else:
      kind = self.interpolateSelector.currentText()
      try:
        if(kind == 'univariate'):
          interpol = interpolate.UnivariateSpline(x, y)
          if(self.interpolateSmoothLabel.isChecked()):
            interpol.set_smoothing_factor(self.parent.interpolateSmooth)
        else:
          interpol = interpolate.interp1d(x, y, kind=kind)
      except:
        self.parent.parent.statusbar.showMessage('Error when interpolating data.', self.parent.parent.STATUS_TIME)
      else:
        # calculate new data
        # need to cast as int as there may be numbers in scientific notation
        numPoints = int(self.parent.advancedInterpolate['points'])
        # optionally check whether axis is log?
        xv = np.linspace(min(x), max(x), numPoints)
        newRoles = ['x', 'y']
        newData = np.vstack((xv, interpol(xv)))
        newData = newData.transpose()
        # generate new data set
        self.parent.parent.data.append(DataObject(self.parent.parent))
        self.parent.parent.data[-1].setName('Interp_' + kind[:3] + '_' + self.parent.parent.data[sourceIndex].name)
        self.parent.parent.data[-1].setNameResid('Interpolated_' + self.parent.parent.data[sourceIndex].nameResid)
        # need to copy contents of original object
        self.parent.parent.data[-1].spawned(self.parent.parent.data[sourceIndex])
        # set certain graphics settings to prevent ungodly mess
        if(self.parent.parent.data[-1].style['linestyle'] == 'None'):
          self.parent.parent.data[-1].style['linestyle'] = 'solid'
        self.parent.parent.data[-1].style['marker'] = 'None'
        self.parent.parent.data[-1].Errorstyle['visible'] = False
        self.parent.parent.data[-1].ErrorShadystyle['visible'] = False
        self.parent.parent.data[-1].Barstyle['showBar'] = False
        self.parent.parent.data[-1].Stackstyle['showStack'] = False
        self.parent.parent.data[-1].Textstyle['showText'] = False
        self.parent.parent.data[-1].Violinstyle['mode'] = 0
        self.parent.parent.data[-1].Scatterstyle['showScatter'] = False
        # a number of calls to update plots
        self.helperDeclusterAndMerge(sourceIndex=sourceIndex, newData=newData, newRoles=newRoles, hideOriginal=False)
        # give voice
        if(numPoints >= len(x)):
          self.parent.parent.statusbar.showMessage('Interpolated data set, while preserving original copy.', self.parent.parent.STATUS_TIME, color='blue')
        else:
          self.parent.parent.statusbar.showMessage('Interpolated data but note that original data set has more points than interpolation.', self.parent.parent.STATUS_TIME, color='blue')

  def changeSmoothMode(self):
    # change value of smooth check for univariate spline
    value = self.interpolateSelector.currentText()
    self.parent.interpolateMode = value
    # de/activate other controls as proper
    if(value == 'univariate'):
      state = True
    else:
      state = False
    self.interpolateSmoothLabel.setEnabled(state)
    self.interpolateSmoothEntry.setEnabled(state)

  def changeSmoothCheck(self):
    # change value of smooth check for univariate spline
    value = self.interpolateSmoothLabel.isChecked()
    self.parent.interpolateCheck = value

  def changeSmooth(self, minval=0, maxval=1):
    # check smooth value for univariate spline
    try:
      value = float(self.interpolateSmoothEntry.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.interpolateSmoothEntry.setText(str(value))
    # update parameters in parent object
    self.parent.interpolateSmooth = value
    # and activate smoothing in univariate spline
    self.interpolateSmoothLabel.setChecked(True)
    
  def changeAvgMovingCheck(self):
    # change value of average moving check
    value = self.avgMoveLabel.isChecked()
    self.parent.avgMovingCheck = value

  def alterAvgNumber(self, entryfield=None, integer=False):
    # check paramter boundaries
    try:
      if(integer):
        value = int(entryfield.value())
      else:
        value = float(entryfield.value())
      originalvalue = value
    except:
      if(integer):
        value = 0
        originalvalue = 1
      else:
        value = 0.0
        originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      entryfield.setValue(value)
    # update parameters in parent object
    self.parent.avgNumberPoints = value

  def drawArea(self):
    # draw area between curve and target curve
    targetIndex = self.targetComboBox.currentIndex()
    if(targetIndex + 1):
      # obtain x and y values
      x, y = self.target.x, self.target.y
      if(not targetIndex):
        # x axis
        y2 = y * 0.0
        item = 'x-axis'
      else:
        # simulate target function at desired x values
        targetCurve = self.parent.parent.fit[self.targetCurves[targetIndex]]
        _, y2 = targetCurve.simulateFunc(x=x)
        item = targetCurve.name
        # should we check for inf values?
        ####procData = procData[np.isfinite(procData[:, index])]

      # generate extras element
      name = 'Intersect_' + self.target.name + '_' + item
      xmin, ymin = np.min(x), np.min((y, y2))
      width, height = np.max(x) - xmin, np.max((y, y2)) - ymin
      xmid, ymid = xmin + width / 2., ymin + height / 2.
      self.parent.parent.extras.append(ExtrasObject(self.parent.parent))
      valueDict = {'extrasType': 'area', 'area__x': x, 'area__y1': y, 'area__y2': y2,
                   'x': xmid, 'y': ymid, 'x2': width, 'y2': height, 'labeltext': name, 'name': name}
      self.parent.parent.extras[-1].setValues(valueDict, redraw=True)
      
      # check for split y axis
      if(self.parent.parent.plotArea.splitY):
        self.parent.parent.extras[-1].onBothAxes = True
        self.parent.parent.extras[-1].duplicateForSplit()
  
      # update extras table
      self.parent.parent.objectsarea.refreshExtrasTable()
      self.parent.parent.objectsarea.refreshCurvesTable()
      self.parent.parent.objectsarea.refreshDataTable()
      self.parent.parent.plotArea.updateToggloContainer()
      
  def changeFormula(self, axis='x'):
    # saves changes to transformation formulas
    if(axis in ['x', 'y']):
      if(axis == 'x'):
        self.parent.formulaX = self.dataTransformXEntry.text()
      else:
        self.parent.formulaY = self.dataTransformYEntry.text()

  def alterBootStyle(self, key=None, entryfield=None):
    if((self.target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
      self.target.setBootStyle(key, value, redraw=True)

  def setBootColor(self, key=None):
    if((self.target != None) and (key != None)):
      # prevent QMenu from vanishing
      self.togglePersist(True)
      # get current color
      if (key in self.bootStyle):
        prevColor = [int(255 * i) for i in self.bootStyle[key]]
        prevColor = QtGui.QColor(*prevColor)
      else:
        prevColor = QtCore.Qt.black
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        # update color in QMenu
        if(key == 'edgecolor'):
          self.lineColorBootButton.setMyColor(*value)
        elif(key == 'facecolor'):
          self.fillColorBootButton.setMyColor(*value)
        value = [i / 255.0 for i in value]
        self.target.setBootStyle(key, value, redraw=True)

      # bring back the QMenu
      self.togglePersist(False)

  def selectBootStyle(self, key=None, entryfield=None):
    if((self.target != None) and (key != None)):
      value = str(entryfield.currentText())
      if(key == 'hatchMultiply'):
        value = int(value)
      self.target.setBootStyle(key, value, redraw=True)

  def setBootDisplay(self):
    # toggle display of confidence bands
    if(self.target != None):
      value = self.displayBootCheck.isChecked()
      self.target.setBootStyle('visible', value, redraw=True)

  def toggleBootFill(self):
    # toggles on/off fill of bootstrapped interval
    if(self.target != None):
      value = self.fillColorBootLabel.isChecked()
      self.target.setBootStyle('doFill', value, redraw=True)

  def updateNotes(self):
    # updates notes in target object
    self.target.notes = self.notesEntry.toPlainText()

  def mergeData(self, targetIndex):
    # merges data of selected data set into current data set
    mergeIndex = self.dataSetSelector.currentText()
    mergeIndex = int(mergeIndex.split('-')[0]) - 1
    mergeData = self.parent.parent.data[mergeIndex].value()
    sourceData = self.parent.parent.data[targetIndex].value()
    sourceRoles, mergeRoles = list(sourceData.keys()), list(mergeData.keys())
    mergeLabels = [i for i in self.parent.parent.data[mergeIndex].labels]
    sourceLabels = [i for i in self.parent.parent.data[targetIndex].labels]
    
    if(not len(mergeRoles)):
      self.parent.parent.statusbar.showMessage('Data set to be merged contains no data, won\'t merge!', self.parent.parent.STATUS_TIME)
    else:
      if(not len(sourceRoles)):
        # okay, this is easy -- source data set is empty just copy the merge data
        combinedData = mergeData
        combinedLabels = mergeLabels
      else:
        # first consider labels?
        if(len(mergeLabels) or len(sourceLabels)):
          # expand labels to correct size
          i = 0
          while(len(sourceLabels) < len(sourceData[sourceRoles[0]])):
            sourceLabels.append(i)
            i += 1
          while(len(mergeLabels) < len(mergeData[mergeRoles[0]])):
            mergeLabels.append(i)
            i += 1
        combinedLabels = sourceLabels + mergeLabels
        # then deal with data
        allRoles = list(set(sourceRoles + mergeRoles))
        # expand data sets prior to merger?
        defaultValues = {'xerr': 0.0, 'yerr': 0.0, 'labels': '', 'fval': 0.0, 'resid': 0.0, 'x': 0.0, 'y': 0.0}
        missingRoles = [i for i in allRoles if (not i in sourceRoles)]
        missingRoles2 = [i for i in allRoles if (not i in mergeRoles)]
        # expand source data set by needed columns
        length = len(sourceData[sourceRoles[0]])
        for entry in missingRoles:
          sourceData[entry] = [defaultValues[entry] for i in range(length)]
        # also expand merge data set by needed columns
        length = len(mergeData[mergeRoles[0]])
        for entry in missingRoles2:
          mergeData[entry] = [defaultValues[entry] for i in range(length)]
        # now append merge data set
        length = len(mergeData[mergeRoles[0]])
        for entry in sourceData:
          if(entry in mergeData):
            sourceData[entry] = np.hstack((sourceData[entry], mergeData[entry]))
          else:
            sourceData[entry] = np.hstack((sourceData[entry], np.array([defaultValues[entry] for i in range(length)])))
        combinedData = sourceData
        
      # now generate a new data set for the merged data
      newData, newRoles = [], []
      for key in combinedData:
        newRoles.append(key)
        if(len(newData)):
          newData = np.vstack((newData, combinedData[key]))
        else:
          newData = combinedData[key]
      newData = np.transpose(newData)

      self.parent.parent.data.append(DataObject(self.parent.parent))
      self.parent.parent.data[-1].setName('merge_' + self.parent.parent.data[targetIndex].name)
      self.parent.parent.data[-1].setNameResid('merge_' + self.parent.parent.data[targetIndex].nameResid)
      # need to copy contents of original object
      self.parent.parent.data[-1].spawned(self.parent.parent.data[targetIndex])
      # hide merged data set
      self.parent.parent.data[mergeIndex].setVisibility(False, redraw=False)
      self.parent.parent.data[mergeIndex].setVisibilityResid(False, redraw=False)
      # need to deal with labels which can be messed up if one of the two data sets does not contain any
      # we need to assign the labels correctly prior to the call to self.helperDeclusterAndMerge()
      self.parent.parent.data[-1].labels = combinedLabels
      # a number of calls to update plots
      # note that the subsequent call to setData() deliberately ignores fval and resid (although we extended them above)
      # this may be changed in the future, hence the assignment
      self.helperDeclusterAndMerge(sourceIndex=targetIndex, newData=newData, newRoles=newRoles, hideOriginal=True)
      # give voice
      self.parent.parent.statusbar.showMessage('Generated merged data set, while preserving original data sets.', self.parent.parent.STATUS_TIME, color='blue')
      # close menu
      self.close()
    
  def updateDataSetSelector(self, targetIndex):
    # populates and updates the QCombobox data set selector
    items = [entry for entry in self.parent.parent.data]
    self.dataSetSelector.clear()
    for index, item in enumerate(items):
      if(index != targetIndex):
        self.dataSetSelector.addItem(str(index + 1) + ' - ' + item.name)
        
    # do we have any items?
    if(self.dataSetSelector.count()):
      self.dataSetSelector.setEnabled(True)
      self.mergeButton.setEnabled(True)
    else:
      self.dataSetSelector.setEnabled(False)
      self.mergeButton.setEnabled(False)

  def toggleRestricted(self, high=False):
    # toggles restriction of plot intervall
    if(high):
      state = self.labelDisplayIntervalHigh.isChecked()
    else:
      state = self.labelDisplayIntervalLow.isChecked()
    self.updateRestrictedFields(state, high=high)

    if(high):
      if(not state):
        self.target.setRestricted('restrictMeHigh', state, redraw=True)
      else:
        self.target.setRestricted('restrictMeHigh', state, redraw=False)
        self.changeRestricted(key='restrictHigh', entryfield=self.entryDisplayIntervalHigh, minval=None, maxval=None)
    else:
      if(not state):
        self.target.setRestricted('restrictMeLow', state, redraw=True)
      else:
        self.target.setRestricted('restrictMeLow', state, redraw=False)
        self.changeRestricted(key='restrictLow', entryfield=self.entryDisplayIntervalLow, minval=None, maxval=None)

  def updateRestrictedFields(self, state, high=False):
    # changes activity of entry fields for plot interval control
    if(high):
      item = self.entryDisplayIntervalHigh
    else:
      item = self.entryDisplayIntervalLow
    if(hasattr(item, 'setEnabled')):
      item.setEnabled(state)

  def changeRestricted(self, key=None, entryfield=None, minval=0, maxval=1):
    if((self.target != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      if(maxval != None):
        value = np.min((value, maxval))
      if(minval != None):
        value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))

      self.target.setRestricted(key, value, redraw=True)
    
  def toggleDisplay(self, button=None, target=None, flag=None, start=-1):
    # toggles visibility of submenus
    # helper function
    def hideMe(target=None):
      if(target != None):
        target.hide()
        self.adjustSize()
        
    def showMe():
      # self.violinRow.adjustSize()
      # self.adjustSize()
      pass
        
    if((button != None) and (target != None)):
      if(button.isChecked()):
        target.show()
        height = target.sizeHint().height()
        if(abs(start - height) > 10):
          if(height > 0):
            animate = QtCore.QParallelAnimationGroup(target)
            animate.addAnimation(QtCore.QPropertyAnimation(target, b"maximumHeight"))
            animate.addAnimation(QtCore.QPropertyAnimation(target, b"minimumHeight"))
            for i in range(2):
              anim = animate.animationAt(i)
              anim.setEndValue(height)
              anim.setDuration(250)
              if(start > -1):
                anim.setStartValue(start)
            if(hasattr(self, 'displayViolinButton') and (button == self.displayViolinButton)):
              animate.finished.connect(showMe)
            animate.start()
        else:
          # if there is no big size difference, immediately update display
          if(hasattr(self, 'displayViolinButton') and (button == self.displayViolinButton)):
            self.violinRow.adjustSize()
        if((flag != None) and (hasattr(self.parent, flag))):
          self.parent.__dict__[flag] = True
      else:
        height = target.sizeHint().height()
        if(height > 0):
          animate = QtCore.QParallelAnimationGroup(target)
          animate.addAnimation(QtCore.QPropertyAnimation(target, b"maximumHeight"))
          animate.addAnimation(QtCore.QPropertyAnimation(target, b"minimumHeight"))
          animate.finished.connect(partial(hideMe, target))
          for i in range(2):
            anim = animate.animationAt(i)
            anim.setStartValue(height)
            anim.setEndValue(0)
            anim.setDuration(250)
          # add animation that controls the size of the container
          # otherwise, we get a white margin in the bottom
          height2 = self.geometry().height()
          animate.addAnimation(QtCore.QPropertyAnimation(self, b"maximumHeight"))
          anim = animate.animationAt(2)
          anim.setStartValue(height2)
          anim.setEndValue(height2 - height)
          anim.setDuration(250)
          animate.start()
        else:
          # this call needed for initializing the height values
          target.setMaximumHeight(0)
          target.setMinimumHeight(0)
          target.hide()
        if((flag != None) and (hasattr(self.parent, flag))):
          self.parent.__dict__[flag] = False
      self.adjustSize()

  def makeActive(self, leader, index):
    field = None
    if(leader.startswith('Data')):
      if(index != self.parent.parent.activeData):
        if(hasattr(self.parent.parent.objectsarea, 'dataSetTable')):
          field = self.parent.parent.objectsarea.dataSetTable.cellWidget(index, 1)
    elif(leader.startswith('Curve')):
      if(index != self.parent.parent.activeFit):
        if(hasattr(self.parent.parent.objectsarea, 'curvesTable')):
          field = self.parent.parent.objectsarea.curvesTable.cellWidget(index, 1)
    
    if(field != None):
      field.setChecked(True)
    self.activeButton.setEnabled(False)

  def alterZ(self, leader, index, direction):
    field = None
    if(leader.startswith('Data')):
      if(hasattr(self.parent.parent.objectsarea, 'dataSetTable')):
        field = self.parent.parent.objectsarea.dataSetTable.cellWidget(index, 2)
    elif(leader.startswith('Curve')):
      if(hasattr(self.parent.parent.objectsarea, 'curvesTable')):
        field = self.parent.parent.objectsarea.curvesTable.cellWidget(index, 2)
    elif(leader.startswith('Resid')):
      if(hasattr(self.parent.parent.objectsarea, 'residTable')):
        field = self.parent.parent.objectsarea.residTable.cellWidget(index + 1, 2)
    elif(leader.startswith('Zero Line')):
      if(hasattr(self.parent.parent.objectsarea, 'residTable')):
        field = self.parent.parent.objectsarea.residTable.cellWidget(index, 2)

    if(field != None):
      value, minimum, maximum = field.value(), field.minimum(), field.maximum()
      newValue = value + direction
      newValue = max(minimum, newValue)
      newValue = min(maximum, newValue)
      # set new value
      if(newValue != value):
        field.setValue(newValue)
        # mysteriously, we have to formally trigger the event
        # recall that it was automatic in previous PyQt versions
        field.editingFinished.emit()
      # (in)activate control buttons
      if(newValue == minimum):
        self.zDownButton.setEnabled(False)
      else:
        self.zDownButton.setEnabled(True)
      if(newValue == maximum):
        self.zUpButton.setEnabled(False)
      else:
        self.zUpButton.setEnabled(True)

  def setField(self, leader, index):
    text = self.entryField.text()
    field = None
    if(leader.startswith('Data')):
      if(hasattr(self.parent.parent.objectsarea, 'dataSetTable')):
        field = self.parent.parent.objectsarea.dataSetTable.cellWidget(index, 3)
    elif(leader.startswith('Curve')):
      if(hasattr(self.parent.parent.objectsarea, 'curvesTable')):
        field = self.parent.parent.objectsarea.curvesTable.cellWidget(index, 3)
    elif(leader.startswith('Resid')):
      if(hasattr(self.parent.parent.objectsarea, 'residTable')):
        field = self.parent.parent.objectsarea.residTable.cellWidget(index + 1, 3)
        
    if(field != None):
      field.setText(text)
      field.editingFinished.emit()

  def guessDeclusterTol(self, index):
    # proposes tolerances for declustering in x and y
    scaleMarker = 1.1
    currWidth, currHeight = 72.0 * self.parent.parent.plotArea.matplot.get_size_inches()
    if(self.parent.parent.data[index].handleData != None):
      markersize = self.parent.parent.data[index].handleData.get_markersize() + self.parent.parent.data[index].handleData.get_markeredgewidth()
      fractionWidth , fractionHeight = currWidth / markersize, currHeight / markersize
      absoluteWidth = scaleMarker * abs(self.parent.parent.plotArea.maxX - self.parent.parent.plotArea.minX) / fractionWidth
      absoluteHeight = scaleMarker * abs(self.parent.parent.plotArea.maxY - self.parent.parent.plotArea.minY) / fractionHeight
      # update tolerances
      self.parent.declusterTolX, self.parent.declusterTolY = absoluteWidth, absoluteHeight
      self.declusterTolXEntry.setText(self.parent.parent.formatNumber(absoluteWidth))
      self.declusterTolYEntry.setText(self.parent.parent.formatNumber(absoluteHeight))

  def transformerData(self, sourceIndex=None):
    # applies transformations on existing data
    formulaX, formulaY = self.dataTransformXEntry.text(), self.dataTransformYEntry.text()
    if((sourceIndex != None) and (len(formulaX) or len(formulaY))):
      data = self.parent.parent.data[sourceIndex].value()
      if(('x' in data) and ('y' in data) and len(data['x'])):
        # prepare data array for transformation
        procData, roles = [], []
        for role, value in data.items():
          roles.append(role)
          procData.append(value)
        procData = np.array(procData).transpose()
        procData2 = deepcopy(procData)
        # process x
        success = True
        if(len(formulaX)):
          transDataX, retv = self.parent.parent.dataarea.transformer(sourceData=procData, roles=roles, formula='x = ' + formulaX, axis='x')
          success = success and retv
        # process y
        if(len(formulaY)):
          transDataY, retv = self.parent.parent.dataarea.transformer(sourceData=procData2, roles=roles, formula='y = ' + formulaY, axis='y')
          success = success and retv
        # check for errors in transformation
        if(success):
          # replace transformed values
          if(len(formulaX)):
            for item in ['x', 'xerr']:
              if(item in roles):
                procData[:, roles.index(item)] = transDataX[:, roles.index(item)]
          if(len(formulaY)):
            for item in ['y', 'yerr']:
              if(item in roles):
                procData[:, roles.index(item)] = transDataY[:, roles.index(item)]
          
          # weed out inf and nan values
          for role in roles:
            index = roles.index(role)
            if(role != 'labels'):
              procData = procData[np.isfinite(procData[:, index])]
          
          # generate new copy of data with transformed data
          if(procData.size):
            self.parent.parent.data.append(DataObject(self.parent.parent))
            self.parent.parent.data[-1].setName('transf_' + self.parent.parent.data[sourceIndex].name)
            self.parent.parent.data[-1].setNameResid('transf_' + self.parent.parent.data[sourceIndex].nameResid)
            # need to copy contents of original object
            self.parent.parent.data[-1].spawned(self.parent.parent.data[sourceIndex])
            # a number of calls to update plots
            self.helperDeclusterAndMerge(sourceIndex=sourceIndex, newData=procData, newRoles=roles, hideOriginal=True)
            # give voice
            self.parent.parent.statusbar.showMessage('Transformed data set, while preserving original copy.', self.parent.parent.STATUS_TIME, color='blue')
          else:
            self.parent.parent.statusbar.showMessage('Only nan/inf values left after data transformation!', self.parent.parent.STATUS_TIME)
        else:
          pass
          # don't need to output error as this should have been done by transformer() function
      else:
        self.parent.parent.statusbar.showMessage('Cannot transform because x and/or y data missing!', self.parent.parent.STATUS_TIME)
    else:
      self.parent.parent.statusbar.showMessage('Enter a formula for x and/or y to transform data.', self.parent.parent.STATUS_TIME)

  def averageData(self, index):
    # averages data to thereby smooth them
    avgNumberPoints, avgMovingCheck = self.parent.avgNumberPoints, self.parent.avgMovingCheck
    labels = np.array(self.parent.parent.data[index].labels)
    data, roles = self.parent.parent.data[index].getData_n_Fit()

    # process data and only retain certain columns
    wanted = ['x', 'y', 'xerr', 'yerr']
    newData, newRoles = [], []
    for ind, key in enumerate(roles):
      if(key in wanted):
        newRoles.append(key)
        if(len(newData)):
          newData = np.vstack((newData, data[:, ind]))
        else:
          newData = data[:, ind]
    newData = np.transpose(newData)
    
    if(('x' in newRoles) and ('y' in newRoles)):
      if(avgMovingCheck):
        stepsize = 1
      else:
        stepsize = avgNumberPoints
      if('labels' in newRoles):
        newData, labels = self.parent.parent.dataarea.movingAverage(sourceData=newData, roles=newRoles, average=avgNumberPoints, stepsize=stepsize, labels=labels)
        labels = list(labels)
      else:
        newData = self.parent.parent.dataarea.movingAverage(sourceData=newData, roles=newRoles, average=avgNumberPoints, stepsize=stepsize)
        
      # now generate new data object and populate
      self.parent.parent.data.append(DataObject(self.parent.parent))
      self.parent.parent.data[-1].setName('avg_' + self.parent.parent.data[index].name)
      self.parent.parent.data[-1].setNameResid('avg_' + self.parent.parent.data[index].nameResid)
      # need to copy contents of original object
      self.parent.parent.data[-1].spawned(self.parent.parent.data[index])
      # a number of calls to update plots
      self.helperDeclusterAndMerge(sourceIndex=index, newData=newData, newRoles=newRoles, hideOriginal=True)
      # give voice
      self.parent.parent.statusbar.showMessage('Transformed data set, while preserving original copy.', self.parent.parent.STATUS_TIME, color='blue')
    else:
      self.parent.parent.statusbar.showMessage('Cannot average data because x and/or y data missing!', self.parent.parent.STATUS_TIME)

  def declusterData(self, index):
    # declusters data in swarmplot fashion
    deltaX, deltaY, maxX = self.parent.declusterTolX, self.parent.declusterTolY, self.parent.declusterMaxX
    data = self.parent.parent.data[index].value()
    if(('x' in data) and ('y' in data)):
      # implement option to terminate declustering if stalled
      self.runFlag, self.fev = True, 0
      self.daughterWindow = MinWindow(self, 'Decluster Data')
      # apply styles to popup window
      if(QSTYLE != None):
        self.daughterWindow.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.daughterWindow.setStyleSheet(QSTYLESHEET)
      self.daughterWindow.show()
      self.daughterWindow.messageLabel.setText('')
      self.daughterWindow.iterLabel.setText('no. data points: ' + str(len(data['x'])))
      useData = np.vstack((data['x'], data['y']))
      displacement = np.zeros(len(data['x']))
      # loop through data array
      dataChanged, initialSign = False, 1.0
      for line in range(1, len(data['x'])):
        if(self.daughterWindow != None):
          self.daughterWindow.minLabel.setText('current data point: ' + str(line))
        currX, currY = useData[0, line], useData[1, line]
        currDeltaX, currDisplacement, initCycle = 0, 0, True
        collisionCheck = True
        while(collisionCheck and self.runFlag):
          collisionCheck, line2 = False, 0
          # adjust delta for current cycle
          if(initCycle):
            initCycle = False
          elif(currDeltaX > 0):
            currDeltaX = -currDeltaX
            currDisplacement *= -1
          else:
            currDeltaX = -currDeltaX + deltaX
            currDisplacement = -currDisplacement + 1
          # check for data points spaced too closely
          while(line2 < line):
            if(not (self.fev % 100)):
              QtCore.QCoreApplication.processEvents()
            self.fev += 1
            normDistance = ((useData[0, line2] - currX - initialSign * currDeltaX) / deltaX) ** 2 + ((useData[1, line2] - currY) / deltaY) ** 2
            if(normDistance <= 1.0):
              collisionCheck = True
              line2 = line
            else:
              line2 += 1
        
        # okay, either we did not find a collision or we adjusted delta accordingly
        if(abs(currDeltaX) > 0):
          useData[0, line] += initialSign * currDeltaX
          displacement[line] = initialSign * currDisplacement
          dataChanged = True
          initialSign *= -1.0
      
      # after declustering, restrict lateral displacement to max value
      if(maxX * deltaX):
        extraDisplacements = {0: 0, 1: .5, 2: .25, 3: .75, 4: .375, 5: .625, 6: .875, 7: .125}
        for line in range(1, len(data['x'])):
          if(abs(displacement[line]) > maxX):
            # check if extra fractional displacement is required
            if(maxX):
              extra = extraDisplacements[int(abs(displacement[line]) / maxX) % len(extraDisplacements)]
            else:
              extra = 0
            
            # restore original x value
            useData[0, line] -= deltaX * displacement[line]
            if(displacement[line] > 0):
              # then limit to max lateral displacement
              useData[0, line] += deltaX * ((displacement[line]) % (maxX + 1) + extra)
            else:
              # add an initial displacement half step in the negative direction to avoid bunch up of points at zero line
              useData[0, line] -= deltaX * ((-displacement[line]) % (maxX + 1) + extra)

      # create declustered data object and plot
      if(not self.runFlag):
        self.parent.parent.statusbar.showMessage('Declustering data aborted by user.', self.parent.parent.STATUS_TIME)
      elif(dataChanged):
        data['x'], data['y'] = useData[0], useData[1]
        newData, newRoles = [], []
        for key in data:
          newRoles.append(key)
          if(len(newData)):
            newData = np.vstack((newData, data[key]))
          else:
            newData = data[key]
        newData = np.transpose(newData)

        self.parent.parent.data.append(DataObject(self.parent.parent))
        self.parent.parent.data[-1].setName('declust_' + self.parent.parent.data[index].name)
        self.parent.parent.data[-1].setNameResid('declust_' + self.parent.parent.data[index].nameResid)
        # need to copy contents of original object
        self.parent.parent.data[-1].spawned(self.parent.parent.data[index])
        # turn off violin/box display in newly generated object to prevent ungodly mess
        self.parent.parent.data[-1].Violinstyle['mode'] = 0
        # a number of calls to update plots
        self.helperDeclusterAndMerge(sourceIndex=index, newData=newData, newRoles=newRoles, hideOriginal=True)
        # give voice
        self.parent.parent.statusbar.showMessage('Generated declustered copy of data set, while preserving original one.', self.parent.parent.STATUS_TIME, color='blue')
        # close menu
        self.close()
      else:
        self.parent.parent.statusbar.showMessage('No data found that would need declustering!', self.parent.parent.STATUS_TIME, color='blue')

      # clean up as needed
      if(self.daughterWindow != None):
        self.daughterWindow.close()
        self.daughterWindow = None
    else:
      self.parent.parent.statusbar.showMessage('Cannot decluster because x and/or y data missing!', self.parent.parent.STATUS_TIME)

  def helperDeclusterAndMerge(self, sourceIndex, newData, newRoles, hideOriginal=True):
    # jointly issued calls by declusterData() and mergeData()
    # update data in new object
    self.parent.parent.data[-1].setData(newData, newRoles, labels=self.parent.parent.data[-1].labels)
    # set new data object as active
    self.parent.parent.activeData = (len(self.parent.parent.data) - 1)
    self.parent.changeActiveDataSet(len(self.parent.parent.data) - 1, setCheck=False, redraw=False)
    # hide original data set
    if(hideOriginal):
      self.parent.parent.data[sourceIndex].setVisibility(False, redraw=False)
      self.parent.parent.data[sourceIndex].setVisibilityResid(False, redraw=False)
    # cause data to be drawn
    self.parent.parent.data[-1].drawMe(redraw=False)
    self.parent.refreshDataTable()
    self.parent.dataSetTable.scrollToBottom()
    # also create a new resid object
    self.parent.parent.plotArea.setAxisLimits(lower=self.parent.parent.plotArea.minX, upper=self.parent.parent.plotArea.maxX, axis='x', updateLabel=False, target='resid', redraw=False, updateGrid=True)
    self.parent.parent.plotArea.setAxisLimits(lower=self.parent.parent.plotArea.minX_div, upper=self.parent.parent.plotArea.maxX_div, axis='x2', updateLabel=False, target='resid', redraw=False, updateGrid=True)
    self.parent.parent.data[-1].drawMeResid(redraw=False)
    self.parent.parent.plotArea.handleResidZero = self.parent.parent.plotArea.plotResidZero(self.parent.parent.plotArea.handleResidZero, redraw=True)
    self.parent.refreshResidTable()
    self.parent.residTable.scrollToBottom()
    # also refresh curves table to account for increased total number of items
    self.parent.refreshCurvesTable()
    self.parent.refreshExtrasTable()
    self.parent.parent.plotArea.updateToggloContainer()
    self.parent.parent.globalarea.updateDataSetTable()
    # update results table
    self.parent.parent.resultsarea.setDataSet(currIndex=len(self.parent.parent.data) - 1, maxIndex=len(self.parent.parent.data) - 1)
    # redraw active curve over current x interval
    self.parent.parent.fit[self.parent.parent.activeFit].drawMe(redraw=False, rescale=False)
    # update legend if needed
    self.parent.updateLegend(redraw=True)

  def changeTol(self, entryfield=None, minval=0, maxval=1):
    # check paramter boundaries
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
    # update parameters in parent object
    if(entryfield == self.declusterTolXEntry):
      self.parent.declusterTolX = value
    else:
      self.parent.declusterTolY = value

  def alterToln(self, entryfield=None, integer=False):
    # check paramter boundaries
    try:
      if(integer):
        value = int(entryfield.value())
      else:
        value = float(entryfield.value())
      originalvalue = value
    except:
      if(integer):
        value = 0
        originalvalue = 1
      else:
        value = 0.0
        originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      entryfield.setValue(value)
    # update parameters in parent object
    self.parent.declusterMaxX = value

class GraphicsArea(QWidgetMac):
  def __init__(self, parent=None, secondAxes=False):
    super(GraphicsArea, self).__init__()
    self.parent = parent
    self.secondAxes = secondAxes
    
    # initial export filter
    self.exportFilter = EXPORTFILTER
    
    # initialize filenames
    self.currStyleFile = None
    self.currExportFile = None
    
    # advanced export settings
    self.advancedExport = {'plotDPI': 600, 'plotResid': True, 'SVGtxt2path': False, 'advancedGraphics': False, 'unicodeHyphen': True, 'stateFileFormat': 'statez'}
    
    # int and float validators
    self.validInt = MyValidInt()
    self.validFloat = MyValidFloat()

    # set up styles
    self.linestyles = ['None', 'solid', 'dashed', 'dashdot', 'dotted']
    self.dashstyles = ['butt', 'round', 'projecting']
    self.positionstyles = ['axes', 'data']
    # linestyles for graphics elements that do not heed 'None' (e.g. axis spines)
    self.linestyles2 = ['solid', 'dashed', 'dashdot', 'dotted']
    
    # set up GUI
    self.advancedGraphicsSettings = []
    self.buildRessource()
    # now populate fields
    self.updateFields(initialize=True)
    # and connect events
    self.connectEvents()

  def buildRessource(self):
    # prepare colored bg boxes
    outCount = 0
    outTypes = ['out1', 'out2']
    
    # build gui
    self.vLayout_0 = QtWidgets.QVBoxLayout(self)
    self.vLayout_0.setContentsMargins(0, 0, 0, 0)
    self.vLayout_0.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
    
    # container widget for subsequent widgets
    self.containerScroll = QtWidgets.QScrollArea()
    self.containerScroll.setWidgetResizable(True)
    # don't use palettes as these are incompatible with style sheets
    self.containerScroll.setAutoFillBackground(True)
    self.vLayout_0.addWidget(self.containerScroll)

    self.containerBox = QWidgetMac()
    # setting the background somehow breaks QStyleSet for children :(
    #self.containerBox.setStyleSheet('#stultus {background-color: white;}')
    self.containerBox.setAutoFillBackground(True)
    self.vLayout = QtWidgets.QVBoxLayout(self.containerBox)
    self.vLayout.setContentsMargins(0, 0, 0, 0)
    self.vLayout.setSpacing(3)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.containerScroll.setWidget(self.containerBox)
    
    # x label config
    self.alignHorizontal = ['left', 'center', 'right']
    self.alignVertical = ['top', 'center', 'bottom', 'baseline']

    if(not self.secondAxes):
      self.outerBox1 = QWidgetMac()
      self.outerBox1.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox1)
      self.Layout_outerBox1 = QtWidgets.QVBoxLayout(self.outerBox1)
      self.Layout_outerBox1.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox1.setSpacing(2)
      self.Layout_outerBox1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
        
      self.configXBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configXBox)
      self.Layout_outerBox1.addWidget(self.configXBox)
      self.Layout_configX = QtWidgets.QHBoxLayout(self.configXBox)
      self.Layout_configX.setContentsMargins(0, 0, 0, 0)
      self.Layout_configX.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configX.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configXLabel = QPushButtonCheckable()
      self.configXLabel.setText('x label')
      self.configXLabel.setToolTip('Show x axis label')
      self.configXLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configXLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX.addWidget(self.configXLabel)
      self.configXName = QLineEditClick()
      self.configXName.setToolTip('Set x axis label')
      self.configXName.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configXName.setMaximumSize(QtCore.QSize(int(298 * SCALEFONT), int(BASE_SIZE)))
      self.configXName.setMinimumSize(QtCore.QSize(int(298 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX.addWidget(self.configXName)
  
      # x label config 2nd line
      self.configXBox2 = QWidgetMac()
      ###self.vLayout.addWidget(self.configXBox2)
      self.Layout_outerBox1.addWidget(self.configXBox2)
      self.Layout_configX2 = QtWidgets.QHBoxLayout(self.configXBox2)
      self.Layout_configX2.setContentsMargins(0, 0, 0, 0)
      self.Layout_configX2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configX2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX2.addWidget(spacer)
      
      self.configXSizeLabel = QtWidgets.QLabel('font')
      self.configXSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configXSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX2.addWidget(self.configXSizeLabel)
      self.configXColorButton = QPushButtonColor()
      self.configXColorButton.setToolTip('Color of x axis label')
      self.configXColorButton.setAutoFillBackground(False)
      self.configXColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configXColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configXColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configX2.addWidget(self.configXColorButton)

      self.configXSize = QDoubleSpinClick()
      self.Layout_configX2.addWidget(self.configXSize)
      self.configXSize.setMinimum(0.0)
      self.configXSize.setMaximum(100.0)
      self.configXSize.setToolTip('Font size of x axis object')
      self.configXSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configXSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      
      self.configXBold = QPushButtonCheckable()
      self.configXBold.setText('B')
      self.configXBold.setToolTip('Font weight of x axis label')
      self.configXBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configXBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configXBold.font()
      useFont.setBold(True)
      self.configXBold.setFont(useFont)
      self.Layout_configX2.addWidget(self.configXBold)
        
      self.configXItalic = QPushButtonCheckable()
      self.configXItalic.setText('I')
      self.configXItalic.setToolTip('Font style of x axis label')
      self.configXItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configXItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configXItalic.font()
      useFont.setItalic(True)
      self.configXItalic.setFont(useFont)
      self.Layout_configX2.addWidget(self.configXItalic)
      
      if(0):
        # unfortunately not supported by matplotlib currently (this is a known bug)
        self.configXVariant = QPushButtonCheckable()
        self.configXVariant.setText('Aa')
        self.configXVariant.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.configXVariant.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        useFont = self.configXVariant.font()
        useFont.setCapitalization(QtGui.QFont.SmallCaps)
        self.configXVariant.setFont(useFont)
        self.Layout_configX2.addWidget(self.configXVariant)
  
      self.configXFont = FontoBox()
      self.configXFont.setToolTip('Font of x axis label')
      self.configXFont.addItems(items=self.parent.fontNames)
      self.configXFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.configXFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX2.addWidget(self.configXFont)
  
      # x label config 3rd line
      self.configXBox3 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configXBox3)
      ###self.vLayout.addWidget(self.configXBox3)
      self.Layout_outerBox1.addWidget(self.configXBox3)
      self.Layout_configX3 = QtWidgets.QHBoxLayout(self.configXBox3)
      self.Layout_configX3.setContentsMargins(0, 0, 0, 0)
      self.Layout_configX3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configX3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX3.addWidget(spacer)
      
      self.configXAngleLabel = QtWidgets.QLabel('angle')
      self.configXAngleLabel.setObjectName('advanced')
      self.configXAngleLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configXAngleLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX3.addWidget(self.configXAngleLabel)

      self.configXAngle = QDoubleSpinClick()
      self.Layout_configX3.addWidget(self.configXAngle)
      self.configXAngle.setMinimum(0.0)
      self.configXAngle.setMaximum(360.0)
      self.configXAngle.setToolTip('Angle of x axis label')
      self.configXAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configXAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configXAngle.setSingleStep(10)
      self.configXAngle.setWrapping(True)
      self.configXAngle.setAdaptive(False)

      self.configXAlignmentLabel = QtWidgets.QLabel('align')
      self.configXAlignmentLabel.setObjectName('advanced')
      self.configXAlignmentLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.configXAlignmentLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX3.addWidget(self.configXAlignmentLabel)
  
      self.configXAlignment = QComboBoxMac()
      self.configXAlignment.setToolTip('Horizontal alignment of x axis label')
      self.configXAlignment.addItems(self.alignHorizontal)
      self.configXAlignment.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configXAlignment.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX3.addWidget(self.configXAlignment)
  
      self.configXAlignmentVertical = QComboBoxMac()
      self.configXAlignmentVertical.setToolTip('Vertical alignment of x axis label')
      self.configXAlignmentVertical.addItems(self.alignVertical)
      self.configXAlignmentVertical.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configXAlignmentVertical.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX3.addWidget(self.configXAlignmentVertical)
      
      self.configXLinespacingLabel = QtWidgets.QLabel('linespacing')
      self.configXLinespacingLabel.setObjectName('advanced')
      self.configXLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configXLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX3.addWidget(self.configXLinespacingLabel)
  
      self.configXLinespacing = QLineEditClick()
      self.configXLinespacing.setToolTip('Linespacing of x axis label')
      self.configXLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configXLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configXLinespacing.setValidator(self.validFloat)
      self.Layout_configX3.addWidget(self.configXLinespacing)
  
      # x label config 4th line
      self.configXBox4 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configXBox4)
      ###self.vLayout.addWidget(self.configXBox4)
      self.Layout_outerBox1.addWidget(self.configXBox4)
      self.Layout_configX4 = QtWidgets.QHBoxLayout(self.configXBox4)
      self.Layout_configX4.setContentsMargins(0, 0, 0, 0)
      self.Layout_configX4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configX4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX4.addWidget(spacer)

      self.configXPadLabel = QtWidgets.QLabel('pad')
      self.configXPadLabel.setObjectName('advanced')
      self.configXPadLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configXPadLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX4.addWidget(self.configXPadLabel)
  
      self.configXPad = QLineEditClick()
      self.configXPad.setToolTip('Padding of x axis label')
      self.configXPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configXPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configXPad.setValidator(self.validFloat)
      self.Layout_configX4.addWidget(self.configXPad)

      self.configXPosLabel = QtWidgets.QLabel('position')
      self.configXPosLabel.setObjectName('advanced')
      self.configXPosLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configXPosLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configX4.addWidget(self.configXPosLabel)
  
      self.configXPos = QLineEditClick()
      self.configXPos.setToolTip('Relative position of x axis label')
      self.configXPos.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configXPos.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configXPos.setValidator(self.validFloat)
      self.Layout_configX4.addWidget(self.configXPos)

      # controls for slaved x axis
      self.outerBox1c = QWidgetMac()
      self.outerBox1c.setObjectName(outTypes[outCount % 2])
      outCount += 1
      ###self.advancedGraphicsSettings.append(self.outerBox1c)
      self.vLayout.addWidget(self.outerBox1c)
      self.Layout_outerBox1c = QtWidgets.QVBoxLayout(self.outerBox1c)
      self.Layout_outerBox1c.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox1c.setSpacing(2)
      self.Layout_outerBox1c.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.toggleItemsTransformed = []
      self.toggleItemsTransformed.append(self.outerBox1c)

      self.slavedXLabelBox = QWidgetMac()
      ###self.advancedGraphicsSettings.append(self.slavedXLabelBox)
      self.Layout_outerBox1c.addWidget(self.slavedXLabelBox)
      self.Layout_slavedXLabel = QtWidgets.QHBoxLayout(self.slavedXLabelBox)
      self.Layout_slavedXLabel.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedXLabel.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedXLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.slavedXLabelShow = QPushButtonCheckable()
      self.slavedXLabelShow.setText('transf. label')
      self.slavedXLabelShow.setToolTip('Show label for transformed x axis')
      self.slavedXLabelShow.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXLabelShow.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel.addWidget(self.slavedXLabelShow)
      
      self.configSlavedXLabel = QLineEditClick()
      self.configSlavedXLabel.setToolTip('Set transformed x axis label')
      self.configSlavedXLabel.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configSlavedXLabel.setMaximumSize(QtCore.QSize(int(298 * SCALEFONT), int(BASE_SIZE)))
      self.configSlavedXLabel.setMinimumSize(QtCore.QSize(int(298 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel.addWidget(self.configSlavedXLabel)

      # slaved x label config 2nd line
      self.slavedXLabelBox2 = QWidgetMac()
      self.Layout_outerBox1c.addWidget(self.slavedXLabelBox2)
      self.Layout_slavedXLabel2 = QtWidgets.QHBoxLayout(self.slavedXLabelBox2)
      self.Layout_slavedXLabel2.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedXLabel2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedXLabel2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel2.addWidget(spacer)
      
      self.slavedXSizeLabel = QtWidgets.QLabel('font')
      self.slavedXSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel2.addWidget(self.slavedXSizeLabel)
      self.slavedXColorButton = QPushButtonColor()
      self.slavedXColorButton.setToolTip('Color of transformed x axis label')
      self.slavedXColorButton.setAutoFillBackground(False)
      self.slavedXColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedXColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedXColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_slavedXLabel2.addWidget(self.slavedXColorButton)

      self.slavedXSize = QDoubleSpinClick()
      self.Layout_slavedXLabel2.addWidget(self.slavedXSize)
      self.slavedXSize.setMinimum(0.0)
      self.slavedXSize.setMaximum(100.0)
      self.slavedXSize.setToolTip('Font size of transformed x axis object')
      self.slavedXSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      
      self.slavedXBold = QPushButtonCheckable()
      self.slavedXBold.setText('B')
      self.slavedXBold.setToolTip('Font weight of transformed x axis label')
      self.slavedXBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedXBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.slavedXBold.font()
      useFont.setBold(True)
      self.slavedXBold.setFont(useFont)
      self.Layout_slavedXLabel2.addWidget(self.slavedXBold)
        
      self.slavedXItalic = QPushButtonCheckable()
      self.slavedXItalic.setText('I')
      self.slavedXItalic.setToolTip('Font style of transformed x axis label')
      self.slavedXItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedXItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.slavedXItalic.font()
      useFont.setItalic(True)
      self.slavedXItalic.setFont(useFont)
      self.Layout_slavedXLabel2.addWidget(self.slavedXItalic)
      
      if(0):
        # unfortunately not supported by matplotlib currently (this is a known bug)
        self.slavedXVariant = QPushButtonCheckable()
        self.slavedXVariant.setText('Aa')
        self.slavedXVariant.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.slavedXVariant.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        useFont = self.slavedXVariant.font()
        useFont.setCapitalization(QtGui.QFont.SmallCaps)
        self.slavedXVariant.setFont(useFont)
        self.Layout_slavedXLabel2.addWidget(self.slavedXVariant)
  
      self.slavedXFont = FontoBox()
      self.slavedXFont.setToolTip('Font of transformed x axis label')
      self.slavedXFont.addItems(items=self.parent.fontNames)
      self.slavedXFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel2.addWidget(self.slavedXFont)
  
      # slaved x label config 3rd line
      self.slavedXLabelBox3 = QWidgetMac()
      self.Layout_outerBox1c.addWidget(self.slavedXLabelBox3)
      self.advancedGraphicsSettings.append(self.slavedXLabelBox3)
      self.Layout_slavedXLabel3 = QtWidgets.QHBoxLayout(self.slavedXLabelBox3)
      self.Layout_slavedXLabel3.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedXLabel3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedXLabel3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel3.addWidget(spacer)
      
      self.slavedXAngleLabel = QtWidgets.QLabel('angle')
      self.slavedXAngleLabel.setObjectName('advanced')
      self.slavedXAngleLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXAngleLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel3.addWidget(self.slavedXAngleLabel)

      self.slavedXAngle = QDoubleSpinClick()
      self.Layout_slavedXLabel3.addWidget(self.slavedXAngle)
      self.slavedXAngle.setMinimum(0.0)
      self.slavedXAngle.setMaximum(360.0)
      self.slavedXAngle.setToolTip('Angle of transformed x axis label')
      self.slavedXAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXAngle.setSingleStep(10)
      self.slavedXAngle.setWrapping(True)
      self.slavedXAngle.setAdaptive(False)

      self.slavedXAlignmentLabel = QtWidgets.QLabel('align')
      self.slavedXAlignmentLabel.setObjectName('advanced')
      self.slavedXAlignmentLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXAlignmentLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel3.addWidget(self.slavedXAlignmentLabel)
  
      self.slavedXAlignment = QComboBoxMac()
      self.slavedXAlignment.setToolTip('Horizontal alignment of transformed x axis label')
      self.slavedXAlignment.addItems(self.alignHorizontal)
      self.slavedXAlignment.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXAlignment.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel3.addWidget(self.slavedXAlignment)
  
      self.slavedXAlignmentVertical = QComboBoxMac()
      self.slavedXAlignmentVertical.setToolTip('Vertical alignment of transformedx axis label')
      self.slavedXAlignmentVertical.addItems(self.alignVertical)
      self.slavedXAlignmentVertical.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXAlignmentVertical.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel3.addWidget(self.slavedXAlignmentVertical)
      
      self.slavedXLinespacingLabel = QtWidgets.QLabel('linespacing')
      self.slavedXLinespacingLabel.setObjectName('advanced')
      self.slavedXLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel3.addWidget(self.slavedXLinespacingLabel)
  
      self.slavedXLinespacing = QLineEditClick()
      self.slavedXLinespacing.setToolTip('Linespacing of transformed x axis label')
      self.slavedXLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXLinespacing.setValidator(self.validFloat)
      self.Layout_slavedXLabel3.addWidget(self.slavedXLinespacing)
  
      # slaved x label config 4th line
      self.slavedXLabelBox4 = QWidgetMac()
      self.Layout_outerBox1c.addWidget(self.slavedXLabelBox4)
      self.advancedGraphicsSettings.append(self.slavedXLabelBox4)
      self.Layout_slavedXLabel4 = QtWidgets.QHBoxLayout(self.slavedXLabelBox4)
      self.Layout_slavedXLabel4.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedXLabel4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedXLabel4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel4.addWidget(spacer)

      self.slavedXPadLabel = QtWidgets.QLabel('pad')
      self.slavedXPadLabel.setObjectName('advanced')
      self.slavedXPadLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPadLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel4.addWidget(self.slavedXPadLabel)
  
      self.slavedXPad = QLineEditClick()
      self.slavedXPad.setToolTip('Padding of tranformed x axis label')
      self.slavedXPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPad.setValidator(self.validFloat)
      self.Layout_slavedXLabel4.addWidget(self.slavedXPad)

      self.slavedXPosLabel = QtWidgets.QLabel('position')
      self.slavedXPosLabel.setObjectName('advanced')
      self.slavedXPosLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPosLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedXLabel4.addWidget(self.slavedXPosLabel)
  
      self.slavedXPos = QLineEditClick()
      self.slavedXPos.setToolTip('Relative position of transformed x axis label')
      self.slavedXPos.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPos.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPos.setValidator(self.validFloat)
      self.Layout_slavedXLabel4.addWidget(self.slavedXPos)

    # y label config
    self.outerBox1b = QWidgetMac()
    self.outerBox1b.setObjectName(outTypes[outCount % 2])
    outCount += 1
    self.vLayout.addWidget(self.outerBox1b)
    self.Layout_outerBox1b = QtWidgets.QVBoxLayout(self.outerBox1b)
    self.Layout_outerBox1b.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox1b.setSpacing(2)
    self.Layout_outerBox1b.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
    self.configYBox = QWidgetMac()
    ###self.vLayout.addWidget(self.configYBox)
    self.Layout_outerBox1b.addWidget(self.configYBox)
    self.Layout_configY = QtWidgets.QHBoxLayout(self.configYBox)
    self.Layout_configY.setContentsMargins(0, 0, 0, 0)
    self.Layout_configY.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configY.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.configYLabel = QPushButtonCheckable()
    if(self.secondAxes):
      self.configYLabel.setText('y2 label')
      self.configYLabel.setToolTip('Show y2 axis label')
    else:
      self.configYLabel.setText('y label')
      self.configYLabel.setToolTip('Show y axis label')
    self.configYLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.configYLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY.addWidget(self.configYLabel)
    self.configYName = QLineEditClick()
    if(self.secondAxes):
      self.configYName.setToolTip('Set y2 axis label')
    else:
      self.configYName.setToolTip('Set y axis label')
    self.configYName.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.configYName.setMaximumSize(QtCore.QSize(int(298 * SCALEFONT), int(BASE_SIZE)))
    self.configYName.setMinimumSize(QtCore.QSize(int(298 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY.addWidget(self.configYName)

    # y label config 2nd line
    self.configYBox2 = QWidgetMac()
    ###self.vLayout.addWidget(self.configYBox2)
    self.Layout_outerBox1b.addWidget(self.configYBox2)
    self.Layout_configY2 = QtWidgets.QHBoxLayout(self.configYBox2)
    self.Layout_configY2.setContentsMargins(0, 0, 0, 0)
    self.Layout_configY2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configY2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY2.addWidget(spacer)
    
    self.configYSizeLabel = QtWidgets.QLabel('font')
    self.configYSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configYSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY2.addWidget(self.configYSizeLabel)
    self.configYColorButton = QPushButtonColor()
    if(self.secondAxes):
      self.configYColorButton.setToolTip('Color of y2 axis label')
    else:
      self.configYColorButton.setToolTip('Color of y axis label')
    self.configYColorButton.setAutoFillBackground(False)
    self.configYColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configYColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configYColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.Layout_configY2.addWidget(self.configYColorButton)

    self.configYSize = QDoubleSpinClick()
    self.Layout_configY2.addWidget(self.configYSize)
    self.configYSize.setMinimum(0.0)
    self.configYSize.setMaximum(100.0)
    if(self.secondAxes):
      self.configYSize.setToolTip('Font size of y2 axis label')
    else:
      self.configYSize.setToolTip('Font size of y axis label')
    self.configYSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.configYSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))

    self.configYBold = QPushButtonCheckable()
    self.configYBold.setText('B')
    if(self.secondAxes):
      self.configYBold.setToolTip('Font weight of y2 axis label')
    else:
      self.configYBold.setToolTip('Font weight of y axis label')
    self.configYBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configYBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    useFont = self.configYBold.font()
    useFont.setBold(True)
    self.configYBold.setFont(useFont)
    self.Layout_configY2.addWidget(self.configYBold)
      
    self.configYItalic = QPushButtonCheckable()
    self.configYItalic.setText('I')
    if(self.secondAxes):
      self.configYItalic.setToolTip('Font style of y2 axis label')
    else:
      self.configYItalic.setToolTip('Font style of y axis label')
    self.configYItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configYItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    useFont = self.configYItalic.font()
    useFont.setItalic(True)
    self.configYItalic.setFont(useFont)
    self.Layout_configY2.addWidget(self.configYItalic)

    if(0):
      # unfortunately not supported by matplotlib currently (this is a known bug)
      self.configYVariant = QPushButtonCheckable()
      self.configYVariant.setText('Aa')
      self.configYVariant.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configYVariant.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configYVariant.font()
      useFont.setCapitalization(QtGui.QFont.SmallCaps)
      self.configYVariant.setFont(useFont)
      self.Layout_configY2.addWidget(self.configYVariant)

    self.configYFont = FontoBox()
    if(self.secondAxes):
      self.configYFont.setToolTip('Font of y2 axis label')
    else:
      self.configYFont.setToolTip('Font of y axis label')
    self.configYFont.addItems(items=self.parent.fontNames)
    self.configYFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
    self.configYFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY2.addWidget(self.configYFont)

    # y label config 2nd line
    self.configYBox3 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configYBox3)
    ###self.vLayout.addWidget(self.configYBox3)
    self.Layout_outerBox1b.addWidget(self.configYBox3)
    self.Layout_configY3 = QtWidgets.QHBoxLayout(self.configYBox3)
    self.Layout_configY3.setContentsMargins(0, 0, 0, 0)
    self.Layout_configY3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configY3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY3.addWidget(spacer)
    
    self.configYAngleLabel = QtWidgets.QLabel('angle')
    self.configYAngleLabel.setObjectName('advanced')
    self.configYAngleLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configYAngleLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY3.addWidget(self.configYAngleLabel)

    self.configYAngle = QDoubleSpinClick()
    self.Layout_configY3.addWidget(self.configYAngle)
    self.configYAngle.setMinimum(0.0)
    self.configYAngle.setMaximum(360.0)
    if(self.secondAxes):
      self.configYAngle.setToolTip('Angle of y2 axis label')
    else:
      self.configYAngle.setToolTip('Angle of y axis label')
    self.configYAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.configYAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.configYAngle.setSingleStep(10)
    self.configYAngle.setWrapping(True)
    self.configYAngle.setAdaptive(False)
    
    self.configYAlignmentLabel = QtWidgets.QLabel('align')
    self.configYAlignmentLabel.setObjectName('advanced')
    self.configYAlignmentLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
    self.configYAlignmentLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY3.addWidget(self.configYAlignmentLabel)

    self.configYAlignment = QComboBoxMac()
    if(self.secondAxes):
      self.configYAlignment.setToolTip('Horizontal alignment of y2 axis label')
    else:
      self.configYAlignment.setToolTip('Horizontal alignment of y axis label')
    self.configYAlignment.addItems(self.alignHorizontal)
    self.configYAlignment.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.configYAlignment.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY3.addWidget(self.configYAlignment)

    self.configYAlignmentVertical = QComboBoxMac()
    if(self.secondAxes):
      self.configYAlignmentVertical.setToolTip('Vertical alignment of y2 axis label')
    else:
      self.configYAlignmentVertical.setToolTip('Vertical alignment of y axis label')
    self.configYAlignmentVertical.addItems(self.alignVertical)
    self.configYAlignmentVertical.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.configYAlignmentVertical.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY3.addWidget(self.configYAlignmentVertical)

    self.configYLinespacingLabel = QtWidgets.QLabel('linespacing')
    self.configYLinespacingLabel.setObjectName('advanced')
    self.configYLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.configYLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY3.addWidget(self.configYLinespacingLabel)

    self.configYLinespacing = QLineEditClick()
    if(self.secondAxes):
      self.configYLinespacing.setToolTip('Linespacing of y2 axis label')
    else:
      self.configYLinespacing.setToolTip('Linespacing of y axis label')
    self.configYLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configYLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configYLinespacing.setValidator(self.validFloat)
    self.Layout_configY3.addWidget(self.configYLinespacing)

    # y label config 4th line
    self.configYBox4 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configYBox4)
    ###self.vLayout.addWidget(self.configYBox4)
    self.Layout_outerBox1b.addWidget(self.configYBox4)
    self.Layout_configY4 = QtWidgets.QHBoxLayout(self.configYBox4)
    self.Layout_configY4.setContentsMargins(0, 0, 0, 0)
    self.Layout_configY4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configY4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY4.addWidget(spacer)

    self.configYPadLabel = QtWidgets.QLabel('pad')
    self.configYPadLabel.setObjectName('advanced')
    self.configYPadLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configYPadLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY4.addWidget(self.configYPadLabel)

    self.configYPad = QLineEditClick()
    if(self.secondAxes):
      self.configYPad.setToolTip('Padding of y2 axis label')
    else:
      self.configYPad.setToolTip('Padding of y axis label')
    self.configYPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configYPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configYPad.setValidator(self.validFloat)
    self.Layout_configY4.addWidget(self.configYPad)

    self.configYPosLabel = QtWidgets.QLabel('position')
    self.configYPosLabel.setObjectName('advanced')
    self.configYPosLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.configYPosLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configY4.addWidget(self.configYPosLabel)

    self.configYPos = QLineEditClick()
    if(self.secondAxes):
      self.configYPos.setToolTip('Relative position of y2 axis label')
    else:
      self.configYPos.setToolTip('Relative position of y axis label')
    self.configYPos.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configYPos.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configYPos.setValidator(self.validFloat)
    self.Layout_configY4.addWidget(self.configYPos)

    # axis config
    blah = HLine()
    ###self.vLayout.addWidget(blah)
    self.outerBox2 = QWidgetMac()
    self.outerBox2.setObjectName(outTypes[outCount % 2])
    outCount += 1
    self.vLayout.addWidget(self.outerBox2)
    self.Layout_outerBox2 = QtWidgets.QVBoxLayout(self.outerBox2)
    self.Layout_outerBox2.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox2.setSpacing(2)
    self.Layout_outerBox2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
    self.configAxisBox = {}; self.Layout_configAxis = {}
    self.configAxisLabel = {}
    self.configAxisWidthLabel = {}; self.configAxisWidth = {}
    self.configAxisStyle = {}; self.configAxisDashStyle = {}; self.configAxisColor = {}
    self.configAxisBox2 = {}; self.Layout_configAxis2 = {}
    self.configAxisLabel2 = {}; self.configAxisPosition = {};
    self.configAxisPositionValue = {}; self.configAxisPositionReset = {}
    self.configAxisBox3 = {}; self.Layout_configAxis3 = {}; self.configAxisLabel3 = {}
    self.configAxisBoundLabel = {}
    self.configAxisBoundLowerLabel = {}; self.configAxisBoundLower = {}
    self.configAxisBoundUpperLabel = {}; self.configAxisBoundUpper = {}
    if(not self.secondAxes):
      useAxes = ['bottom', 'top', 'left', 'right']
    else:
      useAxes = ['left2', 'right2']
    for axis in useAxes:
      self.configAxisBox[axis] = QWidgetMac()
      ###self.vLayout.addWidget(self.configAxisBox[axis])
      self.Layout_outerBox2.addWidget(self.configAxisBox[axis])
      self.Layout_configAxis[axis] = QtWidgets.QHBoxLayout(self.configAxisBox[axis])
      self.Layout_configAxis[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configAxis[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configAxis[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configAxisLabel[axis] = QPushButtonCheckable()
      self.configAxisLabel[axis].setText('ax ' + axis)
      self.configAxisLabel[axis].setToolTip('Show ' + axis + ' axis of plot')
      self.configAxisLabel[axis].setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisLabel[axis].setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis[axis].addWidget(self.configAxisLabel[axis])

      self.configAxisColor[axis] = QPushButtonColor()
      self.configAxisColor[axis].setToolTip('Color of ' + axis + ' axis')
      self.configAxisColor[axis].setAutoFillBackground(False)
      self.configAxisColor[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configAxisColor[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configAxisColor[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configAxis[axis].addWidget(self.configAxisColor[axis])
  
      self.configAxisWidthLabel[axis] = QtWidgets.QLabel('width')
      self.configAxisWidthLabel[axis].setMinimumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisWidthLabel[axis].setMaximumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis[axis].addWidget(self.configAxisWidthLabel[axis])
      # line width spin box
      self.configAxisWidth[axis] = QDoubleSpinClick()
      self.Layout_configAxis[axis].addWidget(self.configAxisWidth[axis])
      self.configAxisWidth[axis].setMinimum(0.0)
      self.configAxisWidth[axis].setMaximum(100.0)
      self.configAxisWidth[axis].setToolTip('Line width of ' + axis + ' axis')
      self.configAxisWidth[axis].setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisWidth[axis].setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))

      self.configAxisStyle[axis] = QComboBoxMac()
      self.configAxisStyle[axis].setToolTip('Line style of ' + axis + ' axis')
      self.configAxisStyle[axis].setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisStyle[axis].setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis[axis].addWidget(self.configAxisStyle[axis])

      self.configAxisDashStyle[axis] = QComboBoxMac()
      self.configAxisDashStyle[axis].setToolTip('Cap style of ' + axis + ' axis')
      self.advancedGraphicsSettings.append(self.configAxisDashStyle[axis])
      self.configAxisDashStyle[axis].setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisDashStyle[axis].setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis[axis].addWidget(self.configAxisDashStyle[axis])
      
      # second row
      self.configAxisBox3[axis] = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configAxisBox3[axis])
      ###self.vLayout.addWidget(self.configAxisBox3[axis])
      self.Layout_outerBox2.addWidget(self.configAxisBox3[axis])
      self.Layout_configAxis3[axis] = QtWidgets.QHBoxLayout(self.configAxisBox3[axis])
      self.Layout_configAxis3[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configAxis3[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configAxis3[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis3[axis].addWidget(spacer)

      self.configAxisBoundLabel[axis] = QPushButtonCheckable()
      self.configAxisBoundLabel[axis].setText('boundary')
      self.configAxisBoundLabel[axis].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundLabel[axis].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundLabel[axis].setToolTip('Limit ' + axis + ' axis to boundaries')
      self.Layout_configAxis3[axis].addWidget(self.configAxisBoundLabel[axis])
      
      self.configAxisBoundLowerLabel[axis] = QtWidgets.QLabel('lower')
      self.configAxisBoundLowerLabel[axis].setObjectName('advanced')
      self.configAxisBoundLowerLabel[axis].setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundLowerLabel[axis].setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundLowerLabel[axis].setToolTip('Lower boundary value of ' + axis + ' axis')
      self.Layout_configAxis3[axis].addWidget(self.configAxisBoundLowerLabel[axis])

      self.configAxisBoundLower[axis] = QLineEditClick()
      self.configAxisBoundLower[axis].setToolTip('Lower boundary value of ' + axis + ' axis')
      self.configAxisBoundLower[axis].setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundLower[axis].setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundLower[axis].setValidator(self.validFloat)
      self.Layout_configAxis3[axis].addWidget(self.configAxisBoundLower[axis])

      self.configAxisBoundUpperLabel[axis] = QtWidgets.QLabel('upper')
      self.configAxisBoundUpperLabel[axis].setObjectName('advanced')
      self.configAxisBoundUpperLabel[axis].setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundUpperLabel[axis].setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundUpperLabel[axis].setToolTip('Upper boundary of ' + axis + ' axis')
      self.Layout_configAxis3[axis].addWidget(self.configAxisBoundUpperLabel[axis])

      self.configAxisBoundUpper[axis] = QLineEditClick()
      self.configAxisBoundUpper[axis].setToolTip('Upper boundary value of ' + axis + ' axis')
      self.configAxisBoundUpper[axis].setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundUpper[axis].setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisBoundUpper[axis].setValidator(self.validFloat)
      self.Layout_configAxis3[axis].addWidget(self.configAxisBoundUpper[axis])

      # third row
      self.configAxisBox2[axis] = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configAxisBox2[axis])
      ###self.vLayout.addWidget(self.configAxisBox2[axis])
      self.Layout_outerBox2.addWidget(self.configAxisBox2[axis])
      self.Layout_configAxis2[axis] = QtWidgets.QHBoxLayout(self.configAxisBox2[axis])
      self.Layout_configAxis2[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configAxis2[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configAxis2[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis2[axis].addWidget(spacer)

      self.configAxisLabel2[axis] = QtWidgets.QLabel('position')
      self.configAxisLabel2[axis].setObjectName('advanced')
      self.configAxisLabel2[axis].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisLabel2[axis].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis2[axis].addWidget(self.configAxisLabel2[axis])

      self.configAxisPosition[axis] = QComboBoxMac()
      self.configAxisPosition[axis].setToolTip('Positioning of ' + axis + ' axis')
      self.configAxisPosition[axis].setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisPosition[axis].setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAxis2[axis].addWidget(self.configAxisPosition[axis])

      self.configAxisPositionValue[axis] = QLineEditClick()
      self.configAxisPositionValue[axis].setToolTip('Position value of ' + axis + ' axis')
      self.configAxisPositionValue[axis].setMaximumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisPositionValue[axis].setMinimumSize(QtCore.QSize(int(54 * SCALEFONT), int(BASE_SIZE)))
      self.configAxisPositionValue[axis].setValidator(self.validFloat)
      self.Layout_configAxis2[axis].addWidget(self.configAxisPositionValue[axis])

      self.configAxisPositionReset[axis] = QPushButtonMac('Reset')
      self.configAxisPositionReset[axis].setToolTip('Reset ' + axis + ' axis to default')
      self.configAxisPositionReset[axis].setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE - 2)))
      self.configAxisPositionReset[axis].setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE - 2)))
      self.configAxisPositionReset[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configAxis2[axis].addWidget(self.configAxisPositionReset[axis])
  
    # arrow config
    blah = HLine()
    ###self.vLayout.addWidget(blah)
    if(self.secondAxes):
      useAxes = ['y2']
    else:
      useAxes = ['x', 'y']

    self.outerBox3 = QWidgetMac()
    self.outerBox3.setObjectName(outTypes[outCount % 2])
    outCount += 1
    self.vLayout.addWidget(self.outerBox3)
    self.Layout_outerBox3 = QtWidgets.QVBoxLayout(self.outerBox3)
    self.Layout_outerBox3.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox3.setSpacing(2)
    self.Layout_outerBox3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.configArrowBox, self.Layout_configArrow = {}, {}
    self.configArrowBox2, self.Layout_configArrow2 = {}, {}
    self.configArrowLabel = {}
    self.configArrowFillColorLabel, self.configArrowLineColorLabel = {}, {}
    self.configArrowLineColor, self.configArrowFillColor = {}, {}
    self.configArrowLineWidth, self.configArrowLineWidthLabel = {}, {}
    self.configArrowHeadLengthLabel, self.configArrowHeadLength = {}, {}
    self.configArrowHeadWidthLabel, self.configArrowHeadWidth = {}, {}
    self.configArrowOverhangLabel, self.configArrowOverhang = {}, {}
    self.configArrowOffsetLabel, self.configArrowOffset = {}, {}
    self.configArrowLocationLabel, self.configArrowLocation = {}, {}
    self.configArrowZLabel, self.configArrowZ = {}, {}
    for axis in useAxes:
      self.configArrowBox[axis] = QWidgetMac()
      ###self.vLayout.addWidget(self.configArrowBox[axis])
      self.Layout_outerBox3.addWidget(self.configArrowBox[axis])
      self.Layout_configArrow[axis] = QtWidgets.QHBoxLayout(self.configArrowBox[axis])
      self.Layout_configArrow[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configArrow[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configArrow[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      self.configArrowLabel[axis] = QPushButtonCheckable()
      self.configArrowLabel[axis].setText('arrow ' + axis)
      self.configArrowLabel[axis].setToolTip('Show ' + axis + ' arrow tip')
      self.configArrowLabel[axis].setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLabel[axis].setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow[axis].addWidget(self.configArrowLabel[axis])

      self.configArrowFillColorLabel[axis] = QPushButtonCheckable()
      self.configArrowFillColorLabel[axis].setText('face')
      self.configArrowFillColorLabel[axis].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowFillColorLabel[axis].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowFillColorLabel[axis].setToolTip('Fill face of ' + axis + ' arrow tip')
      self.Layout_configArrow[axis].addWidget(self.configArrowFillColorLabel[axis])
      self.configArrowFillColor[axis] = QPushButtonColor()
      self.configArrowFillColor[axis].setToolTip('Fill color of ' + axis + ' arrow tip')
      self.configArrowFillColor[axis].setAutoFillBackground(False)
      self.configArrowFillColor[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configArrowFillColor[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configArrowFillColor[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configArrow[axis].addWidget(self.configArrowFillColor[axis])

      self.configArrowLineColorLabel[axis] = QPushButtonCheckable()
      self.configArrowLineColorLabel[axis].setText('edge')
      self.configArrowLineColorLabel[axis].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLineColorLabel[axis].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLineColorLabel[axis].setToolTip('Draw border of ' + axis + ' arrow tip')
      self.Layout_configArrow[axis].addWidget(self.configArrowLineColorLabel[axis])
      self.configArrowLineColor[axis] = QPushButtonColor()
      self.configArrowLineColor[axis].setToolTip('Line color of ' + axis + ' arrow tip')
      self.configArrowLineColor[axis].setAutoFillBackground(False)
      self.configArrowLineColor[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configArrowLineColor[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configArrowLineColor[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configArrow[axis].addWidget(self.configArrowLineColor[axis])
      
      self.configArrowLineWidthLabel[axis] = QtWidgets.QLabel('width')
      self.configArrowLineWidthLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLineWidthLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow[axis].addWidget(self.configArrowLineWidthLabel[axis])
      # line width spin box
      self.configArrowLineWidth[axis] = QDoubleSpinClick()
      self.Layout_configArrow[axis].addWidget(self.configArrowLineWidth[axis])
      self.configArrowLineWidth[axis].setMinimum(0.0)
      self.configArrowLineWidth[axis].setMaximum(100.0)
      self.configArrowLineWidth[axis].setToolTip('Edge width of ' + axis + ' arrow tip')
      self.configArrowLineWidth[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLineWidth[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      
      # selector for axis z
      self.configArrowZLabel[axis] = QtWidgets.QLabel('z')
      ###self.configArrowZLabel[axis].setObjectName('advanced')
      self.configArrowZLabel[axis].setMinimumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowZLabel[axis].setMaximumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow[axis].addWidget(self.configArrowZLabel[axis])
      self.configArrowZ[axis] = QComboBoxMac()
      self.configArrowZ[axis].setToolTip('z order of ' + axis + ' arrow tip')
      self.configArrowZ[axis].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowZ[axis].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow[axis].addWidget(self.configArrowZ[axis])

      # second row arrow config
      self.configArrowBox2[axis] = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configArrowBox2[axis])
      ###self.vLayout.addWidget(self.configArrowBox2[axis])
      self.Layout_outerBox3.addWidget(self.configArrowBox2[axis])
      self.Layout_configArrow2[axis] = QtWidgets.QHBoxLayout(self.configArrowBox2[axis])
      self.Layout_configArrow2[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configArrow2[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configArrow2[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(spacer)

      self.configArrowHeadLengthLabel[axis] = QtWidgets.QLabel('length')
      self.configArrowHeadLengthLabel[axis].setObjectName('advanced')
      self.configArrowHeadLengthLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowHeadLengthLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(self.configArrowHeadLengthLabel[axis])
      self.configArrowHeadLength[axis] = QLineEditClick()
      self.configArrowHeadLength[axis].setToolTip('Length of ' + axis + ' arrow tip (inches)')
      self.configArrowHeadLength[axis].setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowHeadLength[axis].setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowHeadLength[axis].setValidator(self.validFloat)
      self.Layout_configArrow2[axis].addWidget(self.configArrowHeadLength[axis])
      self.configArrowHeadWidthLabel[axis] = QtWidgets.QLabel('width')
      self.configArrowHeadWidthLabel[axis].setObjectName('advanced')
      self.configArrowHeadWidthLabel[axis].setMinimumSize(QtCore.QSize(int(28 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowHeadWidthLabel[axis].setMaximumSize(QtCore.QSize(int(28 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(self.configArrowHeadWidthLabel[axis])
      self.configArrowHeadWidth[axis] = QLineEditClick()
      self.configArrowHeadWidth[axis].setToolTip('Width of ' + axis + ' arrow tip (inches)')
      self.configArrowHeadWidth[axis].setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowHeadWidth[axis].setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowHeadWidth[axis].setValidator(self.validFloat)
      self.Layout_configArrow2[axis].addWidget(self.configArrowHeadWidth[axis])

      self.configArrowOverhangLabel[axis] = QtWidgets.QLabel('ind.')
      self.configArrowOverhangLabel[axis].setObjectName('advanced')
      self.configArrowOverhangLabel[axis].setMinimumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowOverhangLabel[axis].setMaximumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(self.configArrowOverhangLabel[axis])
      self.configArrowOverhang[axis] = QLineEditClick()
      self.configArrowOverhang[axis].setToolTip('Indentation of ' + axis + ' arrow tip')
      self.configArrowOverhang[axis].setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowOverhang[axis].setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowOverhang[axis].setValidator(self.validFloat)
      self.Layout_configArrow2[axis].addWidget(self.configArrowOverhang[axis])

      self.configArrowOffsetLabel[axis] = QtWidgets.QLabel('offset')
      self.configArrowOffsetLabel[axis].setObjectName('advanced')
      self.configArrowOffsetLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowOffsetLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(self.configArrowOffsetLabel[axis])
      self.configArrowOffset[axis] = QLineEditClick()
      self.configArrowOffset[axis].setToolTip('Offset of ' + axis + ' arrow tip')
      self.configArrowOffset[axis].setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowOffset[axis].setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowOffset[axis].setValidator(self.validFloat)
      self.Layout_configArrow2[axis].addWidget(self.configArrowOffset[axis])

      self.configArrowLocationLabel[axis] = QtWidgets.QLabel('locate')
      self.configArrowLocationLabel[axis].setObjectName('advanced')
      self.configArrowLocationLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLocationLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(self.configArrowLocationLabel[axis])
      self.configArrowLocation[axis] = QComboBoxMac()
      self.configArrowLocation[axis].setToolTip('Location of ' + axis + ' arrow tip')
      self.configArrowLocation[axis].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configArrowLocation[axis].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configArrow2[axis].addWidget(self.configArrowLocation[axis])

    # x ticks config
    self.formatOptions = ['default', 'float', 'scientific', 'mathtext', 'engineer']
    if(not self.secondAxes):
      blah = HLine()
      ###self.vLayout.addWidget(blah)

      self.outerBox4 = QWidgetMac()
      self.outerBox4.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox4)
      self.Layout_outerBox4 = QtWidgets.QVBoxLayout(self.outerBox4)
      self.Layout_outerBox4.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox4.setSpacing(2)
      self.Layout_outerBox4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configTickXBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configTickXBox)
      self.Layout_outerBox4.addWidget(self.configTickXBox)
      self.Layout_configTickX = QtWidgets.QHBoxLayout(self.configTickXBox)
      self.Layout_configTickX.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configTickXLabel = QPushButtonCheckable()
      self.configTickXLabel.setText('x ticks')
      self.configTickXLabel.setToolTip('Show x axis tick labels')
      self.configTickXLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX.addWidget(self.configTickXLabel)
      
      self.configTickXAuto = QPushButtonCheckable()
      self.configTickXAuto.setText('auto')
      self.configTickXAuto.setToolTip('Automatically set x axis ticks')
      self.configTickXAuto.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAuto.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX.addWidget(self.configTickXAuto)
          
      self.configTickXEntry = QLineEditClick()
      self.configTickXEntry.setToolTip('Values of x axis ticks')
      self.configTickXEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configTickXEntry.setMaximumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXEntry.setMinimumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX.addWidget(self.configTickXEntry)
  
      self.configTickUseData = QPushButtonMac()
      self.configTickUseData.setText('labels')
      self.configTickUseData.setToolTip('Use data labels as x axis ticks')
      self.configTickUseData.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configTickUseData.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX.addWidget(self.configTickUseData)
  
      # tick label formatting ctd.
      self.configTickXBox6 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configTickXBox6)
      ###self.vLayout.addWidget(self.configTickXBox6)
      self.Layout_outerBox4.addWidget(self.configTickXBox6)
      self.Layout_configTickX6 = QtWidgets.QHBoxLayout(self.configTickXBox6)
      self.Layout_configTickX6.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX6.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX6.addWidget(spacer)
      
      self.configMinorTickXLabel = QPushButtonCheckable()
      self.configMinorTickXLabel.setText('minor')
      self.configMinorTickXLabel.setToolTip('Show x axis minor ticks')
      self.configMinorTickXLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickXLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX6.addWidget(self.configMinorTickXLabel)
      self.configMinorTickX = QComboBoxMac()
      self.configMinorTickX.setToolTip('Number of x axis minor ticks')
      for entry in [str(i + 1) for i in range(9)]:
        self.configMinorTickX.addItem(str(entry))
      self.configMinorTickX.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickX.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX6.addWidget(self.configMinorTickX)

      self.configMinorTickXRelativeLengthLabel = QtWidgets.QLabel('rel length')
      self.configMinorTickXRelativeLengthLabel.setObjectName('advanced')
      self.configMinorTickXRelativeLengthLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickXRelativeLengthLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX6.addWidget(self.configMinorTickXRelativeLengthLabel)
      self.configMinorTickXRelativeLength = QLineEditClick()
      self.configMinorTickXRelativeLength.setToolTip('Relative length of x axis minor ticks')
      self.configMinorTickXRelativeLength.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickXRelativeLength.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickXRelativeLength.setValidator(self.validFloat)
      self.Layout_configTickX6.addWidget(self.configMinorTickXRelativeLength)

      # font formatting
      self.configTickXBox3 = QWidgetMac()
      ###self.vLayout.addWidget(self.configTickXBox3)
      self.Layout_outerBox4.addWidget(self.configTickXBox3)
      self.Layout_configTickX3 = QtWidgets.QHBoxLayout(self.configTickXBox3)
      self.Layout_configTickX3.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX3.addWidget(spacer)
      
      self.configTickXSizeLabel = QtWidgets.QLabel('font')
      self.configTickXSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX3.addWidget(self.configTickXSizeLabel)
      self.configTickXColorButton = QPushButtonColor()
      self.configTickXColorButton.setToolTip('Color of x axis ticks')
      self.configTickXColorButton.setAutoFillBackground(False)
      self.configTickXColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configTickXColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configTickXColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configTickX3.addWidget(self.configTickXColorButton)
  
      self.configTickXSize = QDoubleSpinClick()
      self.Layout_configTickX3.addWidget(self.configTickXSize)
      self.configTickXSize.setMinimum(0.0)
      self.configTickXSize.setMaximum(100.0)
      self.configTickXSize.setToolTip('Font size of x axis ticks')
      self.configTickXSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))

      self.configTickXBold = QPushButtonCheckable()
      self.configTickXBold.setText('B')
      self.configTickXBold.setToolTip('Font weight of x axis ticks')
      self.configTickXBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configTickXBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configTickXBold.font()
      useFont.setBold(True)
      self.configTickXBold.setFont(useFont)
      self.Layout_configTickX3.addWidget(self.configTickXBold)
        
      self.configTickXItalic = QPushButtonCheckable()
      self.configTickXItalic.setText('I')
      self.configTickXItalic.setToolTip('Font style of x axis ticks')
      self.configTickXItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configTickXItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configTickXItalic.font()
      useFont.setItalic(True)
      self.configTickXItalic.setFont(useFont)
      self.Layout_configTickX3.addWidget(self.configTickXItalic)
  
      self.configTickXFont = FontoBox()
      self.configTickXFont.setToolTip('Font of x axis ticks')
      self.configTickXFont.addItems(items=self.parent.fontNames)
      self.configTickXFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX3.addWidget(self.configTickXFont)
  
      self.configTickXBox2 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configTickXBox2)
      ###self.vLayout.addWidget(self.configTickXBox2)
      self.Layout_outerBox4.addWidget(self.configTickXBox2)
      self.Layout_configTickX2 = QtWidgets.QHBoxLayout(self.configTickXBox2)
      self.Layout_configTickX2.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX2.addWidget(spacer)
      
      self.configTickXAngleLabel = QtWidgets.QLabel('angle')
      self.configTickXAngleLabel.setObjectName('advanced')
      self.configTickXAngleLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAngleLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX2.addWidget(self.configTickXAngleLabel)

      self.configTickXAngle = QDoubleSpinClick()
      self.Layout_configTickX2.addWidget(self.configTickXAngle)
      self.configTickXAngle.setMinimum(0.0)
      self.configTickXAngle.setMaximum(360.0)
      self.configTickXAngle.setToolTip('Angle of x axis ticks')
      self.configTickXAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAngle.setSingleStep(10)
      self.configTickXAngle.setWrapping(True)
      self.configTickXAngle.setAdaptive(False)
      
      self.configTickXAlignmentLabel = QtWidgets.QLabel('align')
      self.configTickXAlignmentLabel.setObjectName('advanced')
      self.configTickXAlignmentLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAlignmentLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX2.addWidget(self.configTickXAlignmentLabel)
  
      self.configTickXAlignment = QComboBoxMac()
      self.configTickXAlignment.setToolTip('Horizontal alignment of x axis ticks')
      self.configTickXAlignment.addItems(self.alignHorizontal)
      self.configTickXAlignment.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAlignment.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX2.addWidget(self.configTickXAlignment)
  
      self.configTickXAlignmentVertical = QComboBoxMac()
      self.configTickXAlignmentVertical.setToolTip('Vertical alignment of x axis ticks')
      self.configTickXAlignmentVertical.addItems(self.alignVertical)
      self.configTickXAlignmentVertical.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXAlignmentVertical.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX2.addWidget(self.configTickXAlignmentVertical)

      self.configTickXLinespacingLabel = QtWidgets.QLabel('linespacing')
      self.configTickXLinespacingLabel.setObjectName('advanced')
      self.configTickXLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX2.addWidget(self.configTickXLinespacingLabel)
  
      self.configTickXLinespacing = QLineEditClick()
      self.configTickXLinespacing.setToolTip('Linespacing of x axis ticks')
      self.configTickXLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXLinespacing.setValidator(self.validFloat)
      self.Layout_configTickX2.addWidget(self.configTickXLinespacing)
  
      # x tick config continued
      self.configTickXBox25 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configTickXBox25)
      ###self.vLayout.addWidget(self.configTickXBox25)
      self.Layout_outerBox4.addWidget(self.configTickXBox25)
      self.Layout_configTickX25 = QtWidgets.QHBoxLayout(self.configTickXBox25)
      self.Layout_configTickX25.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX25.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX25.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX25.addWidget(spacer)

      self.configTickXPadLabel = QtWidgets.QLabel('pad')
      self.configTickXPadLabel.setObjectName('advanced')
      self.configTickXPadLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPadLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX25.addWidget(self.configTickXPadLabel)
  
      self.configTickXPad = QLineEditClick()
      self.configTickXPad.setToolTip('Vertical padding of x axis ticks')
      self.configTickXPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPad.setValidator(self.validFloat)
      self.Layout_configTickX25.addWidget(self.configTickXPad)

      self.configTickXPad2Label = QtWidgets.QLabel('pad2')
      self.configTickXPad2Label.setObjectName('advanced')
      self.configTickXPad2Label.setMaximumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPad2Label.setMinimumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX25.addWidget(self.configTickXPad2Label)
  
      self.configTickXPad2 = QLineEditClick()
      self.configTickXPad2.setToolTip('Horizontal padding of x axis ticks')
      self.configTickXPad2.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPad2.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPad2.setValidator(self.validFloat)
      self.Layout_configTickX25.addWidget(self.configTickXPad2)

      # tick label formatting
      self.configTickXBox4 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configTickXBox4)
      ###self.vLayout.addWidget(self.configTickXBox4)
      self.Layout_outerBox4.addWidget(self.configTickXBox4)
      self.Layout_configTickX4 = QtWidgets.QHBoxLayout(self.configTickXBox4)
      self.Layout_configTickX4.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(spacer)
      
      self.configTickXFormatLabel = QtWidgets.QLabel('fmt')
      self.configTickXFormatLabel.setObjectName('advanced')
      self.configTickXFormatLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(self.configTickXFormatLabel)
  
      self.configTickXFormat = QComboBoxMac()
      self.configTickXFormat.setToolTip('Format of x axis ticks')
      self.configTickXFormat.addItems(self.formatOptions)
      self.configTickXFormat.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormat.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(self.configTickXFormat)
  
      self.configTickXFormatPrecisionLabel = QtWidgets.QLabel('precision')
      self.configTickXFormatPrecisionLabel.setObjectName('advanced')
      self.configTickXFormatPrecisionLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatPrecisionLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(self.configTickXFormatPrecisionLabel)
  
      self.configTickXFormatPrecision = QLineEditClick()
      self.configTickXFormatPrecision.setToolTip('Number precision in x axis ticks')
      self.configTickXFormatPrecision.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatPrecision.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatPrecision.setValidator(self.validInt)
      self.Layout_configTickX4.addWidget(self.configTickXFormatPrecision)

      self.configTickXFormatTrailZero = QPushButtonCheckable()
      self.configTickXFormatTrailZero.setText('trail 0s?')
      self.configTickXFormatTrailZero.setToolTip('Remove trailing zeros in x axis ticks')
      self.configTickXFormatTrailZero.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatTrailZero.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(self.configTickXFormatTrailZero)

      self.configTickXFormatSeparator = QPushButtonCheckable()
      self.configTickXFormatSeparator.setText('separate 1000s?')
      self.configTickXFormatSeparator.setToolTip('Separate 1000\'s in x axis ticks')
      self.configTickXFormatSeparator.setMaximumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatSeparator.setMinimumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(self.configTickXFormatSeparator)

      self.configTickXFormatComma = QPushButtonCheckable()
      self.configTickXFormatComma.setText('comma?')
      self.configTickXFormatComma.setToolTip('Use comma to denote decimal numbers in x axis ticks?')
      self.configTickXFormatComma.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXFormatComma.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX4.addWidget(self.configTickXFormatComma)
      self.Layout_configTickX4.addStretch()

      # tick label formatting ctd.
      self.configTickXBox5 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configTickXBox5)
      ###self.vLayout.addWidget(self.configTickXBox5)
      self.Layout_outerBox4.addWidget(self.configTickXBox5)
      self.Layout_configTickX5 = QtWidgets.QHBoxLayout(self.configTickXBox5)
      self.Layout_configTickX5.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickX5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickX5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX5.addWidget(spacer)
      
      self.configTickXPrefixLabel = QtWidgets.QLabel('prefix')
      self.configTickXPrefixLabel.setObjectName('advanced')
      self.configTickXPrefixLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPrefixLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX5.addWidget(self.configTickXPrefixLabel)
      self.configTickXPrefix = QLineEditClick()
      self.configTickXPrefix.setToolTip('Constant prefix in x axis ticks')
      self.configTickXPrefix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configTickXPrefix.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPrefix.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX5.addWidget(self.configTickXPrefix)

      self.configTickXPostfixLabel = QtWidgets.QLabel('postfix')
      self.configTickXPostfixLabel.setObjectName('advanced')
      self.configTickXPostfixLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPostfixLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX5.addWidget(self.configTickXPostfixLabel)
      self.configTickXPostfix = QLineEditClick()
      self.configTickXPostfix.setToolTip('Constant postfix in x axis ticks')
      self.configTickXPostfix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configTickXPostfix.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configTickXPostfix.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickX5.addWidget(self.configTickXPostfix)

    # the inner situation
    if(not self.secondAxes):
      self.outerBox4b = QWidgetMac()
      self.outerBox4b.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox4b)
      self.Layout_outerBox4b = QtWidgets.QVBoxLayout(self.outerBox4b)
      self.Layout_outerBox4b.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox4b.setSpacing(2)
      self.Layout_outerBox4b.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configInnerTickXBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configInnerTickXBox)
      self.Layout_outerBox4b.addWidget(self.configInnerTickXBox)
      self.Layout_configInnerTickX = QtWidgets.QHBoxLayout(self.configInnerTickXBox)
      self.Layout_configInnerTickX.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerTickX.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerTickX.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configInnerLabel = QPushButtonCheckable()
      self.configInnerLabel.setText('split_x')
      self.configInnerLabel.setToolTip('Show split x axis tick labels')
      self.configInnerLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerTickX.addWidget(self.configInnerLabel)

      self.configInnerTickXAuto = QPushButtonCheckable()
      self.configInnerTickXAuto.setText('auto')
      self.configInnerTickXAuto.setToolTip('Automatically set split x axis ticks')
      self.configInnerTickXAuto.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXAuto.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerTickX.addWidget(self.configInnerTickXAuto)
          
      self.configInnerTickXEntry = QLineEditClick()
      self.configInnerTickXEntry.setToolTip('Values of split x axis ticks')
      self.configInnerTickXEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configInnerTickXEntry.setMaximumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXEntry.setMinimumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerTickX.addWidget(self.configInnerTickXEntry)
  
      self.configInnerTickUseData = QPushButtonMac()
      self.configInnerTickUseData.setText('labels')
      self.configInnerTickUseData.setToolTip('Use data labels as split x axis ticks')
      self.configInnerTickUseData.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickUseData.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerTickX.addWidget(self.configInnerTickUseData)

      # 4th row
      self.configInnerMinorTickBox = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configInnerMinorTickBox)
      ###self.vLayout.addWidget(self.configInnerMinorTickBox)
      self.Layout_outerBox4b.addWidget(self.configInnerMinorTickBox)
      self.Layout_configInnerMinorTickBox = QtWidgets.QHBoxLayout(self.configInnerMinorTickBox)
      self.Layout_configInnerMinorTickBox.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerMinorTickBox.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerMinorTickBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerMinorTickBox.addWidget(spacer)

      self.configInnerMinorTickLabel = QPushButtonCheckable()
      self.configInnerMinorTickLabel.setText('minor')
      self.configInnerMinorTickLabel.setToolTip('Show split x axis minor ticks')
      self.configInnerMinorTickLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerMinorTickLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerMinorTickBox.addWidget(self.configInnerMinorTickLabel)
      self.configInnerMinorTickX = QComboBoxMac()
      self.configInnerMinorTickX.setToolTip('Number of split x axis minor ticks')
      for entry in [str(i + 1) for i in range(9)]:
        self.configInnerMinorTickX.addItem(str(entry))
      self.configInnerMinorTickX.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerMinorTickX.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerMinorTickBox.addWidget(self.configInnerMinorTickX)

      '''
      self.configInnerMinorTickRelativeLengthLabel = QtWidgets.QLabel('rel length')
      self.configInnerMinorTickRelativeLengthLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerMinorTickRelativeLengthLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerMinorTickBox.addWidget(self.configInnerMinorTickRelativeLengthLabel)
      self.configInnerMinorTickRelativeLength = QLineEditClick()
      self.configInnerMinorTickXRelativeLength.setToolTip('Relative length of x axis minor ticks')
      self.configInnerMinorTickRelativeLength.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerMinorTickRelativeLength.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerMinorTickRelativeLength.setValidator(self.validFloat)
      self.Layout_configInnerMinorTickBox.addWidget(self.configInnerMinorTickRelativeLength)
      '''

      # second row
      self.configInnerBox2 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configInnerBox2)
      ###self.vLayout.addWidget(self.configInnerBox2)
      self.Layout_outerBox4b.addWidget(self.configInnerBox2)
      self.Layout_configInnerBox2 = QtWidgets.QHBoxLayout(self.configInnerBox2)
      self.Layout_configInnerBox2.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerBox2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerBox2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(spacer)
      
      self.configInnerTickXFormatLabel = QtWidgets.QLabel('fmt')
      self.configInnerTickXFormatLabel.setObjectName('advanced')
      self.configInnerTickXFormatLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormatLabel)
  
      self.configInnerTickXFormat = QComboBoxMac()
      self.configInnerTickXFormat.setToolTip('Format of split x axis ticks')
      self.configInnerTickXFormat.addItems(self.formatOptions)
      self.configInnerTickXFormat.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormat.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormat)
  
      self.configInnerTickXFormatPrecisionLabel = QtWidgets.QLabel('precision')
      self.configInnerTickXFormatPrecisionLabel.setObjectName('advanced')
      self.configInnerTickXFormatPrecisionLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatPrecisionLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormatPrecisionLabel)
  
      self.configInnerTickXFormatPrecision = QLineEditClick()
      self.configInnerTickXFormatPrecision.setToolTip('Number precision in split x axis ticks')
      self.configInnerTickXFormatPrecision.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatPrecision.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatPrecision.setValidator(self.validInt)
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormatPrecision)

      self.configInnerTickXFormatTrailZero = QPushButtonCheckable()
      self.configInnerTickXFormatTrailZero.setToolTip('Remove trailing zeros in split x axis ticks')
      self.configInnerTickXFormatTrailZero.setText('trail 0s?')
      self.configInnerTickXFormatTrailZero.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatTrailZero.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormatTrailZero)
  
      self.configInnerTickXFormatSeparator = QPushButtonCheckable()
      self.configInnerTickXFormatSeparator.setText('separate 1000s?')
      self.configInnerTickXFormatSeparator.setToolTip('Separate 1000\'s in split x axis ticks')
      self.configInnerTickXFormatSeparator.setMaximumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatSeparator.setMinimumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormatSeparator)

      self.configInnerTickXFormatComma = QPushButtonCheckable()
      self.configInnerTickXFormatComma.setText('comma?')
      self.configInnerTickXFormatComma.setToolTip('Use comma to denote decimal numbers in split x axis ticks?')
      self.configInnerTickXFormatComma.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXFormatComma.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox2.addWidget(self.configInnerTickXFormatComma)
      self.Layout_configInnerBox2.addStretch()
      
      # third row
      self.configInnerBox3 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configInnerBox3)
      ###self.vLayout.addWidget(self.configInnerBox3)
      self.Layout_outerBox4b.addWidget(self.configInnerBox3)
      self.Layout_configInnerBox3 = QtWidgets.QHBoxLayout(self.configInnerBox3)
      self.Layout_configInnerBox3.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerBox3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerBox3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox3.addWidget(spacer)
      
      self.configInnerTickXPrefixLabel = QtWidgets.QLabel('prefix')
      self.configInnerTickXPrefixLabel.setObjectName('advanced')
      self.configInnerTickXPrefixLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXPrefixLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox3.addWidget(self.configInnerTickXPrefixLabel)
      self.configInnerTickXPrefix = QLineEditClick()
      self.configInnerTickXPrefix.setToolTip('Constant prefix in split x axis ticks')
      self.configInnerTickXPrefix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configInnerTickXPrefix.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXPrefix.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox3.addWidget(self.configInnerTickXPrefix)

      self.configInnerTickXPostfixLabel = QtWidgets.QLabel('postfix')
      self.configInnerTickXPostfixLabel.setObjectName('advanced')
      self.configInnerTickXPostfixLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXPostfixLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox3.addWidget(self.configInnerTickXPostfixLabel)
      self.configInnerTickXPostfix = QLineEditClick()
      self.configInnerTickXPostfix.setToolTip('Constant postfix in split x axis ticks')
      self.configInnerTickXPostfix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configInnerTickXPostfix.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickXPostfix.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox3.addWidget(self.configInnerTickXPostfix)      

      # fourth row
      self.configInnerBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configInnerBox)
      self.Layout_outerBox4b.addWidget(self.configInnerBox)
      self.Layout_configInnerBox = QtWidgets.QHBoxLayout(self.configInnerBox)
      self.Layout_configInnerBox.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerBox.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerBox.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox.addWidget(spacer)
  
      self.xSplitFractionLabel = QtWidgets.QLabel('fraction')
      self.xSplitFractionLabel.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.xSplitFractionLabel.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox.addWidget(self.xSplitFractionLabel)
      self.xSplitFraction = QLineEditClick()
      self.xSplitFraction.setToolTip('Size ratio of x and split x axes')
      self.xSplitFraction.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.xSplitFraction.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.xSplitFraction.setValidator(self.validFloat)
      self.Layout_configInnerBox.addWidget(self.xSplitFraction)
          
      self.xSplitPadLabel = QtWidgets.QLabel('pad')
      self.xSplitPadLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.xSplitPadLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox.addWidget(self.xSplitPadLabel)
      self.xSplitPad = QLineEditClick()
      self.xSplitPad.setToolTip('Padding between x and split x axes')
      self.xSplitPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.xSplitPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.xSplitPad.setValidator(self.validFloat)
      self.Layout_configInnerBox.addWidget(self.xSplitPad)

      self.configInnerAxesLabel = QPushButtonCheckable()
      self.configInnerAxesLabel.setText('axes')
      self.configInnerAxesLabel.setToolTip('Show inner y axes')
      self.configInnerAxesLabel.setMaximumSize(QtCore.QSize(int(42 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerAxesLabel.setMinimumSize(QtCore.QSize(int(42 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox.addWidget(self.configInnerAxesLabel)

      self.configInnerTickLabel = QPushButtonCheckable()
      self.configInnerTickLabel.setText('ticks')
      self.configInnerTickLabel.setToolTip('Show inner y axis ticks')
      self.configInnerTickLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerTickLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox.addWidget(self.configInnerTickLabel)
      self.Layout_configInnerBox.addStretch()

      # fifth row
      self.configInnerBox5 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configInnerBox5)
      ###self.vLayout.addWidget(self.configInnerBox5)
      self.Layout_outerBox4b.addWidget(self.configInnerBox5)
      self.Layout_configInnerBox5 = QtWidgets.QHBoxLayout(self.configInnerBox5)
      self.Layout_configInnerBox5.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerBox5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerBox5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox5.addWidget(spacer)

      self.configInnerDividerLine = QPushButtonCheckable()
      self.configInnerDividerLine.setText('divider')
      self.configInnerDividerLine.setToolTip('Show split x axis divider lines')
      self.configInnerDividerLine.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLine.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLine)
      
      self.configInnerDividerLineColor = QPushButtonColor()
      self.configInnerDividerLineColor.setToolTip('Line color of split x axis divider lines')
      self.configInnerDividerLineColor.setAutoFillBackground(False)
      self.configInnerDividerLineColor.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configInnerDividerLineColor.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configInnerDividerLineColor.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLineColor)

      self.configInnerDividerLineLengthLabel = QtWidgets.QLabel('length')
      self.configInnerDividerLineLengthLabel.setObjectName('advanced')
      self.configInnerDividerLineLengthLabel.setMinimumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineLengthLabel.setMaximumSize(QtCore.QSize(int(38 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLineLengthLabel)
      self.configInnerDividerLineLength = QDoubleSpinClick()
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLineLength)
      self.configInnerDividerLineLength.setMinimum(0.0)
      self.configInnerDividerLineLength.setMaximum(2.0)
      self.configInnerDividerLineLength.setSingleStep(.02)
      self.configInnerDividerLineLength.setAdaptive(False)
      self.configInnerDividerLineLength.setToolTip('Length of split x axis divider lines')
      self.configInnerDividerLineLength.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineLength.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.configInnerDividerLineWidthLabel = QtWidgets.QLabel('width')
      self.configInnerDividerLineWidthLabel.setObjectName('advanced')
      self.configInnerDividerLineWidthLabel.setMinimumSize(QtCore.QSize(int(42 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineWidthLabel.setMaximumSize(QtCore.QSize(int(42 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLineWidthLabel)
      self.configInnerDividerLineWidth = QDoubleSpinClick()
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLineWidth)
      self.configInnerDividerLineWidth.setMinimum(0.0)
      self.configInnerDividerLineWidth.setMaximum(100.0)
      self.configInnerDividerLineWidth.setToolTip('Width of split x axis divider lines')
      self.configInnerDividerLineWidth.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineWidth.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
  
      self.configInnerDividerLineDashStyle = QComboBoxMac()
      self.configInnerDividerLineDashStyle.setToolTip('Cap style of split x axis divider lines')
      self.configInnerDividerLineDashStyle.setMinimumSize(QtCore.QSize(int(68 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineDashStyle.setMaximumSize(QtCore.QSize(int(68 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox5.addWidget(self.configInnerDividerLineDashStyle)

      # sixth row
      self.configInnerBox6 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configInnerBox6)
      ###self.vLayout.addWidget(self.configInnerBox6)
      self.Layout_outerBox4b.addWidget(self.configInnerBox6)
      self.Layout_configInnerBox6 = QtWidgets.QHBoxLayout(self.configInnerBox6)
      self.Layout_configInnerBox6.setContentsMargins(0, 0, 0, 0)
      self.Layout_configInnerBox6.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configInnerBox6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox6.addWidget(spacer)

      self.configInnerDividerLineAngleLabel = QtWidgets.QLabel('angle')
      self.configInnerDividerLineAngleLabel.setObjectName('advanced')
      self.configInnerDividerLineAngleLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineAngleLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox6.addWidget(self.configInnerDividerLineAngleLabel)
      self.configInnerDividerLineAngle = QDoubleSpinClick()
      self.Layout_configInnerBox6.addWidget(self.configInnerDividerLineAngle)
      self.configInnerDividerLineAngle.setMinimum(0.0)
      self.configInnerDividerLineAngle.setMaximum(360.0)
      self.configInnerDividerLineAngle.setToolTip('Angle of split x axis divider lines')
      self.configInnerDividerLineAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineAngle.setSingleStep(10)
      self.configInnerDividerLineAngle.setWrapping(True)
      self.configInnerDividerLineAngle.setAdaptive(False)

      self.configInnerDividerLineLocationLabel = QtWidgets.QLabel('location')
      self.configInnerDividerLineLocationLabel.setObjectName('advanced')
      self.configInnerDividerLineLocationLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineLocationLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox6.addWidget(self.configInnerDividerLineLocationLabel)
      self.configInnerDividerLineLocation = QComboBoxMac()
      self.configInnerDividerLineLocation.setToolTip('Location of split x axis divider lines')
      self.configInnerDividerLineLocation.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.configInnerDividerLineLocation.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configInnerBox6.addWidget(self.configInnerDividerLineLocation)

      if(not self.parent.plotArea.splitShow):
        self.configInnerBox.hide()
        self.configInnerTickXBox.hide()
        self.configInnerMinorTickBox.hide()
        self.outerBox4b.hide()
        
      # slaved x axis controls
      self.outerBox4c = QWidgetMac()
      self.outerBox4c.setObjectName(outTypes[outCount % 2])
      ###self.advancedGraphicsSettings.append(self.outerBox4c)
      outCount += 1
      self.vLayout.addWidget(self.outerBox4c)
      self.Layout_outerBox4c = QtWidgets.QVBoxLayout(self.outerBox4c)
      self.Layout_outerBox4c.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox4c.setSpacing(2)
      self.Layout_outerBox4c.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.slavedXBox = QWidgetMac()
      ###self.advancedGraphicsSettings.append(self.slavedXBox)
      ###self.vLayout.addWidget(self.slavedXBox)
      self.Layout_outerBox4c.addWidget(self.slavedXBox)
      self.Layout_slavedX = QtWidgets.QHBoxLayout(self.slavedXBox)
      self.Layout_slavedX.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedX.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedX.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.slavedXLabel = QPushButtonCheckable()
      self.slavedXLabel.setText('transformed')
      self.slavedXLabel.setToolTip('Show transformed x values on upper axis')
      self.slavedXLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX.addWidget(self.slavedXLabel)

      self.slavedTickXAuto = QPushButtonCheckable()
      self.slavedTickXAuto.setText('auto')
      self.slavedTickXAuto.setToolTip('Automatically set ticks for transformed x axis')
      self.slavedTickXAuto.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAuto.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX.addWidget(self.slavedTickXAuto)
          
      self.slavedTickXEntry = QLineEditClick()
      self.slavedTickXEntry.setToolTip('Values of transformed x axis ticks')
      self.slavedTickXEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.slavedTickXEntry.setMaximumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXEntry.setMinimumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX.addWidget(self.slavedTickXEntry)
  
      self.slavedXBox15 = QWidgetMac()
      ###self.advancedGraphicsSettings.append(self.slavedXBox15)
      ###self.vLayout.addWidget(self.slavedXBox15)
      self.Layout_outerBox4c.addWidget(self.slavedXBox15)
      self.Layout_slavedX15 = QtWidgets.QHBoxLayout(self.slavedXBox15)
      self.Layout_slavedX15.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedX15.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedX15.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.toggleItemsTransformed.append(self.slavedXBox15)

      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX15.addWidget(spacer)

      self.slavedXTransformLabel = QtWidgets.QLabel('transform')
      ###self.slavedXTransformLabel.setObjectName('advanced')
      self.slavedXTransformLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXTransformLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX15.addWidget(self.slavedXTransformLabel)
      
      self.slavedXReset = QPushButtonMac()
      self.slavedXReset.setText('reset')
      self.slavedXReset.setToolTip('Reset formula for x axis transformation')
      self.slavedXReset.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXReset.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX15.addWidget(self.slavedXReset)

      self.slavedXEntry = QLineEditClick()
      self.slavedXEntry.setToolTip('Formula for x axis transformation')
      self.slavedXEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.slavedXEntry.setMaximumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXEntry.setMinimumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX15.addWidget(self.slavedXEntry)
      self.slavedXEntry.setEnabled(False)

      # second row controls slaved x
      self.slavedXBox2 = QWidgetMac()
      ###self.advancedGraphicsSettings.append(self.slavedXBox2)
      ###self.vLayout.addWidget(self.slavedXBox2)
      self.Layout_outerBox4c.addWidget(self.slavedXBox2)
      self.Layout_slavedX2 = QtWidgets.QHBoxLayout(self.slavedXBox2)
      self.Layout_slavedX2.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedX2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedX2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX2.addWidget(spacer)

      self.slavedXOperationsLabel = QtWidgets.QLabel('operations')
      ###self.slavedXOperationsLabel.setObjectName('advanced')
      self.slavedXOperationsLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXOperationsLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX2.addWidget(self.slavedXOperationsLabel)

      self.toggleItemsTransformed.append(self.slavedXBox2)

      self.operationsContainer = QWidgetMac(self)
      self.Layout_slavedX2.addWidget(self.operationsContainer)
      self.Layout_operationsContainer = QtWidgets.QHBoxLayout(self.operationsContainer)
      self.Layout_operationsContainer.setContentsMargins(0, 0, 0, 0)
      self.Layout_operationsContainer.setSpacing(0)

      self.slavedXPlus = QPushButtonMac()
      self.slavedXPlus.setText('+')
      self.slavedXPlus.setToolTip('Transform x axis by adding number')
      self.slavedXPlus.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPlus.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_operationsContainer.addWidget(self.slavedXPlus)

      self.slavedXMinus = QPushButtonMac()
      self.slavedXMinus.setText('-')
      self.slavedXMinus.setToolTip('Transform x axis by substracting number')
      self.slavedXMinus.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXMinus.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_operationsContainer.addWidget(self.slavedXMinus)

      self.slavedXTimes = QPushButtonMac()
      self.slavedXTimes.setText('*')
      self.slavedXTimes.setToolTip('Transform x axis by multiplying by number')
      self.slavedXTimes.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXTimes.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_operationsContainer.addWidget(self.slavedXTimes)

      self.slavedXDivide = QPushButtonMac()
      self.slavedXDivide.setText('/')
      self.slavedXDivide.setToolTip('Transform x axis by dividing by number')
      self.slavedXDivide.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXDivide.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_operationsContainer.addWidget(self.slavedXDivide)

      self.slavedXPower = QPushButtonMac()
      self.slavedXPower.setText('**')
      self.slavedXPower.setToolTip('Transform x axis by taking value to the power of number')
      self.slavedXPower.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPower.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_operationsContainer.addWidget(self.slavedXPower)

      self.slavedXNumber = QLineEditClick()
      self.slavedXNumber.setToolTip('Number for x axis transformation')
      self.slavedXNumber.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight)
      self.slavedXNumber.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXNumber.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXNumber.setValidator(self.validFloat)
      self.Layout_operationsContainer.addWidget(self.slavedXNumber)
      
      self.slavedXExponential = QPushButtonMac()
      self.slavedXExponential.setText('e')
      self.slavedXExponential.setToolTip('Transform x axis by calculating exponential of x')
      self.slavedXExponential.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXExponential.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX2.addWidget(self.slavedXExponential)

      self.slavedXNaturalLog = QPushButtonMac()
      self.slavedXNaturalLog.setText('ln')
      self.slavedXNaturalLog.setToolTip('Transform x axis by calculating natural logarithm of x')
      self.slavedXNaturalLog.setMaximumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXNaturalLog.setMinimumSize(QtCore.QSize(int(20 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX2.addWidget(self.slavedXNaturalLog)
      self.Layout_slavedX2.addStretch()

      # tick label formatting ctd.
      self.slavedTickXBox6 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.slavedTickXBox6)
      ###self.vLayout.addWidget(self.slavedTickXBox6)
      self.Layout_outerBox4c.addWidget(self.slavedTickXBox6)
      self.Layout_slavedTickX6 = QtWidgets.QHBoxLayout(self.slavedTickXBox6)
      self.Layout_slavedTickX6.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedTickX6.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedTickX6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.toggleItemsTransformed.append(self.slavedTickXBox6)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX6.addWidget(spacer)
      
      self.slavedMinorTickXLabel = QPushButtonCheckable()
      self.slavedMinorTickXLabel.setText('minor')
      self.slavedMinorTickXLabel.setToolTip('Show transformed x axis minor ticks')
      self.slavedMinorTickXLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.slavedMinorTickXLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX6.addWidget(self.slavedMinorTickXLabel)
      self.slavedMinorTickX = QComboBoxMac()
      self.slavedMinorTickX.setToolTip('Number of transformed x axis minor ticks')
      for entry in [str(i + 1) for i in range(9)]:
        self.slavedMinorTickX.addItem(str(entry))
      self.slavedMinorTickX.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.slavedMinorTickX.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX6.addWidget(self.slavedMinorTickX)

      self.slavedMinorTickXRelativeLengthLabel = QtWidgets.QLabel('rel length')
      self.slavedMinorTickXRelativeLengthLabel.setObjectName('advanced')
      self.slavedMinorTickXRelativeLengthLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.slavedMinorTickXRelativeLengthLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX6.addWidget(self.slavedMinorTickXRelativeLengthLabel)
      self.slavedMinorTickXRelativeLength = QLineEditClick()
      self.slavedMinorTickXRelativeLength.setToolTip('Relative length of transformed x axis minor ticks')
      self.slavedMinorTickXRelativeLength.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedMinorTickXRelativeLength.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedMinorTickXRelativeLength.setValidator(self.validFloat)
      self.Layout_slavedTickX6.addWidget(self.slavedMinorTickXRelativeLength)

      # font formatting
      self.slavedTickXBox35 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.slavedTickXBox35)
      ###self.vLayout.addWidget(self.slavedTickXBox35)
      self.Layout_outerBox4c.addWidget(self.slavedTickXBox35)
      self.Layout_slavedTickX35 = QtWidgets.QHBoxLayout(self.slavedTickXBox35)
      self.Layout_slavedTickX35.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedTickX35.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedTickX35.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      self.toggleItemsTransformed.append(self.slavedTickXBox35)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX35.addWidget(spacer)
      
      self.slavedTickXSizeLabel = QtWidgets.QLabel('font')
      self.slavedTickXSizeLabel.setObjectName('advanced')
      self.slavedTickXSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX35.addWidget(self.slavedTickXSizeLabel)
      self.slavedTickXColorButton = QPushButtonColor()
      self.slavedTickXColorButton.setToolTip('Color of transformed x axis ticks')
      self.slavedTickXColorButton.setAutoFillBackground(False)
      self.slavedTickXColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedTickXColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedTickXColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_slavedTickX35.addWidget(self.slavedTickXColorButton)
  
      self.slavedTickXSize = QDoubleSpinClick()
      self.Layout_slavedTickX35.addWidget(self.slavedTickXSize)
      self.slavedTickXSize.setMinimum(0.0)
      self.slavedTickXSize.setMaximum(100.0)
      self.slavedTickXSize.setToolTip('Font size of transformed x axis ticks')
      self.slavedTickXSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))

      self.slavedTickXBold = QPushButtonCheckable()
      self.slavedTickXBold.setText('B')
      self.slavedTickXBold.setToolTip('Font weight of transformed x axis ticks')
      self.slavedTickXBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedTickXBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.slavedTickXBold.font()
      useFont.setBold(True)
      self.slavedTickXBold.setFont(useFont)
      self.Layout_slavedTickX35.addWidget(self.slavedTickXBold)
        
      self.slavedTickXItalic = QPushButtonCheckable()
      self.slavedTickXItalic.setText('I')
      self.slavedTickXItalic.setToolTip('Font style of transformed x axis ticks')
      self.slavedTickXItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedTickXItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.slavedTickXItalic.font()
      useFont.setItalic(True)
      self.slavedTickXItalic.setFont(useFont)
      self.Layout_slavedTickX35.addWidget(self.slavedTickXItalic)
  
      self.slavedTickXFont = FontoBox()
      self.slavedTickXFont.setToolTip('Font of transformed x axis ticks')
      self.slavedTickXFont.addItems(items=self.parent.fontNames)
      self.slavedTickXFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX35.addWidget(self.slavedTickXFont)
  
      self.slavedTickXBox36 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.slavedTickXBox36)
      ###self.vLayout.addWidget(self.slavedTickXBox36)
      self.Layout_outerBox4c.addWidget(self.slavedTickXBox36)
      self.Layout_slavedTickX36 = QtWidgets.QHBoxLayout(self.slavedTickXBox36)
      self.Layout_slavedTickX36.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedTickX36.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedTickX36.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      self.toggleItemsTransformed.append(self.slavedTickXBox36)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX36.addWidget(spacer)
      
      self.slavedTickXAngleLabel = QtWidgets.QLabel('angle')
      self.slavedTickXAngleLabel.setObjectName('advanced')
      self.slavedTickXAngleLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAngleLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX36.addWidget(self.slavedTickXAngleLabel)

      self.slavedTickXAngle = QDoubleSpinClick()
      self.Layout_slavedTickX36.addWidget(self.slavedTickXAngle)
      self.slavedTickXAngle.setMinimum(0.0)
      self.slavedTickXAngle.setMaximum(360.0)
      self.slavedTickXAngle.setToolTip('Angle of transformed x axis ticks')
      self.slavedTickXAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAngle.setSingleStep(10)
      self.slavedTickXAngle.setWrapping(True)
      self.slavedTickXAngle.setAdaptive(False)
      
      self.slavedTickXAlignmentLabel = QtWidgets.QLabel('align')
      self.slavedTickXAlignmentLabel.setObjectName('advanced')
      self.slavedTickXAlignmentLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAlignmentLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX36.addWidget(self.slavedTickXAlignmentLabel)
  
      self.slavedTickXAlignment = QComboBoxMac()
      self.slavedTickXAlignment.setToolTip('Horizontal alignment of transformed x axis ticks')
      self.slavedTickXAlignment.addItems(self.alignHorizontal)
      self.slavedTickXAlignment.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAlignment.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX36.addWidget(self.slavedTickXAlignment)
  
      self.slavedTickXAlignmentVertical = QComboBoxMac()
      self.slavedTickXAlignmentVertical.setToolTip('Vertical alignment of transformed x axis ticks')
      self.slavedTickXAlignmentVertical.addItems(self.alignVertical)
      self.slavedTickXAlignmentVertical.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXAlignmentVertical.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX36.addWidget(self.slavedTickXAlignmentVertical)

      self.slavedTickXLinespacingLabel = QtWidgets.QLabel('linespacing')
      self.slavedTickXLinespacingLabel.setObjectName('advanced')
      self.slavedTickXLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX36.addWidget(self.slavedTickXLinespacingLabel)
  
      self.slavedTickXLinespacing = QLineEditClick()
      self.slavedTickXLinespacing.setToolTip('Linespacing of transformed x axis ticks')
      self.slavedTickXLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXLinespacing.setValidator(self.validFloat)
      self.Layout_slavedTickX36.addWidget(self.slavedTickXLinespacing)
  
      # x tick config continued
      self.slavedTickXBox25 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.slavedTickXBox25)
      ###self.vLayout.addWidget(self.slavedTickXBox25)
      self.Layout_outerBox4c.addWidget(self.slavedTickXBox25)
      self.Layout_slavedTickX25 = QtWidgets.QHBoxLayout(self.slavedTickXBox25)
      self.Layout_slavedTickX25.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedTickX25.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedTickX25.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      self.toggleItemsTransformed.append(self.slavedTickXBox25)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX25.addWidget(spacer)

      self.slavedTickXPadLabel = QtWidgets.QLabel('pad')
      self.slavedTickXPadLabel.setObjectName('advanced')
      self.slavedTickXPadLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXPadLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX25.addWidget(self.slavedTickXPadLabel)
  
      self.slavedTickXPad = QLineEditClick()
      self.slavedTickXPad.setToolTip('Vertical padding of transformed x axis ticks')
      self.slavedTickXPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXPad.setValidator(self.validFloat)
      self.Layout_slavedTickX25.addWidget(self.slavedTickXPad)

      self.slavedTickXPad2Label = QtWidgets.QLabel('pad2')
      self.slavedTickXPad2Label.setObjectName('advanced')
      self.slavedTickXPad2Label.setMaximumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXPad2Label.setMinimumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickX25.addWidget(self.slavedTickXPad2Label)
  
      self.slavedTickXPad2 = QLineEditClick()
      self.slavedTickXPad2.setToolTip('Horizontal padding of transformed x axis ticks')
      self.slavedTickXPad2.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXPad2.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickXPad2.setValidator(self.validFloat)
      self.Layout_slavedTickX25.addWidget(self.slavedTickXPad2)

      # tick label formatting
      self.slavedXBox4 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.slavedXBox4)
      ###self.vLayout.addWidget(self.slavedXBox4)
      self.Layout_outerBox4c.addWidget(self.slavedXBox4)
      self.Layout_slavedX4 = QtWidgets.QHBoxLayout(self.slavedXBox4)
      self.Layout_slavedX4.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedX4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedX4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      self.toggleItemsTransformed.append(self.slavedXBox4)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(spacer)
      
      self.slavedXFormatLabel = QtWidgets.QLabel('fmt')
      self.slavedXFormatLabel.setObjectName('advanced')
      self.slavedXFormatLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(self.slavedXFormatLabel)
  
      self.slavedXFormat = QComboBoxMac()
      self.slavedXFormat.setToolTip('Format of transformed x axis ticks')
      self.slavedXFormat.addItems(self.formatOptions)
      self.slavedXFormat.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormat.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(self.slavedXFormat)
  
      self.slavedXFormatPrecisionLabel = QtWidgets.QLabel('precision')
      self.slavedXFormatPrecisionLabel.setObjectName('advanced')
      self.slavedXFormatPrecisionLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatPrecisionLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(self.slavedXFormatPrecisionLabel)
  
      self.slavedXFormatPrecision = QLineEditClick()
      self.slavedXFormatPrecision.setToolTip('Number precision in transformed x axis ticks')
      self.slavedXFormatPrecision.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatPrecision.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatPrecision.setValidator(self.validInt)
      self.Layout_slavedX4.addWidget(self.slavedXFormatPrecision)

      self.slavedXFormatTrailZero = QPushButtonCheckable()
      self.slavedXFormatTrailZero.setText('trail 0s?')
      self.slavedXFormatTrailZero.setToolTip('Remove trailing zeros in transformed x axis ticks')
      self.slavedXFormatTrailZero.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatTrailZero.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(self.slavedXFormatTrailZero)

      self.slavedXFormatSeparator = QPushButtonCheckable()
      self.slavedXFormatSeparator.setText('separate 1000s?')
      self.slavedXFormatSeparator.setToolTip('Separate 1000\'s in transformed x axis ticks')
      self.slavedXFormatSeparator.setMaximumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatSeparator.setMinimumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(self.slavedXFormatSeparator)

      self.slavedXFormatComma = QPushButtonCheckable()
      self.slavedXFormatComma.setText('comma?')
      self.slavedXFormatComma.setToolTip('Use comma to denote decimal numbers in transformed x axis ticks?')
      self.slavedXFormatComma.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXFormatComma.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX4.addWidget(self.slavedXFormatComma)
      self.Layout_slavedX4.addStretch()

      # tick label formatting ctd.
      self.slavedXBox5 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.slavedXBox5)
      ###self.vLayout.addWidget(self.slavedXBox5)
      self.Layout_outerBox4c.addWidget(self.slavedXBox5)
      self.Layout_slavedX5 = QtWidgets.QHBoxLayout(self.slavedXBox5)
      self.Layout_slavedX5.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedX5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedX5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      self.toggleItemsTransformed.append(self.slavedXBox5)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX5.addWidget(spacer)
      
      self.slavedXPrefixLabel = QtWidgets.QLabel('prefix')
      self.slavedXPrefixLabel.setObjectName('advanced')
      self.slavedXPrefixLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPrefixLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX5.addWidget(self.slavedXPrefixLabel)
      self.slavedXPrefix = QLineEditClick()
      self.slavedXPrefix.setToolTip('Constant prefix in transformed x axis ticks')
      self.slavedXPrefix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.slavedXPrefix.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPrefix.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX5.addWidget(self.slavedXPrefix)

      self.slavedXPostfixLabel = QtWidgets.QLabel('postfix')
      self.slavedXPostfixLabel.setObjectName('advanced')
      self.slavedXPostfixLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPostfixLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX5.addWidget(self.slavedXPostfixLabel)
      self.slavedXPostfix = QLineEditClick()
      self.slavedXPostfix.setToolTip('Constant postfix in transformed x axis ticks')
      self.slavedXPostfix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.slavedXPostfix.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.slavedXPostfix.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedX5.addWidget(self.slavedXPostfix)

    # y ticks config
    self.outerBox5 = QWidgetMac()
    self.outerBox5.setObjectName(outTypes[outCount % 2])
    outCount += 1
    self.vLayout.addWidget(self.outerBox5)
    self.Layout_outerBox5 = QtWidgets.QVBoxLayout(self.outerBox5)
    self.Layout_outerBox5.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox5.setSpacing(2)
    self.Layout_outerBox5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.configTickYBox = QWidgetMac()
    ###self.vLayout.addWidget(self.configTickYBox)
    self.Layout_outerBox5.addWidget(self.configTickYBox)
    self.Layout_configTickY = QtWidgets.QHBoxLayout(self.configTickYBox)
    self.Layout_configTickY.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.configTickYLabel = QPushButtonCheckable()
    if(self.secondAxes):
      self.configTickYLabel.setText('y2 ticks')
      self.configTickYLabel.setToolTip('Show y2 axis tick labels')
    else:
      self.configTickYLabel.setText('y ticks')
      self.configTickYLabel.setToolTip('Show y axis tick labels')
    self.configTickYLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY.addWidget(self.configTickYLabel)
    
    self.configTickYAuto = QPushButtonCheckable()
    self.configTickYAuto.setText('auto')
    if(self.secondAxes):
      self.configTickYAuto.setToolTip('Automatically set y2 axis ticks')
    else:
      self.configTickYAuto.setToolTip('Automatically set y axis ticks')
    self.configTickYAuto.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAuto.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY.addWidget(self.configTickYAuto)
        
    self.configTickYEntry = QLineEditClick()
    if(self.secondAxes):
      self.configTickYEntry.setToolTip('Values of y2 axis ticks')
    else:
      self.configTickYEntry.setToolTip('Values of y axis ticks')
    self.configTickYEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.configTickYEntry.setMaximumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYEntry.setMinimumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY.addWidget(self.configTickYEntry)

    self.configTickYUseData = QPushButtonMac()
    self.configTickYUseData.setText('labels')
    self.configTickYUseData.setToolTip('Use data labels as y axis ticks')
    self.configTickYUseData.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYUseData.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY.addWidget(self.configTickYUseData)
    
    # minor tick formatting
    self.configTickYBox6 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configTickYBox6)
    ###self.vLayout.addWidget(self.configTickYBox6)
    self.Layout_outerBox5.addWidget(self.configTickYBox6)
    self.Layout_configTickY6 = QtWidgets.QHBoxLayout(self.configTickYBox6)
    self.Layout_configTickY6.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY6.addWidget(spacer)
    
    self.configMinorTickYLabel = QPushButtonCheckable()
    self.configMinorTickYLabel.setText('minor')
    if(self.secondAxes):
      self.configMinorTickYLabel.setToolTip('Show y2 axis minor ticks')
    else:
      self.configMinorTickYLabel.setToolTip('Show y axis minor ticks')
    self.configMinorTickYLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
    self.configMinorTickYLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY6.addWidget(self.configMinorTickYLabel)
    self.configMinorTickY = QComboBoxMac()
    if(self.secondAxes):
      self.configMinorTickY.setToolTip('Number of y2 axis minor ticks')
    else:
      self.configMinorTickY.setToolTip('Number of y axis minor ticks')
    for entry in [str(i + 1) for i in range(9)]:
      self.configMinorTickY.addItem(str(entry))
    self.configMinorTickY.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.configMinorTickY.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY6.addWidget(self.configMinorTickY)

    self.configMinorTickYRelativeLengthLabel = QtWidgets.QLabel('rel length')
    self.configMinorTickYRelativeLengthLabel.setObjectName('advanced')
    self.configMinorTickYRelativeLengthLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.configMinorTickYRelativeLengthLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY6.addWidget(self.configMinorTickYRelativeLengthLabel)
    self.configMinorTickYRelativeLength = QLineEditClick()
    if(self.secondAxes):
      self.configMinorTickYRelativeLength.setToolTip('Relative length of y2 axis minor ticks')
    else:
      self.configMinorTickYRelativeLength.setToolTip('Relative length of y axis minor ticks')
    self.configMinorTickYRelativeLength.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configMinorTickYRelativeLength.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configMinorTickYRelativeLength.setValidator(self.validFloat)
    self.Layout_configTickY6.addWidget(self.configMinorTickYRelativeLength)
    
    # font formatting
    self.configTickYBox3 = QWidgetMac()
    ###self.vLayout.addWidget(self.configTickYBox3)
    self.Layout_outerBox5.addWidget(self.configTickYBox3)
    self.Layout_configTickY3 = QtWidgets.QHBoxLayout(self.configTickYBox3)
    self.Layout_configTickY3.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY3.addWidget(spacer)
    
    self.configTickYSizeLabel = QtWidgets.QLabel('font')
    self.configTickYSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY3.addWidget(self.configTickYSizeLabel)
    self.configTickYColorButton = QPushButtonColor()
    if(self.secondAxes):
      self.configTickYColorButton.setToolTip('Color of y2 axis ticks')
    else:
      self.configTickYColorButton.setToolTip('Color of y axis ticks')
    self.configTickYColorButton.setAutoFillBackground(False)
    self.configTickYColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configTickYColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configTickYColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
    self.Layout_configTickY3.addWidget(self.configTickYColorButton)

    self.configTickYSize = QDoubleSpinClick()
    self.Layout_configTickY3.addWidget(self.configTickYSize)
    self.configTickYSize.setMinimum(0.0)
    self.configTickYSize.setMaximum(100.0)
    if(self.secondAxes):
      self.configTickYSize.setToolTip('Font size of y2 axis ticks')
    else:
      self.configTickYSize.setToolTip('Font size of y axis ticks')
    self.configTickYSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))

    self.configTickYBold = QPushButtonCheckable()
    self.configTickYBold.setText('B')
    if(self.secondAxes):
      self.configTickYBold.setToolTip('Font weight of y2 axis ticks')
    else:
      self.configTickYBold.setToolTip('Font weight of y axis ticks')
    self.configTickYBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configTickYBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    useFont = self.configTickYBold.font()
    useFont.setBold(True)
    self.configTickYBold.setFont(useFont)
    self.Layout_configTickY3.addWidget(self.configTickYBold)
      
    self.configTickYItalic = QPushButtonCheckable()
    self.configTickYItalic.setText('I')
    if(self.secondAxes):
      self.configTickYItalic.setToolTip('Font style of y2 axis ticks')
    else:
      self.configTickYItalic.setToolTip('Font style of y axis ticks')
    self.configTickYItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.configTickYItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    useFont = self.configTickYItalic.font()
    useFont.setItalic(True)
    self.configTickYItalic.setFont(useFont)
    self.Layout_configTickY3.addWidget(self.configTickYItalic)

    self.configTickYFont = FontoBox()
    if(self.secondAxes):
      self.configTickYFont.setToolTip('Font of y2 axis ticks')
    else:
      self.configTickYFont.setToolTip('Font of y axis ticks')
    self.configTickYFont.addItems(items=self.parent.fontNames)
    self.configTickYFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY3.addWidget(self.configTickYFont)

    self.configTickYBox2 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configTickYBox2)
    ###self.vLayout.addWidget(self.configTickYBox2)
    self.Layout_outerBox5.addWidget(self.configTickYBox2)
    self.Layout_configTickY2 = QtWidgets.QHBoxLayout(self.configTickYBox2)
    self.Layout_configTickY2.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY2.addWidget(spacer)

    self.configTickYAngleLabel = QtWidgets.QLabel('angle')
    self.configTickYAngleLabel.setObjectName('advanced')
    self.configTickYAngleLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAngleLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY2.addWidget(self.configTickYAngleLabel)

    self.configTickYAngle = QDoubleSpinClick()
    self.Layout_configTickY2.addWidget(self.configTickYAngle)
    self.configTickYAngle.setMinimum(0.0)
    self.configTickYAngle.setMaximum(360.0)
    if(self.secondAxes):
      self.configTickYAngle.setToolTip('Angle of y2 axis ticks')
    else:
      self.configTickYAngle.setToolTip('Angle of y axis ticks')
    self.configTickYAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAngle.setSingleStep(10)
    self.configTickYAngle.setWrapping(True)
    self.configTickYAngle.setAdaptive(False)
    
    self.configTickYAlignmentLabel = QtWidgets.QLabel('align')
    self.configTickYAlignmentLabel.setObjectName('advanced')
    self.configTickYAlignmentLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAlignmentLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY2.addWidget(self.configTickYAlignmentLabel)

    self.configTickYAlignment = QComboBoxMac()
    if(self.secondAxes):
      self.configTickYAlignment.setToolTip('Horizontal alignment of y2 axis ticks')
    else:
      self.configTickYAlignment.setToolTip('Horizontal alignment of y axis ticks')
    self.configTickYAlignment.addItems(self.alignHorizontal)
    self.configTickYAlignment.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAlignment.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY2.addWidget(self.configTickYAlignment)

    self.configTickYAlignmentVertical = QComboBoxMac()
    if(self.secondAxes):
      self.configTickYAlignmentVertical.setToolTip('Vertical alignment of y2 axis ticks')
    else:
      self.configTickYAlignmentVertical.setToolTip('Vertical alignment of y axis ticks')
    self.configTickYAlignmentVertical.addItems(self.alignVertical)
    self.configTickYAlignmentVertical.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYAlignmentVertical.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY2.addWidget(self.configTickYAlignmentVertical)

    self.configTickYLinespacingLabel = QtWidgets.QLabel('linespacing')
    self.configTickYLinespacingLabel.setObjectName('advanced')
    self.configTickYLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY2.addWidget(self.configTickYLinespacingLabel)

    self.configTickYLinespacing = QLineEditClick()
    if(self.secondAxes):
      self.configTickYLinespacing.setToolTip('Linespacing of y2 axis ticks')
    else:
      self.configTickYLinespacing.setToolTip('Linespacing of y axis ticks')
    self.configTickYLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYLinespacing.setValidator(self.validFloat)
    self.Layout_configTickY2.addWidget(self.configTickYLinespacing)

    # y tick config continued
    self.configTickYBox25 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configTickYBox25)
    ###self.vLayout.addWidget(self.configTickYBox25)
    self.Layout_outerBox5.addWidget(self.configTickYBox25)
    self.Layout_configTickY25 = QtWidgets.QHBoxLayout(self.configTickYBox25)
    self.Layout_configTickY25.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY25.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY25.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY25.addWidget(spacer)

    self.configTickYPadLabel = QtWidgets.QLabel('pad')
    self.configTickYPadLabel.setObjectName('advanced')
    self.configTickYPadLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPadLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY25.addWidget(self.configTickYPadLabel)

    self.configTickYPad = QLineEditClick()
    if(self.secondAxes):
      self.configTickYPad.setToolTip('Horizontal padding of y2 axis ticks')
    else:
      self.configTickYPad.setToolTip('Horizontal padding of y axis ticks')
    self.configTickYPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPad.setValidator(self.validFloat)
    self.Layout_configTickY25.addWidget(self.configTickYPad)

    self.configTickYPad2Label = QtWidgets.QLabel('pad2')
    self.configTickYPad2Label.setObjectName('advanced')
    self.configTickYPad2Label.setMaximumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPad2Label.setMinimumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY25.addWidget(self.configTickYPad2Label)

    self.configTickYPad2 = QLineEditClick()
    if(self.secondAxes):
      self.configTickYPad2.setToolTip('Vertical padding of y2 axis ticks')
    else:
      self.configTickYPad2.setToolTip('Vertical padding of y axis ticks')
    self.configTickYPad2.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPad2.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPad2.setValidator(self.validFloat)
    self.Layout_configTickY25.addWidget(self.configTickYPad2)

    # tick label formatting
    self.configTickYBox4 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configTickYBox4)
    ###self.vLayout.addWidget(self.configTickYBox4)
    self.Layout_outerBox5.addWidget(self.configTickYBox4)
    self.Layout_configTickY4 = QtWidgets.QHBoxLayout(self.configTickYBox4)
    self.Layout_configTickY4.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(spacer)
    
    self.configTickYFormatLabel = QtWidgets.QLabel('fmt')
    self.configTickYFormatLabel.setObjectName('advanced')
    self.configTickYFormatLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(self.configTickYFormatLabel)

    self.configTickYFormat = QComboBoxMac()
    if(self.secondAxes):
      self.configTickYFormat.setToolTip('Format of y2 axis ticks')
    else:
      self.configTickYFormat.setToolTip('Format of y axis ticks')
    self.configTickYFormat.addItems(self.formatOptions)
    self.configTickYFormat.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormat.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(self.configTickYFormat)

    self.configTickYFormatPrecisionLabel = QtWidgets.QLabel('precision')
    self.configTickYFormatPrecisionLabel.setObjectName('advanced')
    self.configTickYFormatPrecisionLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatPrecisionLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(self.configTickYFormatPrecisionLabel)

    self.configTickYFormatPrecision = QLineEditClick()
    if(self.secondAxes):
      self.configTickYFormatPrecision.setToolTip('Number precision in y2 axis ticks')
    else:
      self.configTickYFormatPrecision.setToolTip('Number precision in y axis ticks')
    self.configTickYFormatPrecision.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatPrecision.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatPrecision.setValidator(self.validInt)
    self.Layout_configTickY4.addWidget(self.configTickYFormatPrecision)

    self.configTickYFormatTrailZero = QPushButtonCheckable()
    self.configTickYFormatTrailZero.setText('trail 0s?')
    if(self.secondAxes):
      self.configTickYFormatTrailZero.setToolTip('Remove trailing zeros in y2 axis ticks')
    else:
      self.configTickYFormatTrailZero.setToolTip('Remove trailing zeros in y axis ticks')
    self.configTickYFormatTrailZero.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatTrailZero.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(self.configTickYFormatTrailZero)

    self.configTickYFormatSeparator = QPushButtonCheckable()
    self.configTickYFormatSeparator.setText('separate 1000s?')
    if(self.secondAxes):
      self.configTickYFormatSeparator.setToolTip('Separate 1000\'s in y2 axis ticks')
    else:
      self.configTickYFormatSeparator.setToolTip('Separate 1000\'s in y axis ticks')
    self.configTickYFormatSeparator.setMaximumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatSeparator.setMinimumSize(QtCore.QSize(int(84 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(self.configTickYFormatSeparator)

    self.configTickYFormatComma = QPushButtonCheckable()
    self.configTickYFormatComma.setText('comma?')
    if(self.secondAxes):
      self.configTickYFormatComma.setToolTip('Use comma to denote decimal numbers in y2 axis ticks?')
    else:
      self.configTickYFormatComma.setToolTip('Use comma to denote decimal numbers in y axis ticks?')
    self.configTickYFormatComma.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYFormatComma.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY4.addWidget(self.configTickYFormatComma)
    self.Layout_configTickY4.addStretch()

    # tick label formatting ctd.
    self.configTickYBox5 = QWidgetMac()
    self.advancedGraphicsSettings.append(self.configTickYBox5)
    ###self.vLayout.addWidget(self.configTickYBox5)
    self.Layout_outerBox5.addWidget(self.configTickYBox5)
    self.Layout_configTickY5 = QtWidgets.QHBoxLayout(self.configTickYBox5)
    self.Layout_configTickY5.setContentsMargins(0, 0, 0, 0)
    self.Layout_configTickY5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.Layout_configTickY5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    spacer = QtWidgets.QLabel()
    spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY5.addWidget(spacer)
    
    self.configTickYPrefixLabel = QtWidgets.QLabel('prefix')
    self.configTickYPrefixLabel.setObjectName('advanced')
    self.configTickYPrefixLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPrefixLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY5.addWidget(self.configTickYPrefixLabel)
    self.configTickYPrefix = QLineEditClick()
    if(self.secondAxes):
      self.configTickYPrefix.setToolTip('Constant prefix in y2 axis ticks')
    else:
      self.configTickYPrefix.setToolTip('Constant prefix in y axis ticks')
    self.configTickYPrefix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.configTickYPrefix.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPrefix.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY5.addWidget(self.configTickYPrefix)

    self.configTickYPostfixLabel = QtWidgets.QLabel('postfix')
    self.configTickYPostfixLabel.setObjectName('advanced')
    self.configTickYPostfixLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPostfixLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY5.addWidget(self.configTickYPostfixLabel)
    self.configTickYPostfix = QLineEditClick()
    if(self.secondAxes):
      self.configTickYPostfix.setToolTip('Constant postfix in y2 axis ticks')
    else:
      self.configTickYPostfix.setToolTip('Constant postfix in y axis ticks')
    self.configTickYPostfix.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.configTickYPostfix.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.configTickYPostfix.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.Layout_configTickY5.addWidget(self.configTickYPostfix)

    if(not self.secondAxes):
      self.outerBox5b = QWidgetMac()
      self.outerBox5b.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox5b)
      self.Layout_outerBox5b = QtWidgets.QVBoxLayout(self.outerBox5b)
      self.Layout_outerBox5b.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox5b.setSpacing(2)
      self.Layout_outerBox5b.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      self.configTickResidYBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configTickResidYBox)
      self.Layout_outerBox5b.addWidget(self.configTickResidYBox)
      self.Layout_configTickResidY = QtWidgets.QHBoxLayout(self.configTickResidYBox)
      self.Layout_configTickResidY.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickResidY.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickResidY.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configTickResidYLabel = QPushButtonCheckable()
      self.configTickResidYLabel.setText('resid')
      self.configTickResidYLabel.setToolTip('Show residuals y axis tick labels')
      self.configTickResidYLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configTickResidYLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidY.addWidget(self.configTickResidYLabel)
  
      self.configTickResidYAuto = QPushButtonCheckable()
      self.configTickResidYAuto.setText('auto')
      self.configTickResidYAuto.setToolTip('Automatically set residuals y axis ticks')
      self.configTickResidYAuto.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configTickResidYAuto.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidY.addWidget(self.configTickResidYAuto)
          
      self.configTickResidYEntry = QLineEditClick()
      self.configTickResidYEntry.setToolTip('Values of residuals y axis ticks')
      self.configTickResidYEntry.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configTickResidYEntry.setMaximumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.configTickResidYEntry.setMinimumSize(QtCore.QSize(int(252 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidY.addWidget(self.configTickResidYEntry)

      # minor tick formatting
      self.configTickResidYBox2 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configTickResidYBox2)
      ###self.vLayout.addWidget(self.configTickResidYBox2)
      self.Layout_outerBox5b.addWidget(self.configTickResidYBox2)
      self.Layout_configTickResidYBox2 = QtWidgets.QHBoxLayout(self.configTickResidYBox2)
      self.Layout_configTickResidYBox2.setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickResidYBox2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickResidYBox2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidYBox2.addWidget(spacer)
      
      self.configMinorTickResidLabel = QPushButtonCheckable()
      self.configMinorTickResidLabel.setText('minor')
      self.configMinorTickResidLabel.setToolTip('Show residuals y axis minor ticks')
      self.configMinorTickResidLabel.setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickResidLabel.setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidYBox2.addWidget(self.configMinorTickResidLabel)
      self.configMinorTickResid = QComboBoxMac()
      self.configMinorTickResid.setToolTip('Number of residuals y axis minor ticks')
      for entry in [str(i + 1) for i in range(9)]:
        self.configMinorTickResid.addItem(str(entry))
      self.configMinorTickResid.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickResid.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidYBox2.addWidget(self.configMinorTickResid)
  
      self.configMinorTickResidRelativeLengthLabel = QtWidgets.QLabel('rel length')
      self.configMinorTickResidRelativeLengthLabel.setObjectName('advanced')
      self.configMinorTickResidRelativeLengthLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickResidRelativeLengthLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickResidYBox2.addWidget(self.configMinorTickResidRelativeLengthLabel)
      self.configMinorTickResidRelativeLength = QLineEditClick()
      self.configMinorTickResidRelativeLength.setToolTip('Relative length of residuals y axis minor ticks')
      self.configMinorTickResidRelativeLength.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickResidRelativeLength.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configMinorTickResidRelativeLength.setValidator(self.validFloat)
      self.Layout_configTickResidYBox2.addWidget(self.configMinorTickResidRelativeLength)
  
    # tick mark config
    self.outerBox6 = QWidgetMac()
    self.outerBox6.setObjectName(outTypes[outCount % 2])
    outCount += 1
    self.vLayout.addWidget(self.outerBox6)
    self.Layout_outerBox6 = QtWidgets.QVBoxLayout(self.outerBox6)
    self.Layout_outerBox6.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox6.setSpacing(2)
    self.Layout_outerBox6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.configTickMarkBox = {}; self.Layout_configTickMark = {}
    self.configTickMarkLabel = {}
    self.configTickMarkWidthLabel = {}; self.configTickMarkWidth = {}
    self.configTickMarkLengthLabel = {}; self.configTickMarkLength = {}
    self.configTickMarkDirection = {}; self.configTickMarkColor = {}
    self.configTickMarkLabelShow = {}
    if(not self.secondAxes):
      useAxes = ['bottom', 'top', 'left', 'right']
    else:
      useAxes = ['left2', 'right2']
    for axis in useAxes:
      self.configTickMarkBox[axis] = QWidgetMac()
      ###self.vLayout.addWidget(self.configTickMarkBox[axis])
      self.Layout_outerBox6.addWidget(self.configTickMarkBox[axis])
      self.Layout_configTickMark[axis] = QtWidgets.QHBoxLayout(self.configTickMarkBox[axis])
      self.Layout_configTickMark[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configTickMark[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configTickMark[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configTickMarkLabel[axis] = QPushButtonCheckable()
      self.configTickMarkLabel[axis].setText('tick ' + axis)
      self.configTickMarkLabel[axis].setToolTip('Show ' + axis + ' axis tick marks')
      self.configTickMarkLabel[axis].setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkLabel[axis].setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkLabel[axis])

      self.configTickMarkDirection[axis] = QComboBoxMac()
      self.configTickMarkDirection[axis].setToolTip('Direction of ' + axis + ' axis tick marks')
      self.directionstyles = ['in', 'out', 'inout']
      self.configTickMarkDirection[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkDirection[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkDirection[axis])

      self.configTickMarkColor[axis] = QPushButtonColor()
      self.configTickMarkColor[axis].setToolTip('Color of ' + axis + ' axis tick marks')
      self.configTickMarkColor[axis].setAutoFillBackground(False)
      self.configTickMarkColor[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configTickMarkColor[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configTickMarkColor[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkColor[axis])
  
      self.configTickMarkWidthLabel[axis] = QtWidgets.QLabel('width')
      self.configTickMarkWidthLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkWidthLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkWidthLabel[axis])
      self.configTickMarkWidth[axis] = QDoubleSpinClick()
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkWidth[axis])
      self.configTickMarkWidth[axis].setMinimum(0.0)
      self.configTickMarkWidth[axis].setMaximum(100.0)
      self.configTickMarkWidth[axis].setToolTip('Line width of ' + axis + ' axis tick marks')
      self.configTickMarkWidth[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkWidth[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.configTickMarkLengthLabel[axis] = QtWidgets.QLabel('length')
      self.configTickMarkLengthLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkLengthLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkLengthLabel[axis])
      self.configTickMarkLength[axis] = QDoubleSpinClick()
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkLength[axis])
      self.configTickMarkLength[axis].setMinimum(0.0)
      self.configTickMarkLength[axis].setMaximum(100.0)
      self.configTickMarkLength[axis].setToolTip('Line length of ' + axis + ' axis tick marks')
      self.configTickMarkLength[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkLength[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.configTickMarkLabelShow[axis] = QPushButtonCheckable()
      # better don't initially hide this option as it may be used relatively often
      ###self.advancedGraphicsSettings.append(self.configTickMarkLabelShow[axis])
      self.configTickMarkLabelShow[axis].setText('label')
      self.configTickMarkLabelShow[axis].setToolTip('Show ' + axis + ' axis tick labels')
      self.configTickMarkLabelShow[axis].setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configTickMarkLabelShow[axis].setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configTickMark[axis].addWidget(self.configTickMarkLabelShow[axis])

    if(not self.secondAxes):
      self.slavedTickMarkBox = QWidgetMac()
      self.Layout_outerBox6.addWidget(self.slavedTickMarkBox)
      self.Layout_slavedTickMark = QtWidgets.QHBoxLayout(self.slavedTickMarkBox)
      self.Layout_slavedTickMark.setContentsMargins(0, 0, 0, 0)
      self.Layout_slavedTickMark.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_slavedTickMark.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      self.toggleItemsTransformed.append(self.slavedTickMarkBox)

      self.slavedTickMarkLabel = QPushButtonCheckable()
      self.slavedTickMarkLabel.setText('tick transf.')
      self.slavedTickMarkLabel.setToolTip('Show transformed axis tick marks')
      self.slavedTickMarkLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkLabel)

      self.slavedTickMarkDirection = QComboBoxMac()
      self.slavedTickMarkDirection.setToolTip('Direction of transformed axis tick marks')
      self.directionstyles = ['in', 'out', 'inout']
      self.slavedTickMarkDirection.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkDirection.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkDirection)

      self.slavedTickMarkColor = QPushButtonColor()
      self.slavedTickMarkColor.setToolTip('Color of transformed axis tick marks')
      self.slavedTickMarkColor.setAutoFillBackground(False)
      self.slavedTickMarkColor.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedTickMarkColor.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.slavedTickMarkColor.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkColor)
  
      self.slavedTickMarkWidthLabel = QtWidgets.QLabel('width')
      self.slavedTickMarkWidthLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkWidthLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkWidthLabel)
      self.slavedTickMarkWidth = QDoubleSpinClick()
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkWidth)
      self.slavedTickMarkWidth.setMinimum(0.0)
      self.slavedTickMarkWidth.setMaximum(100.0)
      self.slavedTickMarkWidth.setToolTip('Line width of transformed axis tick marks')
      self.slavedTickMarkWidth.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkWidth.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.slavedTickMarkLengthLabel = QtWidgets.QLabel('length')
      self.slavedTickMarkLengthLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkLengthLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkLengthLabel)
      self.slavedTickMarkLength = QDoubleSpinClick()
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkLength)
      self.slavedTickMarkLength.setMinimum(0.0)
      self.slavedTickMarkLength.setMaximum(100.0)
      self.slavedTickMarkLength.setToolTip('Line length of transformed axis tick marks')
      self.slavedTickMarkLength.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkLength.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.slavedTickMarkLabelShow = QPushButtonCheckable()
      self.slavedTickMarkLabelShow.setText('label')
      self.slavedTickMarkLabelShow.setToolTip('Show transformed axis tick labels')
      self.slavedTickMarkLabelShow.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.slavedTickMarkLabelShow.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_slavedTickMark.addWidget(self.slavedTickMarkLabelShow)

    # grid config
    blah = HLine()
    ###self.vLayout.addWidget(blah)

    self.outerBox7 = QWidgetMac()
    self.outerBox7.setObjectName(outTypes[outCount % 2])
    outCount += 1
    self.vLayout.addWidget(self.outerBox7)
    self.Layout_outerBox7 = QtWidgets.QVBoxLayout(self.outerBox7)
    self.Layout_outerBox7.setContentsMargins(2, 4, 2, 4)
    self.Layout_outerBox7.setSpacing(2)
    self.Layout_outerBox7.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

    self.configGridBox, self.Layout_configGrid = {}, {}
    self.configGridLabel = {}
    self.configGridWidthLabel, self.configGridWidth = {}, {}
    self.configGridColor, self.configGridStyle = {}, {}
    self.configGridDashStyle, self.configGridOrder = {}, {}
    # ctrls for minor gridli
    self.configGridBox2, self.Layout_configGrid2 = {}, {}
    self.configGridLabel2 = {}
    self.configGridWidthLabel2, self.configGridWidth2 = {}, {}
    self.configGridColor2, self.configGridStyle2 = {}, {}
    self.configGridDashStyle2, self.configGridOrder2 = {}, {}
    self.configGridBox3, self.Layout_configGrid3, self.configGridLabel3, self.configGridRectColor = {}, {}, {}, {}
    if(not self.secondAxes):
      useAxes = ['x', 'y', 'x2']
    else:
      useAxes = ['y2']
    for axis in useAxes:
      self.configGridBox[axis] = QWidgetMac()
      ###self.vLayout.addWidget(self.configGridBox[axis])
      self.Layout_outerBox7.addWidget(self.configGridBox[axis])
      self.Layout_configGrid[axis] = QtWidgets.QHBoxLayout(self.configGridBox[axis])
      self.Layout_configGrid[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configGrid[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configGrid[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.configGridLabel[axis] = QPushButtonCheckable()
      if(axis != 'x2'):
        self.configGridLabel[axis].setText('grid ' + axis)
        self.configGridLabel[axis].setToolTip('Show ' + axis + ' axis grid')
      else:
        self.configGridLabel[axis].setText('grid split_x')
        self.configGridLabel[axis].setToolTip('Show split x axis grid')
      self.configGridLabel[axis].setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configGridLabel[axis].setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid[axis].addWidget(self.configGridLabel[axis])

      self.orderstyles = ['front', 'back']
      self.configGridOrder[axis] = QComboBoxMac()
      self.configGridOrder[axis].setToolTip('z order of ' + axis + ' axis grid')
      if(axis == 'x2'):
        self.configGridOrder[axis].setToolTip('z order of split x axis grid')
      self.configGridOrder[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configGridOrder[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid[axis].addWidget(self.configGridOrder[axis])

      self.configGridColor[axis] = QPushButtonColor()
      self.configGridColor[axis].setToolTip('Color of ' + axis + ' axis grid')
      if(axis == 'x2'):
        self.configGridColor[axis].setToolTip('Color of split x axis grid')
      self.configGridColor[axis].setAutoFillBackground(False)
      self.configGridColor[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configGridColor[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configGridColor[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configGrid[axis].addWidget(self.configGridColor[axis])
  
      self.configGridWidthLabel[axis] = QtWidgets.QLabel('width')
      self.configGridWidthLabel[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configGridWidthLabel[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid[axis].addWidget(self.configGridWidthLabel[axis])
      self.configGridWidth[axis] = QDoubleSpinClick()
      self.Layout_configGrid[axis].addWidget(self.configGridWidth[axis])
      self.configGridWidth[axis].setMinimum(0.0)
      self.configGridWidth[axis].setMaximum(100.0)
      self.configGridWidth[axis].setToolTip('Line width of ' + axis + ' axis grid')
      if(axis == 'x2'):
        self.configGridWidth[axis].setToolTip('Line width of split x axis grid')
      self.configGridWidth[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configGridWidth[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.configGridStyle[axis] = QComboBoxMac()
      self.configGridStyle[axis].setToolTip('Line style of ' + axis + ' axis grid')
      if(axis == 'x2'):
        self.configGridStyle[axis].setToolTip('Line style of split x axis grid')
      self.configGridStyle[axis].setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
      self.configGridStyle[axis].setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid[axis].addWidget(self.configGridStyle[axis])

      self.configGridDashStyle[axis] = QComboBoxMac()
      self.configGridDashStyle[axis].setToolTip('Cap style of ' + axis + ' axis grid')
      if(axis == 'x2'):
        self.configGridDashStyle[axis].setToolTip('Cap style of split x axis grid')
      self.advancedGraphicsSettings.append(self.configGridDashStyle[axis])
      self.configGridDashStyle[axis].setMinimumSize(QtCore.QSize(int(74 * SCALEFONT), int(BASE_SIZE)))
      self.configGridDashStyle[axis].setMaximumSize(QtCore.QSize(int(74 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid[axis].addWidget(self.configGridDashStyle[axis])
      
      # controls for minor grid formatting
      self.configGridBox2[axis] = QWidgetMac()
      ###self.vLayout.addWidget(self.configGridBox2[axis])
      self.Layout_outerBox7.addWidget(self.configGridBox2[axis])
      self.Layout_configGrid2[axis] = QtWidgets.QHBoxLayout(self.configGridBox2[axis])
      self.Layout_configGrid2[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configGrid2[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configGrid2[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid2[axis].addWidget(spacer)

      self.configGridLabel2[axis] = QPushButtonCheckable()
      self.configGridLabel2[axis].setText('minor')
      if(axis != 'x2'):
        self.configGridLabel2[axis].setToolTip('Show ' + axis + ' axis minor grid')
      else:
        self.configGridLabel2[axis].setToolTip('Show split x axis minor grid')
      self.configGridLabel2[axis].setMaximumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.configGridLabel2[axis].setMinimumSize(QtCore.QSize(int(55 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid2[axis].addWidget(self.configGridLabel2[axis])

      self.configGridOrder2[axis] = QComboBoxMac()
      self.configGridOrder2[axis].setToolTip('z order of ' + axis + ' axis minor grid')
      if(axis == 'x2'):
        self.configGridOrder2[axis].setToolTip('z order of split x axis minor grid')
      self.configGridOrder2[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configGridOrder2[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid2[axis].addWidget(self.configGridOrder2[axis])

      self.configGridColor2[axis] = QPushButtonColor()
      self.configGridColor2[axis].setToolTip('Color of ' + axis + ' axis minor grid')
      if(axis == 'x2'):
        self.configGridColor2[axis].setToolTip('Color of split x axis minor grid')
      self.configGridColor2[axis].setAutoFillBackground(False)
      self.configGridColor2[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configGridColor2[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configGridColor2[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configGrid2[axis].addWidget(self.configGridColor2[axis])
  
      self.configGridWidthLabel2[axis] = QtWidgets.QLabel('width')
      self.configGridWidthLabel2[axis].setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configGridWidthLabel2[axis].setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid2[axis].addWidget(self.configGridWidthLabel2[axis])
      self.configGridWidth2[axis] = QDoubleSpinClick()
      self.Layout_configGrid2[axis].addWidget(self.configGridWidth2[axis])
      self.configGridWidth2[axis].setMinimum(0.0)
      self.configGridWidth2[axis].setMaximum(100.0)
      self.configGridWidth2[axis].setToolTip('Line width of ' + axis + ' axis minor grid')
      if(axis == 'x2'):
        self.configGridWidth2[axis].setToolTip('Line width of split x axis minor grid')
      self.configGridWidth2[axis].setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configGridWidth2[axis].setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))

      self.configGridStyle2[axis] = QComboBoxMac()
      self.configGridStyle2[axis].setToolTip('Line style of ' + axis + ' axis minor grid')
      if(axis == 'x2'):
        self.configGridStyle2[axis].setToolTip('Line style of split x axis minor grid')
      self.configGridStyle2[axis].setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
      self.configGridStyle2[axis].setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid2[axis].addWidget(self.configGridStyle2[axis])

      self.configGridDashStyle2[axis] = QComboBoxMac()
      self.configGridDashStyle2[axis].setToolTip('Cap style of ' + axis + ' axis minor grid')
      if(axis == 'x2'):
        self.configGridDashStyle2[axis].setToolTip('Cap style of split x axis minor grid')
      self.configGridDashStyle2[axis].setMinimumSize(QtCore.QSize(int(74 * SCALEFONT), int(BASE_SIZE)))
      self.configGridDashStyle2[axis].setMaximumSize(QtCore.QSize(int(74 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid2[axis].addWidget(self.configGridDashStyle2[axis])
      
      # hide controls initially
      self.advancedGraphicsSettings.append(self.configGridBox2[axis])

      # controls for minor grid formatting
      self.configGridBox3[axis] = QWidgetMac()
      ###self.vLayout.addWidget(self.configGridBox3[axis])
      self.Layout_outerBox7.addWidget(self.configGridBox3[axis])
      self.Layout_configGrid3[axis] = QtWidgets.QHBoxLayout(self.configGridBox3[axis])
      self.Layout_configGrid3[axis].setContentsMargins(0, 0, 0, 0)
      self.Layout_configGrid3[axis].setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configGrid3[axis].setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid3[axis].addWidget(spacer)

      self.configGridLabel3[axis] = QPushButtonCheckable()
      self.configGridLabel3[axis].setText('shady')
      if(axis != 'x2'):
        self.configGridLabel3[axis].setToolTip('Show ' + axis + ' axis shading')
      else:
        self.configGridLabel3[axis].setToolTip('Show split x axis shading')
      self.configGridLabel3[axis].setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configGridLabel3[axis].setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configGrid3[axis].addWidget(self.configGridLabel3[axis])

      self.configGridRectColor[axis] = QPushButtonColor()
      self.configGridRectColor[axis].setToolTip('Color of ' + axis + ' axis shading')
      if(axis == 'x2'):
        self.configGridRectColor[axis].setToolTip('Color of split x axis shading')
      self.configGridRectColor[axis].setAutoFillBackground(False)
      self.configGridRectColor[axis].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configGridRectColor[axis].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configGridRectColor[axis].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configGrid3[axis].addWidget(self.configGridRectColor[axis])
  
      # hide controls initially
      self.advancedGraphicsSettings.append(self.configGridBox3[axis])

    if((not self.parent.plotArea.splitShow) and ('x2' in self.configGridBox)):
      self.configGridBox['x2'].hide()
      self.configGridBox2['x2'].hide()
      self.configGridBox3['x2'].hide()

    # splitY controls
    if(self.secondAxes):
      self.splitYSeparator = HLine()
      ###self.vLayout.addWidget(self.splitYSeparator)

      self.outerBox8 = QWidgetMac()
      self.outerBox8.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox8)
      self.Layout_outerBox8 = QtWidgets.QVBoxLayout(self.outerBox8)
      self.Layout_outerBox8.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox8.setSpacing(2)
      self.Layout_outerBox8.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configSplitYBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configSplitYBox)
      self.Layout_outerBox8.addWidget(self.configSplitYBox)
      self.Layout_configSplitY = QtWidgets.QHBoxLayout(self.configSplitYBox)
      self.Layout_configSplitY.setContentsMargins(0, 0, 0, 0)
      self.Layout_configSplitY.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configSplitY.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      self.configSplitYLabel = QPushButtonCheckable()
      self.configSplitYLabel.setText('split y')
      self.configSplitYLabel.setToolTip('Split 2nd y axis from 1st y axis?')
      self.configSplitYLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitY.addWidget(self.configSplitYLabel)

      self.configSplitYFractionLabel = QtWidgets.QLabel('fraction')
      self.configSplitYFractionLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYFractionLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitY.addWidget(self.configSplitYFractionLabel)
      self.configSplitYFraction = QLineEditClick()
      self.configSplitYFraction.setToolTip('Size ratio of y and split y axes')
      self.configSplitYFraction.setMaximumSize(QtCore.QSize(int(48 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYFraction.setMinimumSize(QtCore.QSize(int(48 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYFraction.setValidator(self.validFloat)
      self.Layout_configSplitY.addWidget(self.configSplitYFraction)
          
      self.configSplitYPadLabel = QtWidgets.QLabel('pad')
      self.configSplitYPadLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYPadLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitY.addWidget(self.configSplitYPadLabel)
      self.configSplitYPad = QLineEditClick()
      self.configSplitYPad.setToolTip('Padding between y and split y axes')
      self.configSplitYPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYPad.setValidator(self.validFloat)
      self.Layout_configSplitY.addWidget(self.configSplitYPad)

      self.configSplitYAxesLabel = QPushButtonCheckable()
      self.configSplitYAxesLabel.setText('axes')
      self.configSplitYAxesLabel.setToolTip('Show inner x axes')
      self.configSplitYAxesLabel.setMaximumSize(QtCore.QSize(int(42 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYAxesLabel.setMinimumSize(QtCore.QSize(int(42 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitY.addWidget(self.configSplitYAxesLabel)

      self.configSplitYTickLabel = QPushButtonCheckable()
      self.configSplitYTickLabel.setText('ticks')
      self.configSplitYTickLabel.setToolTip('Show inner x axis ticks')
      self.configSplitYTickLabel.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYTickLabel.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitY.addWidget(self.configSplitYTickLabel)
      self.Layout_configSplitY.addStretch()

      # second row
      self.configSplitYBox2 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configSplitYBox2)
      ###self.vLayout.addWidget(self.configSplitYBox2)
      self.Layout_outerBox8.addWidget(self.configSplitYBox2)
      self.Layout_configSplitYBox2 = QtWidgets.QHBoxLayout(self.configSplitYBox2)
      self.Layout_configSplitYBox2.setContentsMargins(0, 0, 0, 0)
      self.Layout_configSplitYBox2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configSplitYBox2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox2.addWidget(spacer)

      self.configSplitYDividerLine = QPushButtonCheckable()
      self.configSplitYDividerLine.setText('divider')
      self.configSplitYDividerLine.setToolTip('Show split y axis divider lines')
      self.configSplitYDividerLine.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLine.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLine)

      self.configSplitYDividerLineColor = QPushButtonColor()
      self.configSplitYDividerLineColor.setToolTip('Line color of split y axis divider lines')
      self.configSplitYDividerLineColor.setAutoFillBackground(False)
      self.configSplitYDividerLineColor.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configSplitYDividerLineColor.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configSplitYDividerLineColor.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLineColor)

      self.configSplitYDividerLineLengthLabel = QtWidgets.QLabel('length')
      self.configSplitYDividerLineLengthLabel.setObjectName('advanced')
      self.configSplitYDividerLineLengthLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineLengthLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLineLengthLabel)
      self.configSplitYDividerLineLength = QDoubleSpinClick()
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLineLength)
      self.configSplitYDividerLineLength.setMinimum(0.0)
      self.configSplitYDividerLineLength.setMaximum(2.0)
      self.configSplitYDividerLineLength.setSingleStep(.02)
      self.configSplitYDividerLineLength.setAdaptive(False)
      self.configSplitYDividerLineLength.setToolTip('Length of split y axis divider lines')
      self.configSplitYDividerLineLength.setMaximumSize(QtCore.QSize(int(48 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineLength.setMinimumSize(QtCore.QSize(int(48 * SCALEFONT), int(BASE_SIZE)))

      self.configSplitYDividerLineWidthLabel = QtWidgets.QLabel('width')
      self.configSplitYDividerLineWidthLabel.setObjectName('advanced')
      self.configSplitYDividerLineWidthLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineWidthLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLineWidthLabel)
      self.configSplitYDividerLineWidth = QDoubleSpinClick()
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLineWidth)
      self.configSplitYDividerLineWidth.setMinimum(0.0)
      self.configSplitYDividerLineWidth.setMaximum(100.0)
      self.configSplitYDividerLineWidth.setToolTip('Width of split y axis divider lines')
      self.configSplitYDividerLineWidth.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineWidth.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
  
      self.configSplitYDividerLineDashStyle = QComboBoxMac()
      self.configSplitYDividerLineDashStyle.setToolTip('Cap style of split y axis divider lines')
      self.configSplitYDividerLineDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox2.addWidget(self.configSplitYDividerLineDashStyle)

      # third row
      self.configSplitYBox3 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configSplitYBox3)
      ###self.vLayout.addWidget(self.configSplitYBox3)
      self.Layout_outerBox8.addWidget(self.configSplitYBox3)
      self.Layout_configSplitYBox3 = QtWidgets.QHBoxLayout(self.configSplitYBox3)
      self.Layout_configSplitYBox3.setContentsMargins(0, 0, 0, 0)
      self.Layout_configSplitYBox3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configSplitYBox3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      spacer = QWidgetMac(self)
      spacer.setMaximumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(16 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox3.addWidget(spacer)

      self.configSplitYDividerLineAngleLabel = QtWidgets.QLabel('angle')
      self.configSplitYDividerLineAngleLabel.setObjectName('advanced')
      self.configSplitYDividerLineAngleLabel.setMaximumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineAngleLabel.setMinimumSize(QtCore.QSize(int(44 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox3.addWidget(self.configSplitYDividerLineAngleLabel)
      self.configSplitYDividerLineAngle = QDoubleSpinClick()
      self.Layout_configSplitYBox3.addWidget(self.configSplitYDividerLineAngle)
      self.configSplitYDividerLineAngle.setMinimum(0.0)
      self.configSplitYDividerLineAngle.setMaximum(360.0)
      self.configSplitYDividerLineAngle.setToolTip('Angle of split y axis divider lines')
      self.configSplitYDividerLineAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineAngle.setSingleStep(10)
      self.configSplitYDividerLineAngle.setWrapping(True)
      self.configSplitYDividerLineAngle.setAdaptive(False)

      self.configSplitYDividerLineLocationLabel = QtWidgets.QLabel('location')
      self.configSplitYDividerLineLocationLabel.setObjectName('advanced')
      self.configSplitYDividerLineLocationLabel.setMaximumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineLocationLabel.setMinimumSize(QtCore.QSize(int(46 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox3.addWidget(self.configSplitYDividerLineLocationLabel)
      self.configSplitYDividerLineLocation = QComboBoxMac()
      self.configSplitYDividerLineLocation.setToolTip('Location of split y axis divider lines')
      self.configSplitYDividerLineLocation.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.configSplitYDividerLineLocation.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configSplitYBox3.addWidget(self.configSplitYDividerLineLocation)

      if(not self.parent.plotArea.splitY):
        ###self.configSplitYBox.hide()
        self.outerBox8.hide()
        self.configSplitYBox2.hide()
        self.configSplitYBox3.hide()
        ###self.splitYSeparator.hide()
      
    # legend config
    if(not self.secondAxes):
      self.placementstyles = 'best;upper right;upper left;lower left;lower right;right;center left;center right;lower center;upper center;center;custom'.split(';')
      blah = HLine()
      ###self.vLayout.addWidget(blah)

      self.outerBox9 = QWidgetMac()
      self.outerBox9.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox9)
      self.Layout_outerBox9 = QtWidgets.QVBoxLayout(self.outerBox9)
      self.Layout_outerBox9.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox9.setSpacing(2)
      self.Layout_outerBox9.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configLegendBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configLegendBox)
      self.Layout_outerBox9.addWidget(self.configLegendBox)
      self.Layout_configLegend = QtWidgets.QHBoxLayout(self.configLegendBox)
      self.Layout_configLegend.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      self.configLegendLabel = QPushButtonCheckable()
      self.configLegendLabel.setText('legend')
      self.configLegendLabel.setToolTip('Show figure legend')
      self.configLegendLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend.addWidget(self.configLegendLabel)
  
      self.configLegendPlacement = QComboBoxMac()
      self.configLegendPlacement.setToolTip('Placement of figure legend')
      self.configLegendPlacement.addItems(self.placementstyles)
      self.configLegendPlacement.setMaximumSize(QtCore.QSize(int(86 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPlacement.setMinimumSize(QtCore.QSize(int(86 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend.addWidget(self.configLegendPlacement)
  
      self.configLegendColor = {}; self.configLegendColorLabel = {}
      for prop in ['face', 'edge']:
        self.configLegendColorLabel[prop] = QPushButtonCheckable()
        self.configLegendColorLabel[prop].setText(prop)
        self.configLegendColorLabel[prop].setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        self.configLegendColorLabel[prop].setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
        if(prop == 'face'):
          self.configLegendColorLabel[prop].setToolTip('Fill face of figure legend')
        else:
          self.configLegendColorLabel[prop].setToolTip('Draw border of figure legend')
        self.Layout_configLegend.addWidget(self.configLegendColorLabel[prop])
        self.configLegendColor[prop] = QPushButtonColor()
        self.configLegendColor[prop].setToolTip(prop.title() + ' color of figure legend')
        self.configLegendColor[prop].setAutoFillBackground(False)
        self.configLegendColor[prop].setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.configLegendColor[prop].setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
        self.configLegendColor[prop].setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
        self.Layout_configLegend.addWidget(self.configLegendColor[prop])
  
      self.configLegendEdgeWidthLabel = QtWidgets.QLabel('width')
      self.configLegendEdgeWidthLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendEdgeWidthLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend.addWidget(self.configLegendEdgeWidthLabel)
      self.configLegendEdgeWidth = QDoubleSpinClick()
      self.Layout_configLegend.addWidget(self.configLegendEdgeWidth)
      self.configLegendEdgeWidth.setMinimum(0.0)
      self.configLegendEdgeWidth.setMaximum(100.0)
      self.configLegendEdgeWidth.setToolTip('Edge width of figure legend')
      self.configLegendEdgeWidth.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendEdgeWidth.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend.addStretch()

      # config for variable positioning of legend
      self.configLegendBox1 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configLegendBox1)
      ###self.vLayout.addWidget(self.configLegendBox1)
      self.Layout_outerBox9.addWidget(self.configLegendBox1)
      self.Layout_configLegend1 = QtWidgets.QHBoxLayout(self.configLegendBox1)
      self.Layout_configLegend1.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend1.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend1.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend1.addWidget(spacer)
      
      self.configLegendXLabel = QtWidgets.QLabel('x')
      self.configLegendXLabel.setObjectName('advanced')
      self.configLegendXLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendXLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend1.addWidget(self.configLegendXLabel)
      self.configLegendX = QLineEditClick()
      self.configLegendX.setToolTip('Custom x position of legend\n(only works if legend position is set to \'custom\')')
      self.configLegendX.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendX.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendX.setValidator(self.validFloat)
      self.Layout_configLegend1.addWidget(self.configLegendX)

      self.configLegendYLabel = QtWidgets.QLabel('y')
      self.configLegendYLabel.setObjectName('advanced')
      self.configLegendYLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendYLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend1.addWidget(self.configLegendYLabel)
      self.configLegendY = QLineEditClick()
      self.configLegendY.setToolTip('Custom y position of legend\n(only works if legend position is set to \'custom\')')
      self.configLegendY.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendY.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendY.setValidator(self.validFloat)
      self.Layout_configLegend1.addWidget(self.configLegendY)

      self.configLegendLinespacingLabel = QtWidgets.QLabel('linespacing')
      self.configLegendLinespacingLabel.setObjectName('advanced')
      self.configLegendLinespacingLabel.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLinespacingLabel.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend1.addWidget(self.configLegendLinespacingLabel)
  
      self.configLegendLinespacing = QLineEditClick()
      self.configLegendLinespacing.setToolTip('Linespacing of of figure legend entries')
      self.configLegendLinespacing.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLinespacing.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLinespacing.setValidator(self.validFloat)
      self.Layout_configLegend1.addWidget(self.configLegendLinespacing)

      self.configLegendBox2 = QWidgetMac()
      ###self.vLayout.addWidget(self.configLegendBox2)
      self.Layout_outerBox9.addWidget(self.configLegendBox2)
      self.Layout_configLegend2 = QtWidgets.QHBoxLayout(self.configLegendBox2)
      self.Layout_configLegend2.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend2.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend2.addWidget(spacer)
      
      self.configLegendSizeLabel = QtWidgets.QLabel('font')
      self.configLegendSizeLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendSizeLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend2.addWidget(self.configLegendSizeLabel)
      self.configLegendLabelColor = QPushButtonColor()
      self.configLegendLabelColor.setToolTip('Font color of figure legend')
      self.configLegendLabelColor.setAutoFillBackground(False)
      self.configLegendLabelColor.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLegendLabelColor.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLegendLabelColor.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configLegend2.addWidget(self.configLegendLabelColor)
  
      self.configLegendLabelSize = QDoubleSpinClick()
      self.Layout_configLegend2.addWidget(self.configLegendLabelSize)
      self.configLegendLabelSize.setMinimum(0.0)
      self.configLegendLabelSize.setMaximum(100.0)
      self.configLegendLabelSize.setToolTip('Font size of figure legend')
      self.configLegendLabelSize.setMaximumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLabelSize.setMinimumSize(QtCore.QSize(int(52 * SCALEFONT), int(BASE_SIZE)))
  
      self.configLegendLabelBold = QPushButtonCheckable()
      self.configLegendLabelBold.setText('B')
      self.configLegendLabelBold.setToolTip('Font weight of figure legend')
      self.configLegendLabelBold.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLegendLabelBold.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configLegendLabelBold.font()
      useFont.setBold(True)
      self.configLegendLabelBold.setFont(useFont)
      self.Layout_configLegend2.addWidget(self.configLegendLabelBold)
        
      self.configLegendLabelItalic = QPushButtonCheckable()
      self.configLegendLabelItalic.setText('I')
      self.configLegendLabelItalic.setToolTip('Font style of figure legend')
      self.configLegendLabelItalic.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLegendLabelItalic.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      useFont = self.configLegendLabelItalic.font()
      useFont.setItalic(True)
      self.configLegendLabelItalic.setFont(useFont)
      self.Layout_configLegend2.addWidget(self.configLegendLabelItalic)
  
      self.configLegendLabelFont = FontoBox()
      self.configLegendLabelFont.setToolTip('Font of figure legend')
      self.configLegendLabelFont.addItems(items=self.parent.fontNames)
      self.configLegendLabelFont.setMaximumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLabelFont.setMinimumSize(QtCore.QSize(int(188 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend2.addWidget(self.configLegendLabelFont)
  
      self.configLegendBox6 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configLegendBox6)
      ###self.vLayout.addWidget(self.configLegendBox6)
      self.Layout_outerBox9.addWidget(self.configLegendBox6)
      self.Layout_configLegend6 = QtWidgets.QHBoxLayout(self.configLegendBox6)
      self.Layout_configLegend6.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend6.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend6.addWidget(spacer)

      self.configLegendShadowLabel = QPushButtonCheckable()
      self.configLegendShadowLabel.setText('shadow')
      self.configLegendShadowLabel.setToolTip('Show shadow of figure legend')
      self.configLegendShadowLabel.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowLabel.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend6.addWidget(self.configLegendShadowLabel)
      
      self.configLegendShadowColor = QPushButtonColor()
      self.configLegendShadowColor.setToolTip('Color of shadow of figure legend')
      self.configLegendShadowColor.setAutoFillBackground(False)
      self.configLegendShadowColor.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLegendShadowColor.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configLegendShadowColor.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configLegend6.addWidget(self.configLegendShadowColor)
      
      self.configLegendShadowOffXLabel = QtWidgets.QLabel('offX')
      self.configLegendShadowOffXLabel.setObjectName('advanced')
      self.configLegendShadowOffXLabel.setMinimumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowOffXLabel.setMaximumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend6.addWidget(self.configLegendShadowOffXLabel)
      self.configLegendShadowOffX = QLineEditClick()
      self.configLegendShadowOffX.setToolTip('Offset x of shadow of figure legend')
      self.configLegendShadowOffX.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowOffX.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowOffX.setValidator(self.validFloat)
      self.Layout_configLegend6.addWidget(self.configLegendShadowOffX)
      
      self.configLegendShadowOffYLabel = QtWidgets.QLabel('offY')
      self.configLegendShadowOffYLabel.setObjectName('advanced')
      self.configLegendShadowOffYLabel.setMinimumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowOffYLabel.setMaximumSize(QtCore.QSize(int(22 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend6.addWidget(self.configLegendShadowOffYLabel)
      self.configLegendShadowOffY = QLineEditClick()
      self.configLegendShadowOffY.setToolTip('Offset y of shadow of figure legend')
      self.configLegendShadowOffY.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowOffY.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendShadowOffY.setValidator(self.validFloat)
      self.Layout_configLegend6.addWidget(self.configLegendShadowOffY)
      self.Layout_configLegend6.addStretch()
  
      self.configLegendBox5 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configLegendBox5)
      ###self.vLayout.addWidget(self.configLegendBox5)
      self.Layout_outerBox9.addWidget(self.configLegendBox5)
      self.Layout_configLegend5 = QtWidgets.QHBoxLayout(self.configLegendBox5)
      self.Layout_configLegend5.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend5.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend5.addWidget(spacer)

      self.configLegendHatchLabel = QtWidgets.QLabel('hatch')
      self.configLegendHatchLabel.setObjectName('advanced')
      self.configLegendHatchLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendHatchLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend5.addWidget(self.configLegendHatchLabel)

      self.hatchstyles = ['', '/', '|', '-', '+', 'x', 'o', 'O', '.', '*']
      self.hatchMultiplystyles = [str(i) for i in range(1, 11)]
      self.legendComboHatchStyle = QComboBoxMac()
      self.legendComboHatchStyle.setToolTip('Hatch style of legend')
      for entry in self.hatchstyles:
        self.legendComboHatchStyle.addItem(entry)
      self.legendComboHatchStyle.setMaximumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.legendComboHatchStyle.setMinimumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend5.addWidget(self.legendComboHatchStyle)
      self.legendComboHatchMultiplyStyle = QComboBoxMac()
      self.legendComboHatchMultiplyStyle.setToolTip('Hatch repeat of legend')
      for entry in self.hatchMultiplystyles:
        self.legendComboHatchMultiplyStyle.addItem(str(entry))
      self.legendComboHatchMultiplyStyle.setMaximumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.legendComboHatchMultiplyStyle.setMinimumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend5.addWidget(self.legendComboHatchMultiplyStyle)

      # hatch line config
      self.legendHatchLineWidthLabel = QtWidgets.QLabel('# line')
      self.legendHatchLineWidthLabel.setObjectName('advanced')
      self.legendHatchLineWidthLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.legendHatchLineWidthLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend5.addWidget(self.legendHatchLineWidthLabel)
      # hatch line width spin box
      self.legendHatchLineWidthSpin = QDoubleSpinClick()
      self.Layout_configLegend5.addWidget(self.legendHatchLineWidthSpin)
      self.legendHatchLineWidthSpin.setMinimum(0.0)
      self.legendHatchLineWidthSpin.setMaximum(100.0)
      self.legendHatchLineWidthSpin.setToolTip('Hatch line width of legend')
      self.legendHatchLineWidthSpin.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.legendHatchLineWidthSpin.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))

      self.configLegendRoundingLabel = QtWidgets.QLabel('rounding')
      self.configLegendRoundingLabel.setObjectName('advanced')
      self.configLegendRoundingLabel.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendRoundingLabel.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend5.addWidget(self.configLegendRoundingLabel)
      self.configLegendRounding = QLineEditClick()
      self.configLegendRounding.setToolTip('Box rounding of figure legend')
      self.configLegendRounding.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendRounding.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendRounding.setValidator(self.validFloat)
      self.Layout_configLegend5.addWidget(self.configLegendRounding)
      self.Layout_configLegend5.addStretch()

      self.configLegendBox3 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configLegendBox3)
      ###self.vLayout.addWidget(self.configLegendBox3)
      self.Layout_outerBox9.addWidget(self.configLegendBox3)
      self.Layout_configLegend3 = QtWidgets.QHBoxLayout(self.configLegendBox3)
      self.Layout_configLegend3.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend3.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(spacer)
      
      self.configLegendNColLabel = QtWidgets.QLabel('#cols')
      self.configLegendNColLabel.setObjectName('advanced')
      self.configLegendNColLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendNColLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(self.configLegendNColLabel)
      self.configLegendNCol = QComboBoxMac()
      self.configLegendNCol.setToolTip('Number of columns in figure legend')
      for entry in [str(i + 1) for i in range(10)]:
        self.configLegendNCol.addItem(str(entry))
      self.configLegendNCol.setMaximumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendNCol.setMinimumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(self.configLegendNCol)
  
      self.configLegendMarkerFirstLabel = QPushButtonCheckable()
      self.configLegendMarkerFirstLabel.setText('mark. 1st')
      self.configLegendMarkerFirstLabel.setToolTip('Draw markers first in figure legend')
      self.configLegendMarkerFirstLabel.setMaximumSize(QtCore.QSize(int(62 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendMarkerFirstLabel.setMinimumSize(QtCore.QSize(int(62 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(self.configLegendMarkerFirstLabel)
      # disable this control on Linux platforms
      if(platform in ['linux', 'darwin']):
        self.configLegendMarkerFirstLabel.hide()
  
      self.configLegendMarkerScaleLabel = QtWidgets.QLabel('scale')
      self.configLegendMarkerScaleLabel.setObjectName('advanced')
      self.configLegendMarkerScaleLabel.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendMarkerScaleLabel.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(self.configLegendMarkerScaleLabel)
      self.configLegendMarkerScale = QLineEditClick()
      self.configLegendMarkerScale.setToolTip('Scale factor of markers in figure legend')
      self.configLegendMarkerScale.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendMarkerScale.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendMarkerScale.setValidator(self.validFloat)
      self.Layout_configLegend3.addWidget(self.configLegendMarkerScale)
  
      self.configLegendMarkerNumPointsLabel = QtWidgets.QLabel('#pts')
      self.configLegendMarkerNumPointsLabel.setObjectName('advanced')
      self.configLegendMarkerNumPointsLabel.setMaximumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendMarkerNumPointsLabel.setMinimumSize(QtCore.QSize(int(24 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(self.configLegendMarkerNumPointsLabel)
      self.configLegendMarkerNumPoints = QComboBoxMac()
      self.configLegendMarkerNumPoints.setToolTip('Number of markers in figure legend')
      for entry in [str(i + 1) for i in range(10)]:
        self.configLegendMarkerNumPoints.addItem(str(entry))
      self.configLegendMarkerNumPoints.setMaximumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendMarkerNumPoints.setMinimumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend3.addWidget(self.configLegendMarkerNumPoints)
  
      self.configLegendBox4 = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configLegendBox4)
      ###self.vLayout.addWidget(self.configLegendBox4)
      self.Layout_outerBox9.addWidget(self.configLegendBox4)
      self.Layout_configLegend4 = QtWidgets.QHBoxLayout(self.configLegendBox4)
      self.Layout_configLegend4.setContentsMargins(0, 0, 0, 0)
      self.Layout_configLegend4.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configLegend4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend4.addWidget(spacer)
      
      self.configLegendPadLabel = QtWidgets.QLabel('pad')
      self.configLegendPadLabel.setObjectName('advanced')
      self.configLegendPadLabel.setMaximumSize(QtCore.QSize(int(18 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadLabel.setMinimumSize(QtCore.QSize(int(18 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend4.addWidget(self.configLegendPadLabel)
      self.configLegendPadBorderLabel = QtWidgets.QLabel('border')
      self.configLegendPadBorderLabel.setObjectName('advanced')
      self.configLegendPadBorderLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadBorderLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend4.addWidget(self.configLegendPadBorderLabel)
      self.configLegendPadBorder = QLineEditClick()
      self.configLegendPadBorder.setToolTip('Border padding in figure legend')
      self.configLegendPadBorder.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadBorder.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadBorder.setValidator(self.validFloat)
      self.Layout_configLegend4.addWidget(self.configLegendPadBorder)
      
      self.configLegendPadRowLabel = QtWidgets.QLabel('rows')
      self.configLegendPadRowLabel.setObjectName('advanced')
      self.configLegendPadRowLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadRowLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend4.addWidget(self.configLegendPadRowLabel)
      self.configLegendPadRow = QLineEditClick()
      self.configLegendPadRow.setToolTip('Row padding in figure legend')
      self.configLegendPadRow.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadRow.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadRow.setValidator(self.validFloat)
      self.Layout_configLegend4.addWidget(self.configLegendPadRow)
      
      self.configLegendPadColLabel = QtWidgets.QLabel('cols')
      self.configLegendPadColLabel.setObjectName('advanced')
      self.configLegendPadColLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadColLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend4.addWidget(self.configLegendPadColLabel)
      self.configLegendPadCol = QLineEditClick()
      self.configLegendPadCol.setToolTip('Column padding in figure legend')
      self.configLegendPadCol.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadCol.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendPadCol.setValidator(self.validFloat)
      self.Layout_configLegend4.addWidget(self.configLegendPadCol)

      self.configLegendLabelPadLabel = QtWidgets.QLabel('labels')
      self.configLegendLabelPadLabel.setObjectName('advanced')
      self.configLegendLabelPadLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLabelPadLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configLegend4.addWidget(self.configLegendLabelPadLabel)
      self.configLegendLabelPad = QLineEditClick()
      self.configLegendLabelPad.setToolTip('Row padding in figure legend')
      self.configLegendLabelPad.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLabelPad.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configLegendLabelPad.setValidator(self.validFloat)
      self.Layout_configLegend4.addWidget(self.configLegendLabelPad)
      
    # canvas config
    if(not self.secondAxes):
      blah = HLine()
      ###self.vLayout.addWidget(blah)
      
      self.outerBox10 = QWidgetMac()
      self.outerBox10.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox10)
      self.Layout_outerBox10 = QtWidgets.QVBoxLayout(self.outerBox10)
      self.Layout_outerBox10.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox10.setSpacing(2)
      self.Layout_outerBox10.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configCanvasBox = QWidgetMac()
      ###self.vLayout.addWidget(self.configCanvasBox)
      self.Layout_outerBox10.addWidget(self.configCanvasBox)
      self.Layout_configCanvas = QtWidgets.QHBoxLayout(self.configCanvasBox)
      self.Layout_configCanvas.setContentsMargins(0, 0, 0, 0)
      self.Layout_configCanvas.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configCanvas.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      self.configFigureLabel = QPushButtonCheckable()
      self.configFigureLabel.setText('figure')
      self.configFigureLabel.setToolTip('Show figure background')
      self.configFigureLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configFigureLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configCanvas.addWidget(self.configFigureLabel)
      self.configFigureColorButton = QPushButtonColor()
      self.configFigureColorButton.setToolTip('Color of figure background')
      self.configFigureColorButton.setAutoFillBackground(False)
      self.configFigureColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configFigureColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configFigureColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configCanvas.addWidget(self.configFigureColorButton)
  
      self.configCanvasLabel = QPushButtonCheckable()
      self.configCanvasLabel.setText('canvas')
      self.configCanvasLabel.setToolTip('Show figure canvas')
      self.configCanvasLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configCanvas.addWidget(self.configCanvasLabel)
      self.configCanvasColorButton = QPushButtonColor()
      self.configCanvasColorButton.setToolTip('Color of figure canvas')
      self.configCanvasColorButton.setAutoFillBackground(False)
      self.configCanvasColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configCanvasColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configCanvasColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configCanvas.addWidget(self.configCanvasColorButton)

      # canvas gradient
      self.canvasGradientBox = QWidgetMac()
      ###self.vLayout.addWidget(self.canvasGradientBox)
      self.Layout_outerBox10.addWidget(self.canvasGradientBox)
      self.Layout_canvasGradient = QtWidgets.QHBoxLayout(self.canvasGradientBox)
      self.Layout_canvasGradient.setContentsMargins(0, 0, 0, 0)
      self.Layout_canvasGradient.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_canvasGradient.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      self.advancedGraphicsSettings.append(self.canvasGradientBox)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_canvasGradient.addWidget(spacer)

      self.configCanvasGradientLabel = QPushButtonCheckable()
      self.configCanvasGradientLabel.setText('gradient')
      self.configCanvasGradientLabel.setToolTip('Show gradient on figure canvas')
      self.configCanvasGradientLabel.setMaximumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientLabel.setMinimumSize(QtCore.QSize(int(56 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientLabel)

      self.configCanvasGradientStyle = QComboBoxMac()
      self.configCanvasGradientStyle.setToolTip('Style of gradient on figure canvas')
      self.configCanvasGradientStyle.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientStyle.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      for entry in ['linear', 'radial']:
        self.configCanvasGradientStyle.addItem(str(entry))
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientStyle)

      self.configCanvasGradientColor1Button = QPushButtonColor()
      self.configCanvasGradientColor1Button.setToolTip('Color #1 of gradient on figure canvas')
      self.configCanvasGradientColor1Button.setAutoFillBackground(False)
      self.configCanvasGradientColor1Button.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configCanvasGradientColor1Button.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configCanvasGradientColor1Button.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientColor1Button)  
      self.configCanvasGradientColor2Button = QPushButtonColor()
      self.configCanvasGradientColor2Button.setToolTip('Color #2 of gradient on figure canvas')
      self.configCanvasGradientColor2Button.setAutoFillBackground(False)
      self.configCanvasGradientColor2Button.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configCanvasGradientColor2Button.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configCanvasGradientColor2Button.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientColor2Button)  

      self.configCanvasGradientAngleLabel = QtWidgets.QLabel('angle')
      self.configCanvasGradientAngleLabel.setObjectName('advanced')
      self.configCanvasGradientAngleLabel.setMaximumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientAngleLabel.setMinimumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientAngleLabel)
      self.configCanvasGradientAngle = QDoubleSpinClick()
      self.configCanvasGradientAngle.setToolTip('Angle of gradient on figure canvas')
      self.configCanvasGradientAngle.setMinimum(0.0)
      self.configCanvasGradientAngle.setMaximum(360.0)
      self.configCanvasGradientAngle.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientAngle.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientAngle.setSingleStep(10)
      self.configCanvasGradientAngle.setWrapping(True)
      self.configCanvasGradientAngle.setAdaptive(False)
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientAngle)

      self.configCanvasGradientCenterLabel = QtWidgets.QLabel('center')
      self.configCanvasGradientCenterLabel.setObjectName('advanced')
      self.configCanvasGradientCenterLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientCenterLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientCenterLabel)
      self.configCanvasGradientCenterX = QLineEditClick()
      self.configCanvasGradientCenterX.setToolTip('Center x position of gradient on figure canvas')
      self.configCanvasGradientCenterX.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientCenterX.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientCenterX.setValidator(self.validFloat)
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientCenterX)
      self.configCanvasGradientCenterY = QLineEditClick()
      self.configCanvasGradientCenterY.setToolTip('Center y position of gradient on figure canvas')
      self.configCanvasGradientCenterY.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientCenterY.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientCenterY.setValidator(self.validFloat)
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientCenterY)
      
      self.configCanvasGradientWidthLabel = QtWidgets.QLabel('width')
      self.configCanvasGradientWidthLabel.setObjectName('advanced')
      self.configCanvasGradientWidthLabel.setMaximumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientWidthLabel.setMinimumSize(QtCore.QSize(int(26 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientWidthLabel)
      self.configCanvasGradientWidth = QLineEditClick()
      self.configCanvasGradientWidth.setToolTip('Width of gradient on figure canvas')
      self.configCanvasGradientWidth.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientWidth.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.configCanvasGradientWidth.setValidator(self.validFloat)
      self.Layout_canvasGradient.addWidget(self.configCanvasGradientWidth)

      # canvas dimensions
      self.exportSizeBox = QWidgetMac()
      ###self.vLayout.addWidget(self.exportSizeBox)
      self.Layout_outerBox10.addWidget(self.exportSizeBox)
      self.Layout_exportSize = QtWidgets.QHBoxLayout(self.exportSizeBox)
      self.Layout_exportSize.setContentsMargins(0, 0, 0, 0)
      self.Layout_exportSize.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_exportSize.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportSize.addWidget(spacer)
      
      self.exportSizeBaseLabel = QtWidgets.QLabel('fig.')
      self.exportSizeBaseLabel.setMaximumSize(QtCore.QSize(int(18 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeBaseLabel.setMinimumSize(QtCore.QSize(int(18 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportSize.addWidget(self.exportSizeBaseLabel)
      self.exportSizeXLabel = QtWidgets.QLabel('width')
      self.exportSizeXLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeXLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportSize.addWidget(self.exportSizeXLabel)
      self.exportSizeX = QLineEditClick()
      self.exportSizeX.setToolTip('Width of exported figure in inches')
      self.exportSizeX.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeX.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeX.setValidator(self.validFloat)
      self.Layout_exportSize.addWidget(self.exportSizeX)
      self.exportSizeYLabel = QtWidgets.QLabel('height')
      self.exportSizeYLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeYLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportSize.addWidget(self.exportSizeYLabel)
      self.exportSizeY = QLineEditClick()
      self.exportSizeY.setToolTip('Height of exported figure in inches')
      self.exportSizeY.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeY.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeY.setValidator(self.validFloat)
      self.Layout_exportSize.addWidget(self.exportSizeY)
      self.exportSizeCurrentButton = QPushButtonMac()
      self.exportSizeCurrentButton.setText('Use screen')
      self.exportSizeCurrentButton.setToolTip('Use current dimensions on screen for exporting figure')
      self.exportSizeCurrentButton.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      self.exportSizeCurrentButton.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportSize.addWidget(self.exportSizeCurrentButton)
      
      # pad graphics
      self.exportPadBox = QWidgetMac()
      ###self.vLayout.addWidget(self.exportPadBox)
      self.Layout_outerBox10.addWidget(self.exportPadBox)
      self.Layout_exportPad = QtWidgets.QHBoxLayout(self.exportPadBox)
      self.Layout_exportPad.setContentsMargins(0, 0, 0, 0)
      self.Layout_exportPad.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_exportPad.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(4 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportPad.addWidget(spacer)
  
      self.exportPadLabelMain = QtWidgets.QLabel('pad')
      self.exportPadLabelMain.setMaximumSize(QtCore.QSize(int(18 * SCALEFONT), int(BASE_SIZE)))
      self.exportPadLabelMain.setMinimumSize(QtCore.QSize(int(18 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_exportPad.addWidget(self.exportPadLabelMain)
  
      self.exportPadLabel = {}; self.exportPadEntry = {}
      for axis in ['bottom', 'top', 'left', 'right']:
        self.exportPadLabel[axis] = QtWidgets.QLabel(axis)
        self.exportPadLabel[axis].setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
        self.exportPadLabel[axis].setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
        self.Layout_exportPad.addWidget(self.exportPadLabel[axis])
  
        self.exportPadEntry[axis] = QLineEditClick()
        self.exportPadEntry[axis].setToolTip('Pad ' + axis + ' of figure')
        self.exportPadEntry[axis].setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
        self.exportPadEntry[axis].setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
        self.exportPadEntry[axis].setValidator(self.validFloat)
        self.Layout_exportPad.addWidget(self.exportPadEntry[axis])
      
      # format frame
      self.configFrameBox = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configFrameBox)
      ###self.vLayout.addWidget(self.configFrameBox)
      self.Layout_outerBox10.addWidget(self.configFrameBox)
      self.Layout_configFrame = QtWidgets.QHBoxLayout(self.configFrameBox)
      self.Layout_configFrame.setContentsMargins(0, 0, 0, 0)
      self.Layout_configFrame.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configFrame.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
      
      self.configFrameLabel = QPushButtonCheckable()
      self.configFrameLabel.setText('frame')
      self.configFrameLabel.setToolTip('Show figure frame')
      self.configFrameLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configFrameLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configFrame.addWidget(self.configFrameLabel)
      
      self.configFrameColor = QPushButtonColor()
      self.configFrameColor.setToolTip('Color of figure frame')
      self.configFrameColor.setAutoFillBackground(False)
      self.configFrameColor.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configFrameColor.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configFrameColor.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configFrame.addWidget(self.configFrameColor)

      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configFrame.addWidget(spacer)
    
      self.configFrameWidthLabel = QtWidgets.QLabel('width')
      self.configFrameWidthLabel.setObjectName('advanced')
      self.configFrameWidthLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configFrameWidthLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configFrame.addWidget(self.configFrameWidthLabel)
      self.configFrameWidth = QDoubleSpinClick()
      self.Layout_configFrame.addWidget(self.configFrameWidth)
      self.configFrameWidth.setMinimum(0.0)
      self.configFrameWidth.setMaximum(100.0)
      self.configFrameWidth.setToolTip('Line width of figure frame')
      self.configFrameWidth.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configFrameWidth.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
  
      self.configFrameStyle = QComboBoxMac()
      self.configFrameStyle.setToolTip('Line style of figure frame')
      self.configFrameStyle.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.configFrameStyle.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configFrame.addWidget(self.configFrameStyle)
  
      self.configFrameDashStyle = QComboBoxMac()
      self.configFrameDashStyle.setToolTip('Cap style of figure frame')
      self.configFrameDashStyle.setMinimumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.configFrameDashStyle.setMaximumSize(QtCore.QSize(int(70 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configFrame.addWidget(self.configFrameDashStyle)
    
    # xkcdify
    if(not self.secondAxes):
      blah = HLine()
      ###self.vLayout.addWidget(blah)
      
      self.outerBox11 = QWidgetMac()
      self.outerBox11.setObjectName(outTypes[outCount % 2])
      self.advancedGraphicsSettings.append(self.outerBox11)
      outCount += 1
      self.vLayout.addWidget(self.outerBox11)
      self.Layout_outerBox11 = QtWidgets.QVBoxLayout(self.outerBox11)
      self.Layout_outerBox11.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox11.setSpacing(2)
      self.Layout_outerBox11.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configXkcdBox = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configXkcdBox)
      ###self.vLayout.addWidget(self.configXkcdBox)
      self.Layout_outerBox11.addWidget(self.configXkcdBox)
      self.Layout_configXkcd = QtWidgets.QHBoxLayout(self.configXkcdBox)
      self.Layout_configXkcd.setContentsMargins(0, 0, 0, 0)
      self.Layout_configXkcd.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configXkcd.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configXkcdLabel = QPushButtonCheckable()
      self.configXkcdLabel.setText('xkcdify')
      self.configXkcdLabel.setToolTip('Use xkcd effects')
      self.configXkcdLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configXkcdLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configXkcd.addWidget(self.configXkcdLabel)
  
      self.xkcdScaleLabel = QtWidgets.QLabel('scale')
      self.xkcdScaleLabel.setObjectName('advanced')
      self.xkcdScaleLabel.setMaximumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdScaleLabel.setMinimumSize(QtCore.QSize(int(32 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configXkcd.addWidget(self.xkcdScaleLabel)
      self.xkcdScale = QLineEditClick()
      self.xkcdScale.setToolTip('Amplitude of xkcd effects')
      self.xkcdScale.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdScale.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdScale.setValidator(self.validFloat)
      self.Layout_configXkcd.addWidget(self.xkcdScale)
  
      self.xkcdLengthLabel = QtWidgets.QLabel('length')
      self.xkcdLengthLabel.setObjectName('advanced')
      self.xkcdLengthLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdLengthLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configXkcd.addWidget(self.xkcdLengthLabel)
      self.xkcdLength = QLineEditClick()
      self.xkcdLength.setToolTip('Length scale of xkcd effects')
      self.xkcdLength.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdLength.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdLength.setValidator(self.validFloat)
      self.Layout_configXkcd.addWidget(self.xkcdLength)
  
      self.xkcdRandomLabel = QtWidgets.QLabel('random')
      self.xkcdRandomLabel.setObjectName('advanced')
      self.xkcdRandomLabel.setMaximumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdRandomLabel.setMinimumSize(QtCore.QSize(int(36 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configXkcd.addWidget(self.xkcdRandomLabel)
      self.xkcdRandom = QLineEditClick()
      self.xkcdRandom.setToolTip('Randomness of xkcd effects')
      self.xkcdRandom.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdRandom.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.xkcdRandom.setValidator(self.validFloat)
      self.Layout_configXkcd.addWidget(self.xkcdRandom)
  
      # path effects -- stroke
      self.configPathEffectsBox = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configPathEffectsBox)
      ###self.vLayout.addWidget(self.configPathEffectsBox)
      self.Layout_outerBox11.addWidget(self.configPathEffectsBox)
      self.Layout_configPathEffects = QtWidgets.QHBoxLayout(self.configPathEffectsBox)
      self.Layout_configPathEffects.setContentsMargins(0, 0, 0, 0)
      self.Layout_configPathEffects.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configPathEffects.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configPathEffectsLabel = QPushButtonCheckable()
      self.configPathEffectsLabel.setText('outline')
      self.configPathEffectsLabel.setToolTip('Use outline effects')
      self.configPathEffectsLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configPathEffectsLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathEffects.addWidget(self.configPathEffectsLabel)
  
      self.configPathEffectsColorButton = QPushButtonColor()
      self.configPathEffectsColorButton.setToolTip('Color of outline')
      self.configPathEffectsColorButton.setAutoFillBackground(False)
      self.configPathEffectsColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configPathEffectsColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configPathEffectsColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configPathEffects.addWidget(self.configPathEffectsColorButton)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathEffects.addWidget(spacer)
    
      self.configPathEffectsWidthLabel = QtWidgets.QLabel('width')
      self.configPathEffectsWidthLabel.setObjectName('advanced')
      self.configPathEffectsWidthLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configPathEffectsWidthLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathEffects.addWidget(self.configPathEffectsWidthLabel)

      self.configPathEffectsWidth = QDoubleSpinClick()
      self.Layout_configPathEffects.addWidget(self.configPathEffectsWidth)
      self.configPathEffectsWidth.setMinimum(0.0)
      self.configPathEffectsWidth.setMaximum(100.0)
      self.configPathEffectsWidth.setToolTip('Line width of outline')
      self.configPathEffectsWidth.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
      self.configPathEffectsWidth.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
   
      # path effects -- shadow
      self.configPathShadowBox = QWidgetMac()
      self.advancedGraphicsSettings.append(self.configPathShadowBox)
      ###self.vLayout.addWidget(self.configPathShadowBox)
      self.Layout_outerBox11.addWidget(self.configPathShadowBox)
      self.Layout_configPathShadow = QtWidgets.QHBoxLayout(self.configPathShadowBox)
      self.Layout_configPathShadow.setContentsMargins(0, 0, 0, 0)
      self.Layout_configPathShadow.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configPathShadow.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configPathShadowLabel = QPushButtonCheckable()
      self.configPathShadowLabel.setText('shadow')
      self.configPathShadowLabel.setToolTip('Use shadow effects')
      self.configPathShadowLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathShadow.addWidget(self.configPathShadowLabel)
  
      self.configPathShadowColorButton = QPushButtonColor()
      self.configPathShadowColorButton.setToolTip('Color of shadow')
      self.configPathShadowColorButton.setAutoFillBackground(False)
      self.configPathShadowColorButton.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configPathShadowColorButton.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
      self.configPathShadowColorButton.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
      self.Layout_configPathShadow.addWidget(self.configPathShadowColorButton)
  
      spacer = QtWidgets.QLabel()
      spacer.setMaximumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
      spacer.setMinimumSize(QtCore.QSize(int(7 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathShadow.addWidget(spacer)
    
      self.configPathShadowOffXLabel = QtWidgets.QLabel('offX')
      self.configPathShadowOffXLabel.setObjectName('advanced')
      self.configPathShadowOffXLabel.setMinimumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowOffXLabel.setMaximumSize(QtCore.QSize(int(33 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathShadow.addWidget(self.configPathShadowOffXLabel)
      self.configPathShadowOffX = QLineEditClick()
      self.configPathShadowOffX.setToolTip('Offset x of shadow')
      self.configPathShadowOffX.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowOffX.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowOffX.setValidator(self.validFloat)
      self.Layout_configPathShadow.addWidget(self.configPathShadowOffX)
   
      self.configPathShadowOffYLabel = QtWidgets.QLabel('offY')
      self.configPathShadowOffYLabel.setObjectName('advanced')
      self.configPathShadowOffYLabel.setMinimumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowOffYLabel.setMaximumSize(QtCore.QSize(int(30 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configPathShadow.addWidget(self.configPathShadowOffYLabel)
      self.configPathShadowOffY = QLineEditClick()
      self.configPathShadowOffY.setToolTip('Offset y of shadow')
      self.configPathShadowOffY.setMaximumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowOffY.setMinimumSize(QtCore.QSize(int(34 * SCALEFONT), int(BASE_SIZE)))
      self.configPathShadowOffY.setValidator(self.validFloat)
      self.Layout_configPathShadow.addWidget(self.configPathShadowOffY)

    # additional button to control visibility of advanced graphics settings
    if(not self.secondAxes):
      blah = HLine()
      ###self.advancedGraphicsSettings.append(blah)
      ###self.vLayout.addWidget(blah)
      
      self.outerBox12 = QWidgetMac()
      self.outerBox12.setObjectName(outTypes[outCount % 2])
      outCount += 1
      self.vLayout.addWidget(self.outerBox12)
      self.Layout_outerBox12 = QtWidgets.QVBoxLayout(self.outerBox12)
      self.Layout_outerBox12.setContentsMargins(2, 4, 2, 4)
      self.Layout_outerBox12.setSpacing(2)
      self.Layout_outerBox12.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configAdvancedSettings = QWidgetMac()
      ###self.vLayout.addWidget(self.configAdvancedSettings)
      self.Layout_outerBox12.addWidget(self.configAdvancedSettings)
      self.Layout_configAdvancedSettings = QtWidgets.QHBoxLayout(self.configAdvancedSettings)
      self.Layout_configAdvancedSettings.setContentsMargins(0, 0, 0, 0)
      self.Layout_configAdvancedSettings.setSpacing(int(-2 + 8 * SCALEFONT))
      self.Layout_configAdvancedSettings.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.configAdvancedSettingsLabel = QtWidgets.QLabel('Adv. settings')
      self.configAdvancedSettingsLabel.setMaximumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.configAdvancedSettingsLabel.setMinimumSize(QtCore.QSize(int(66 * SCALEFONT), int(BASE_SIZE)))
      self.Layout_configAdvancedSettings.addWidget(self.configAdvancedSettingsLabel)
      
      self.configAdvancedSettingsButton = QPushButtonCheckable()
      self.configAdvancedSettingsButton.setText('show')
      self.configAdvancedSettingsButton.setMinimumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configAdvancedSettingsButton.setMaximumSize(QtCore.QSize(int(40 * SCALEFONT), int(BASE_SIZE)))
      self.configAdvancedSettingsButton.setToolTip('Display advanced graphics options')
      self.Layout_configAdvancedSettings.addWidget(self.configAdvancedSettingsButton)
      
    self.vLayout.addStretch()
 
    # export graphics
    if(not self.secondAxes):
      self.exportBox = QWidgetMac()
      self.vLayout_0.addWidget(self.exportBox)
      self.Layout_export = QtWidgets.QHBoxLayout(self.exportBox)
      self.Layout_export.setContentsMargins(0, 0, 0, 0)
      self.Layout_export.setSpacing(2)
      self.Layout_export.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)

      self.previewButton = QPushButtonMac()
      self.previewButton.setText(' Preview')
      self.previewButton.setToolTip('Display plot graphics preview\n(F5)')
      self.previewButton.setMaximumHeight(int(BASE_SIZE))
      self.previewButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      self.previewButton.setIcon(FOM_ICON_DESKTOP)
      self.Layout_export.addWidget(self.previewButton, stretch=1)

      self.exportButton = QPushButtonMac()
      self.exportButton.setText(' Graphics')
      self.exportButton.setToolTip('Export graphics in bitmapped and vector formats\n(Ctrl-P)')
      self.exportButton.setMaximumHeight(int(BASE_SIZE))
      self.exportButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      self.exportButton.setIcon(FOM_ICON_ARROW_RIGHT)
      self.Layout_export.addWidget(self.exportButton, stretch=1)

      self.reportButton = QPushButtonMac()
      self.reportButton.setText(' Report')
      self.reportButton.setToolTip('Compile report of current graphics and fit results\n(Ctrl-G or F3)')
      self.reportButton.setMaximumHeight(int(BASE_SIZE))
      self.reportButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      self.reportButton.setIcon(FOM_ICON_ARROW_RIGHT)
      self.Layout_export.addWidget(self.reportButton, stretch=1)
  
      # load/save style
      self.loadStyleSet = QPushButtonMac()
      self.loadStyleSet.setText(' Open style')
      self.loadStyleSet.setToolTip('Load graphics style settings')
      self.loadStyleSet.setMaximumHeight(int(BASE_SIZE))
      self.loadStyleSet.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      self.loadStyleSet.setIcon(FOM_ICON_OPEN)
      self.Layout_export.addWidget(self.loadStyleSet, stretch=1)

      self.saveStyleSet = QPushButtonMac()
      self.saveStyleSet.setText(' Save style')
      self.saveStyleSet.setToolTip('Save current graphics style settings to file')
      self.saveStyleSet.setMaximumHeight(int(BASE_SIZE))
      self.saveStyleSet.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
      self.saveStyleSet.setIcon(FOM_ICON_SAVE)
      self.Layout_export.addWidget(self.saveStyleSet, stretch=1)

    # set visibility of transformed axis settings
    if(not self.secondAxes):
      if(self.slavedXLabel.isChecked()):
        for entry in self.toggleItemsTransformed:
          if((not (entry in self.advancedGraphicsSettings)) or self.advancedExport['advancedGraphics']):
            entry.show()
      else:
        for entry in self.toggleItemsTransformed:
          entry.hide()

    # hide less frequent options
    for entry in self.advancedGraphicsSettings:
      if((not self.advancedExport['advancedGraphics']) and (hasattr(entry, 'hide'))):
        entry.hide()
        
        
  def visibilityCanvasGradientControls(self):
    # controls visibility of canvas control elements
    items1 = [self.configCanvasGradientAngleLabel, self.configCanvasGradientAngle]
    items2 = [self.configCanvasGradientCenterLabel, self.configCanvasGradientCenterX,
              self.configCanvasGradientCenterY, self.configCanvasGradientWidthLabel, self.configCanvasGradientWidth]

    # determine state
    if(self.configCanvasGradientStyle.currentText() == 'linear'):
      for entry in items1:
        entry.show()
      for entry in items2:
        entry.hide()
    else:
      for entry in items1:
        entry.hide()
      for entry in items2:
        entry.show()

  def updateFields(self, initialize=False):
    # updates all fields in entry mask
#    defaultFont = 'DejaVu Sans'
    # x label config
    if(not self.secondAxes):
      self.configXLabel.setChecked(self.parent.plotArea.labelXShow)
      self.configXName.setText(self.parent.plotArea.labelX)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.labelXColor[0:3]]
      self.configXColorButton.setMyColor(*colorvalue)
      self.configXSize.setValue(self.parent.plotArea.labelXSize)
      if(self.parent.plotArea.labelXWeight == 'bold'):
        self.configXBold.setChecked(True)
      else:
        self.configXBold.setChecked(False)
      if(self.parent.plotArea.labelXStyle == 'italic'):
        self.configXItalic.setChecked(True)
      else:
        self.configXItalic.setChecked(False)
      '''
      if(self.parent.plotArea.labelXVariant == 'small-caps'):
        self.configXVariant.setChecked(True)
      else:
        self.configXVariant.setChecked(False)
      '''
      self.configXFont.setMe(self.parent.plotArea.axisFont['x'])
      if(self.parent.plotArea.labelXAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.labelXAlignment)
        self.configXAlignment.setCurrentIndex(currindex)
      else:
        self.configXAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.labelXAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.labelXAlignmentVertical)
        self.configXAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configXAlignmentVertical.setCurrentIndex(0)
      self.configXLinespacing.setText(str(self.parent.plotArea.labelXLinespacing))
      self.configXPad.setText(str(self.parent.plotArea.labelXPad))
      self.configXPos.setText(str(self.parent.plotArea.labelXPos))
      self.configXAngle.setValue(self.parent.plotArea.labelXAngle)

    # y label config
    if(not self.secondAxes):
      self.configYLabel.setChecked(self.parent.plotArea.labelYShow)
      self.configYName.setText(self.parent.plotArea.labelY)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.labelYColor[0:3]]
      self.configYColorButton.setMyColor(*colorvalue)
      self.configYSize.setValue(self.parent.plotArea.labelYSize)
      if(self.parent.plotArea.labelYWeight == 'bold'):
        self.configYBold.setChecked(True)
      else:
        self.configYBold.setChecked(False)
      if(self.parent.plotArea.labelYStyle == 'italic'):
        self.configYItalic.setChecked(True)
      else:
        self.configYItalic.setChecked(False)
      '''
      if(self.parent.plotArea.labelYVariant == 'small-caps'):
        self.configYVariant.setChecked(True)
      else:
        self.configYVariant.setChecked(False)
      '''
      self.configYFont.setMe(self.parent.plotArea.axisFont['y'])
      if(self.parent.plotArea.labelYAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.labelYAlignment)
        self.configYAlignment.setCurrentIndex(currindex)
      else:
        self.configYAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.labelYAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.labelYAlignmentVertical)
        self.configYAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configYAlignmentVertical.setCurrentIndex(0)
      self.configYLinespacing.setText(str(self.parent.plotArea.labelYLinespacing))
      self.configYPad.setText(str(self.parent.plotArea.labelYPad))
      self.configYPos.setText(str(self.parent.plotArea.labelYPos))
      self.configYAngle.setValue(self.parent.plotArea.labelYAngle)
    else:
      self.configYLabel.setChecked(self.parent.plotArea.labelY2Show)
      self.configYName.setText(self.parent.plotArea.labelY2)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.labelY2Color[0:3]]
      self.configYColorButton.setMyColor(*colorvalue)
      self.configYSize.setValue(self.parent.plotArea.labelY2Size)
      if(self.parent.plotArea.labelY2Weight == 'bold'):
        self.configYBold.setChecked(True)
      else:
        self.configYBold.setChecked(False)
      if(self.parent.plotArea.labelY2Style == 'italic'):
        self.configYItalic.setChecked(True)
      else:
        self.configYItalic.setChecked(False)
      '''
      if(self.parent.plotArea.labelY2Variant == 'small-caps'):
        self.configYVariant.setChecked(True)
      else:
        self.configYVariant.setChecked(False)
      '''
      self.configYFont.setMe(self.parent.plotArea.axisFont['y2'])
      if(self.parent.plotArea.labelY2Alignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.labelY2Alignment)
        self.configYAlignment.setCurrentIndex(currindex)
      else:
        self.configYAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.labelY2AlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.labelY2AlignmentVertical)
        self.configYAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configYAlignmentVertical.setCurrentIndex(0)
      self.configYLinespacing.setText(str(self.parent.plotArea.labelY2Linespacing))
      self.configYPad.setText(str(self.parent.plotArea.labelY2Pad))
      self.configYPos.setText(str(self.parent.plotArea.labelY2Pos))
      self.configYAngle.setValue(self.parent.plotArea.labelY2Angle)

    # axis config
    if(not self.secondAxes):
      useAxes = ['bottom', 'top', 'left', 'right']
    else:
      useAxes = ['left2', 'right2']
    for axis in useAxes:
      self.configAxisLabel[axis].setChecked(self.parent.plotArea.axisVisible[axis])
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.axisColor[axis][0:3]]
      self.configAxisColor[axis].setMyColor(*colorvalue)
      self.configAxisWidth[axis].setValue(self.parent.plotArea.axisWidth[axis])
      if(initialize):
        for entry in self.linestyles2:
          self.configAxisStyle[axis].addItem(entry)
      if(self.parent.plotArea.axisStyle[axis] in self.linestyles2):
        currindex = self.linestyles2.index(self.parent.plotArea.axisStyle[axis])
        self.configAxisStyle[axis].setCurrentIndex(currindex)
      else:
        self.configAxisStyle[axis].setCurrentIndex(0)
      if(initialize):
        for entry in self.dashstyles:
          self.configAxisDashStyle[axis].addItem(entry)
      if(self.parent.plotArea.axisDashStyle[axis] in self.dashstyles):
        currindex = self.dashstyles.index(self.parent.plotArea.axisDashStyle[axis])
        self.configAxisDashStyle[axis].setCurrentIndex(currindex)
      else:
        self.configAxisDashStyle[axis].setCurrentIndex(0)
      if(initialize):
        for entry in self.positionstyles:
          self.configAxisPosition[axis].addItem(entry)
      if(self.parent.plotArea.axisPosition[axis] in self.positionstyles):
        currindex = self.positionstyles.index(self.parent.plotArea.axisPosition[axis])
        self.configAxisPosition[axis].setCurrentIndex(currindex)
      else:
        self.configAxisPosition[axis].setCurrentIndex(0)
      self.configAxisPositionValue[axis].setText(str(self.parent.plotArea.axisPositionValue[axis]))
      self.configAxisBoundLabel[axis].setChecked(self.parent.plotArea.axisBoundaryCheck[axis])
      self.configAxisBoundLower[axis].setText(str(self.parent.plotArea.axisBoundary[axis][0]))
      self.configAxisBoundUpper[axis].setText(str(self.parent.plotArea.axisBoundary[axis][1]))
        
    # arrow config
    if(self.secondAxes):
      useAxes = ['y2']
    else:
      useAxes = ['x', 'y']
    for axis in useAxes:
      self.configArrowLabel[axis].setChecked(self.parent.plotArea.arrowVisible[axis])
      self.configArrowLineColorLabel[axis].setChecked(self.parent.plotArea.arrowEdgeShow[axis])
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.arrowColor[axis][0:3]]
      self.configArrowLineColor[axis].setMyColor(*colorvalue)
      self.configArrowFillColorLabel[axis].setChecked(self.parent.plotArea.arrowFillShow[axis])
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.arrowFill[axis][0:3]]
      self.configArrowFillColor[axis].setMyColor(*colorvalue)
      self.configArrowLineWidth[axis].setValue(self.parent.plotArea.arrowEdge[axis])
      self.Zstyles = ['axis', 'front']
      if(initialize):
        for item in self.Zstyles:
          self.configArrowZ[axis].addItem(item)
      if(self.parent.plotArea.arrowZ[axis] in self.Zstyles):
        currindex = self.Zstyles.index(self.parent.plotArea.arrowZ[axis])
        self.configArrowZ[axis].setCurrentIndex(currindex)
      else:
        self.configArrowZ[axis].setCurrentIndex(0)        
      self.configArrowHeadLength[axis].setText(str(self.parent.plotArea.arrowHeadLength[axis]))
      self.configArrowHeadWidth[axis].setText(str(self.parent.plotArea.arrowHeadWidth[axis]))
      self.configArrowOverhang[axis].setText(str(self.parent.plotArea.arrowOverhang[axis]))
      self.configArrowOffset[axis].setText(str(self.parent.plotArea.arrowOffset[axis]))

    self.locationXstyles = ['bottom', 'top', 'both']
    self.locationYstyles = ['left', 'right', 'both']
    
    if(self.secondAxes):
      if(initialize):
        for item in self.locationYstyles:
          self.configArrowLocation['y2'].addItem(item)
      if(self.parent.plotArea.arrowLocation['y2'] in self.locationYstyles):
        currindex = self.locationYstyles.index(self.parent.plotArea.arrowLocation['y2'])
        self.configArrowLocation['y2'].setCurrentIndex(currindex)
      else:
        self.configArrowLocation['y2'].setCurrentIndex(0)
    else:
      if(initialize):
        for item in self.locationXstyles:
          self.configArrowLocation['x'].addItem(item)
        for item in self.locationYstyles:
          self.configArrowLocation['y'].addItem(item)
      if(self.parent.plotArea.arrowLocation['x'] in self.locationXstyles):
        currindex = self.locationXstyles.index(self.parent.plotArea.arrowLocation['x'])
        self.configArrowLocation['x'].setCurrentIndex(currindex)
      else:
        self.configArrowLocation['x'].setCurrentIndex(0)
      if(self.parent.plotArea.arrowLocation['y'] in self.locationYstyles):
        currindex = self.locationYstyles.index(self.parent.plotArea.arrowLocation['y'])
        self.configArrowLocation['y'].setCurrentIndex(currindex)
      else:
        self.configArrowLocation['y'].setCurrentIndex(0)

    # x ticks config
    if(not self.secondAxes):
      self.configTickXLabel.setChecked(self.parent.plotArea.ticksXShow)
      self.configTickXAuto.setChecked(self.parent.plotArea.ticksXAuto)
      tickstr = self.magicTickstring(self.parent.plotArea.ticksX)
      self.configTickXEntry.setText(tickstr)
      self.configMinorTickXLabel.setChecked(self.parent.plotArea.ticksXMinor != 0)
      if((self.parent.plotArea.ticksXMinor - 2) in [i for i in range(9)]):
        self.configMinorTickX.setCurrentIndex(self.parent.plotArea.ticksXMinor - 2)
      else:
        self.configMinorTickX.setCurrentIndex(0)
      self.configMinorTickXRelativeLength.setText(str(self.parent.plotArea.ticksXMinorRelativeLength))
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksXColor[0:3]]
      self.configTickXColorButton.setMyColor(*colorvalue)
      self.configTickXSize.setValue(self.parent.plotArea.ticksXSize)
      if(self.parent.plotArea.ticksXWeight == 'bold'):
        self.configTickXBold.setChecked(True)
      else:
        self.configTickXBold.setChecked(False)
      if(self.parent.plotArea.ticksXStyle == 'italic'):
        self.configTickXItalic.setChecked(True)
      else:
        self.configTickXItalic.setChecked(False)
      self.configTickXAngle.setValue(self.parent.plotArea.ticksXAngle)
      if(self.parent.plotArea.ticksXAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.ticksXAlignment)
        self.configTickXAlignment.setCurrentIndex(currindex)
      else:
        self.configTickXAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.ticksXAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.ticksXAlignmentVertical)
        self.configTickXAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configTickXAlignmentVertical.setCurrentIndex(0)
      self.configTickXLinespacing.setText(str(self.parent.plotArea.ticksXLinespacing))
      self.configTickXPad.setText(str(self.parent.plotArea.ticksXPad))
      self.configTickXPad2.setText(str(self.parent.plotArea.ticksXPad2))
      self.configTickXFont.setMe(self.parent.plotArea.tickFont['x'])
      if(self.parent.plotArea.ticksXFormat in self.formatOptions):
        currindex = self.formatOptions.index(self.parent.plotArea.ticksXFormat)
        self.configTickXFormat.setCurrentIndex(currindex)
      else:
        self.configTickXFormat.setCurrentIndex(0)
      self.configTickXFormatPrecision.setText(str(self.parent.plotArea.ticksXFormatPrecision))
      self.configTickXFormatTrailZero.setChecked(self.parent.plotArea.ticksXFormatTrailZero)
      self.configTickXFormatSeparator.setChecked(self.parent.plotArea.ticksXFormatSeparator)
      self.configTickXFormatComma.setChecked(self.parent.plotArea.ticksXFormatComma)
      self.configTickXPrefix.setText(str(self.parent.plotArea.ticksXFormatPrefix))
      self.configTickXPostfix.setText(str(self.parent.plotArea.ticksXFormatPostfix))
      self.updateTickFormatFields(axis='x', tickFormat=self.parent.plotArea.ticksXFormat)      

    # y ticks config
    if(not self.secondAxes):
      self.configTickYLabel.setChecked(self.parent.plotArea.ticksYShow)
      self.configTickYAuto.setChecked(self.parent.plotArea.ticksYAuto)
      tickstr = self.magicTickstring(self.parent.plotArea.ticksY)
      self.configTickYEntry.setText(tickstr)
      self.configMinorTickYLabel.setChecked(self.parent.plotArea.ticksYMinor != 0)
      if((self.parent.plotArea.ticksYMinor - 2) in [i for i in range(9)]):
        self.configMinorTickY.setCurrentIndex(self.parent.plotArea.ticksYMinor - 2)
      else:
        self.configMinorTickY.setCurrentIndex(0)
      self.configMinorTickYRelativeLength.setText(str(self.parent.plotArea.ticksYMinorRelativeLength))
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksYColor[0:3]]
      self.configTickYColorButton.setMyColor(*colorvalue)
      self.configTickYSize.setValue(self.parent.plotArea.ticksYSize)
      if(self.parent.plotArea.ticksYWeight == 'bold'):
        self.configTickYBold.setChecked(True)
      else:
        self.configTickYBold.setChecked(False)
      if(self.parent.plotArea.ticksYStyle == 'italic'):
        self.configTickYItalic.setChecked(True)
      else:
        self.configTickYItalic.setChecked(False)
      self.configTickYAngle.setValue(self.parent.plotArea.ticksYAngle)
      if(self.parent.plotArea.ticksYAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.ticksYAlignment)
        self.configTickYAlignment.setCurrentIndex(currindex)
      else:
        self.configTickYAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.ticksYAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.ticksYAlignmentVertical)
        self.configTickYAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configTickYAlignmentVertical.setCurrentIndex(0)
      self.configTickYLinespacing.setText(str(self.parent.plotArea.ticksYLinespacing))
      self.configTickYPad.setText(str(self.parent.plotArea.ticksYPad))
      self.configTickYPad2.setText(str(self.parent.plotArea.ticksYPad2))
      self.configTickYFont.setMe(self.parent.plotArea.tickFont['y'])
      if(self.parent.plotArea.ticksYFormat in self.formatOptions):
        currindex = self.formatOptions.index(self.parent.plotArea.ticksYFormat)
        self.configTickYFormat.setCurrentIndex(currindex)
      else:
        self.configTickYFormat.setCurrentIndex(0)
      self.configTickYFormatPrecision.setText(str(self.parent.plotArea.ticksYFormatPrecision))
      self.configTickYFormatTrailZero.setChecked(self.parent.plotArea.ticksYFormatTrailZero)
      self.configTickYFormatSeparator.setChecked(self.parent.plotArea.ticksYFormatSeparator)
      self.configTickYFormatComma.setChecked(self.parent.plotArea.ticksYFormatComma)
      self.configTickYPrefix.setText(str(self.parent.plotArea.ticksYFormatPrefix))
      self.configTickYPostfix.setText(str(self.parent.plotArea.ticksYFormatPostfix))
      self.updateTickFormatFields(axis='y', tickFormat=self.parent.plotArea.ticksYFormat)      
    else:
      self.configTickYLabel.setChecked(self.parent.plotArea.ticksY2Show)
      self.configTickYAuto.setChecked(self.parent.plotArea.ticksY2Auto)
      tickstr = self.magicTickstring(self.parent.plotArea.ticksY2)
      self.configTickYEntry.setText(tickstr)
      self.configMinorTickYLabel.setChecked(self.parent.plotArea.ticksY2Minor != 0)
      if((self.parent.plotArea.ticksY2Minor - 2) in [i for i in range(9)]):
        self.configMinorTickY.setCurrentIndex(self.parent.plotArea.ticksY2Minor - 2)
      else:
        self.configMinorTickY.setCurrentIndex(0)
      self.configMinorTickYRelativeLength.setText(str(self.parent.plotArea.ticksY2MinorRelativeLength))
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksY2Color[0:3]]
      self.configTickYColorButton.setMyColor(*colorvalue)
      self.configTickYSize.setValue(self.parent.plotArea.ticksY2Size)
      if(self.parent.plotArea.ticksY2Weight == 'bold'):
        self.configTickYBold.setChecked(True)
      else:
        self.configTickYBold.setChecked(False)
      if(self.parent.plotArea.ticksY2Style == 'italic'):
        self.configTickYItalic.setChecked(True)
      else:
        self.configTickYItalic.setChecked(False)
      self.configTickYAngle.setValue(self.parent.plotArea.ticksY2Angle)
      if(self.parent.plotArea.ticksYAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.ticksY2Alignment)
        self.configTickYAlignment.setCurrentIndex(currindex)
      else:
        self.configTickYAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.ticksYAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.ticksY2AlignmentVertical)
        self.configTickYAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.configTickYAlignmentVertical.setCurrentIndex(0)
      self.configTickYLinespacing.setText(str(self.parent.plotArea.ticksY2Linespacing))
      self.configTickYPad.setText(str(self.parent.plotArea.ticksY2Pad))
      self.configTickYPad2.setText(str(self.parent.plotArea.ticksY2Pad2))
      self.configTickYFont.setMe(self.parent.plotArea.tickFont['y2'])
      if(self.parent.plotArea.ticksY2Format in self.formatOptions):
        currindex = self.formatOptions.index(self.parent.plotArea.ticksY2Format)
        self.configTickYFormat.setCurrentIndex(currindex)
      else:
        self.configTickYFormat.setCurrentIndex(0)
      self.configTickYFormatPrecision.setText(str(self.parent.plotArea.ticksY2FormatPrecision))
      self.configTickYFormatTrailZero.setChecked(self.parent.plotArea.ticksY2FormatTrailZero)
      self.configTickYFormatSeparator.setChecked(self.parent.plotArea.ticksY2FormatSeparator)
      self.configTickYFormatComma.setChecked(self.parent.plotArea.ticksY2FormatComma)
      self.configTickYPrefix.setText(str(self.parent.plotArea.ticksY2FormatPrefix))
      self.configTickYPostfix.setText(str(self.parent.plotArea.ticksY2FormatPostfix))
      self.updateTickFormatFields(axis='y2', tickFormat=self.parent.plotArea.ticksY2Format)      

    # y resid ticks config
    if(not self.secondAxes):
      self.configTickResidYLabel.setChecked(self.parent.plotArea.ticksResidYShow)
      self.configTickResidYAuto.setChecked(self.parent.plotArea.ticksResidYAuto)
      tickstr = self.magicTickstring(self.parent.plotArea.ticksResidY)
      self.configTickResidYEntry.setText(tickstr)
      self.configMinorTickResidLabel.setChecked(self.parent.plotArea.ticksResidYMinor != 0)
      if((self.parent.plotArea.ticksResidYMinor - 2) in [i for i in range(9)]):
        self.configMinorTickResid.setCurrentIndex(self.parent.plotArea.ticksResidYMinor - 2)
      else:
        self.configMinorTickResid.setCurrentIndex(0)
      self.configMinorTickResidRelativeLength.setText(str(self.parent.plotArea.ticksResidYMinorRelativeLength))

    # tick mark config
    if(not self.secondAxes):
      useAxes = ['bottom', 'top', 'left', 'right']
    else:
      useAxes = ['left2', 'right2']
    for axis in useAxes:
      self.configTickMarkLabel[axis].setChecked(self.parent.plotArea.ticksVisible[axis])
      if(initialize):
        for entry in self.directionstyles:
          self.configTickMarkDirection[axis].addItem(entry)
      if(self.parent.plotArea.ticksDirection[axis] in self.directionstyles):
        currindex = self.directionstyles.index(self.parent.plotArea.ticksDirection[axis])
        self.configTickMarkDirection[axis].setCurrentIndex(currindex)
      else:
        self.configTickMarkDirection[axis].setCurrentIndex(0)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksColor[axis][0:3]]
      self.configTickMarkColor[axis].setMyColor(*colorvalue)
      self.configTickMarkWidth[axis].setValue(self.parent.plotArea.ticksWidth[axis])
      self.configTickMarkLength[axis].setValue(self.parent.plotArea.ticksLength[axis])
      self.configTickMarkLabelShow[axis].setChecked(self.parent.plotArea.ticksLabelShow[axis])

    # slaved tick mark config
    if(not self.secondAxes):
      axis = 'slaved'
      self.slavedTickMarkLabel.setChecked(self.parent.plotArea.ticksVisible[axis])
      if(initialize):
        for entry in self.directionstyles:
          self.slavedTickMarkDirection.addItem(entry)
      if(self.parent.plotArea.ticksDirection[axis] in self.directionstyles):
        currindex = self.directionstyles.index(self.parent.plotArea.ticksDirection[axis])
        self.slavedTickMarkDirection.setCurrentIndex(currindex)
      else:
        self.slavedTickMarkDirection.setCurrentIndex(0)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksColor[axis][0:3]]
      self.slavedTickMarkColor.setMyColor(*colorvalue)
      self.slavedTickMarkWidth.setValue(self.parent.plotArea.ticksWidth[axis])
      self.slavedTickMarkLength.setValue(self.parent.plotArea.ticksLength[axis])
      self.slavedTickMarkLabelShow.setChecked(self.parent.plotArea.ticksLabelShow[axis])

    # inner situation config
    if(not self.secondAxes):
      self.xSplitFraction.setText(str(self.parent.plotArea.splitFraction))
      self.xSplitPad.setText(str(self.parent.plotArea.splitPad))

      self.configInnerAxesLabel.setChecked(self.parent.plotArea.innerAxes)
      self.configInnerTickLabel.setChecked(self.parent.plotArea.innerTicks)
      self.configInnerTickXAuto.setChecked(self.parent.plotArea.ticksXAuto_div)
        
      self.configInnerLabel.setChecked(self.parent.plotArea.ticksXShow_div)
      tickstr = self.magicTickstring(self.parent.plotArea.ticksX_div)
      self.configInnerTickXEntry.setText(tickstr)

      if(self.parent.plotArea.ticksXFormat_div in self.formatOptions):
        currindex = self.formatOptions.index(self.parent.plotArea.ticksXFormat_div)
        self.configInnerTickXFormat.setCurrentIndex(currindex)
      else:
        self.configInnerTickXFormat.setCurrentIndex(0)
      self.configInnerTickXFormatPrecision.setText(str(self.parent.plotArea.ticksXFormatPrecision_div))
      self.configInnerTickXFormatTrailZero.setChecked(self.parent.plotArea.ticksXFormatTrailZero_div)
      self.configInnerTickXFormatSeparator.setChecked(self.parent.plotArea.ticksXFormatSeparator_div)
      self.configInnerTickXFormatComma.setChecked(self.parent.plotArea.ticksXFormatComma_div)
      self.configInnerTickXPrefix.setText(str(self.parent.plotArea.ticksXFormatPrefix_div))
      self.configInnerTickXPostfix.setText(str(self.parent.plotArea.ticksXFormatPostfix_div))
      self.updateTickFormatFields(axis='x2', tickFormat=self.parent.plotArea.ticksXFormat_div)

      self.configInnerMinorTickLabel.setChecked(self.parent.plotArea.ticksXMinor_div != 0)
      if((self.parent.plotArea.ticksXMinor_div - 2) in [i for i in range(9)]):
        self.configInnerMinorTickX.setCurrentIndex(self.parent.plotArea.ticksXMinor_div - 2)
      else:
        self.configInnerMinorTickX.setCurrentIndex(0)

      self.configInnerDividerLine.setChecked(self.parent.plotArea.splitDivider)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.splitDividerColor[0:3]]
      self.configInnerDividerLineColor.setMyColor(*colorvalue)
      self.configInnerDividerLineLength.setValue(self.parent.plotArea.splitDividerLength)
      self.configInnerDividerLineWidth.setValue(self.parent.plotArea.splitDividerWidth)
      if(initialize):
        for entry in self.dashstyles:
          self.configInnerDividerLineDashStyle.addItem(entry)
      if(self.parent.plotArea.splitDividerDashStyle in self.dashstyles):
        currindex = self.dashstyles.index(self.parent.plotArea.splitDividerDashStyle)
        self.configInnerDividerLineDashStyle.setCurrentIndex(currindex)
      else:
        self.configInnerDividerLineDashStyle.setCurrentIndex(0)
      self.configInnerDividerLineAngle.setValue(self.parent.plotArea.splitDividerAngle)
      self.locationstyles = ['bottom', 'top', 'both']
      if(initialize):
        for entry in self.locationstyles:
          self.configInnerDividerLineLocation.addItem(entry)
      if(self.parent.plotArea.splitDividerLocation in self.locationstyles):
        currindex = self.locationstyles.index(self.parent.plotArea.splitDividerLocation)
        self.configInnerDividerLineLocation.setCurrentIndex(currindex)
      else:
        self.configInnerDividerLineLocation.setCurrentIndex(0)
        
    # slaved x controls
    if(not self.secondAxes):
      self.slavedXLabel.setChecked(self.parent.plotArea.slavedX)
      text = ''
      for entry in self.parent.plotArea.slavedXTransform:
        if(len(text)):
          text += ', '
        if(entry[0] in ['e', 'ln']):
          text += '[' + entry[0] + ']'
        else:
          text += repr(entry)
      self.slavedXEntry.setText(text)
      self.slavedXNumber.setText('2.0')
      self.slavedXLabelShow.setChecked(self.parent.plotArea.slavedXLabelShow)
      self.configSlavedXLabel.setText(self.parent.plotArea.slavedXLabel)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.slavedLabelXColor[0:3]]
      self.slavedXColorButton.setMyColor(*colorvalue)
      self.slavedXSize.setValue(self.parent.plotArea.slavedLabelXSize)
      if(self.parent.plotArea.slavedLabelXWeight == 'bold'):
        self.slavedXBold.setChecked(True)
      else:
        self.slavedXBold.setChecked(False)
      if(self.parent.plotArea.slavedLabelXStyle == 'italic'):
        self.slavedXItalic.setChecked(True)
      else:
        self.slavedXItalic.setChecked(False)
      '''
      if(self.parent.plotArea.slavedLabelXVariant == 'small-caps'):
        self.slavedXVariant.setChecked(True)
      else:
        self.slavedXVariant.setChecked(False)
      '''
      self.slavedXFont.setMe(self.parent.plotArea.axisFont['xs'])
      if(self.parent.plotArea.slavedLabelXAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.slavedLabelXAlignment)
        self.slavedXAlignment.setCurrentIndex(currindex)
      else:
        self.slavedXAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.slavedLabelXAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.slavedLabelXAlignmentVertical)
        self.slavedXAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.slavedXAlignmentVertical.setCurrentIndex(0)
      self.slavedXLinespacing.setText(str(self.parent.plotArea.slavedLabelXLinespacing))
      self.slavedXPad.setText(str(self.parent.plotArea.slavedLabelXPad))
      self.slavedXPos.setText(str(self.parent.plotArea.slavedLabelXPos))
      self.slavedXAngle.setValue(self.parent.plotArea.slavedLabelXAngle)

      self.slavedMinorTickXLabel.setChecked(self.parent.plotArea.slavedTicksXMinor != 0)
      if((self.parent.plotArea.slavedTicksXMinor - 2) in [i for i in range(9)]):
        self.slavedMinorTickX.setCurrentIndex(self.parent.plotArea.slavedTicksXMinor - 2)
      else:
        self.slavedMinorTickX.setCurrentIndex(0)
      self.slavedMinorTickXRelativeLength.setText(str(self.parent.plotArea.slavedTicksXMinorRelativeLength))
      colorvalue = [int(i * 255.0) for i in self.parent.plotArea.slavedTicksXColor[0:3]]
      self.slavedTickXColorButton.setMyColor(*colorvalue)
      self.slavedTickXSize.setValue(self.parent.plotArea.slavedTicksXSize)
      if(self.parent.plotArea.slavedTicksXWeight == 'bold'):
        self.slavedTickXBold.setChecked(True)
      else:
        self.slavedTickXBold.setChecked(False)
      if(self.parent.plotArea.slavedTicksXStyle == 'italic'):
        self.slavedTickXItalic.setChecked(True)
      else:
        self.slavedTickXItalic.setChecked(False)
      self.slavedTickXAngle.setValue(self.parent.plotArea.slavedTicksXAngle)
      if(self.parent.plotArea.slavedTicksXAlignment in self.alignHorizontal):
        currindex = self.alignHorizontal.index(self.parent.plotArea.slavedTicksXAlignment)
        self.slavedTickXAlignment.setCurrentIndex(currindex)
      else:
        self.slavedTickXAlignment.setCurrentIndex(0)
      if(self.parent.plotArea.slavedTicksXAlignmentVertical in self.alignVertical):
        currindex = self.alignVertical.index(self.parent.plotArea.slavedTicksXAlignmentVertical)
        self.slavedTickXAlignmentVertical.setCurrentIndex(currindex)
      else:
        self.slavedTickXAlignmentVertical.setCurrentIndex(0)
      self.slavedTickXLinespacing.setText(str(self.parent.plotArea.slavedTicksXLinespacing))
      self.slavedTickXPad.setText(str(self.parent.plotArea.slavedTicksXPad))
      self.slavedTickXPad2.setText(str(self.parent.plotArea.slavedTicksXPad2))
      self.slavedTickXFont.setMe(self.parent.plotArea.tickFont['xs'])
      if(self.parent.plotArea.slavedXFormat in self.formatOptions):
        currindex = self.formatOptions.index(self.parent.plotArea.slavedXFormat)
        self.slavedXFormat.setCurrentIndex(currindex)
      else:
        self.slavedXFormat.setCurrentIndex(0)
      self.slavedXFormatPrecision.setText(str(self.parent.plotArea.slavedXFormatPrecision))
      self.slavedXFormatTrailZero.setChecked(self.parent.plotArea.slavedXFormatTrailZero)
      self.slavedXFormatSeparator.setChecked(self.parent.plotArea.slavedXFormatSeparator)
      self.slavedXFormatComma.setChecked(self.parent.plotArea.slavedXFormatComma)
      self.slavedXPrefix.setText(str(self.parent.plotArea.slavedXFormatPrefix))
      self.slavedXPostfix.setText(str(self.parent.plotArea.slavedXFormatPostfix))

      self.slavedTickXAuto.setChecked(self.parent.plotArea.slavedTicksXAuto)
      tickstr = self.magicTickstring(self.parent.plotArea.slavedTicksX)
      self.slavedTickXEntry.setText(tickstr)

    # grid config
    if(not self.secondAxes):
      useAxes = ['x', 'y', 'x2']
    else:
      useAxes = ['y2']
    for axis in useAxes:
      self.configGridLabel[axis].setChecked(self.parent.plotArea.gridVisible[axis])
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.gridColor[axis][0:3]]
      self.configGridColor[axis].setMyColor(*colorvalue)
      self.configGridWidth[axis].setValue(self.parent.plotArea.gridWidth[axis])
      useLineStyle = [i for i in self.linestyles if (i != 'None')]
      if(initialize):
        for entry in useLineStyle:
          self.configGridStyle[axis].addItem(entry)
      if(self.parent.plotArea.gridStyle[axis] in useLineStyle):
        currindex = useLineStyle.index(self.parent.plotArea.gridStyle[axis])
        self.configGridStyle[axis].setCurrentIndex(currindex)
      else:
        self.configGridStyle[axis].setCurrentIndex(0)
      if(initialize):
        for entry in self.dashstyles:
          self.configGridDashStyle[axis].addItem(entry)
      if(self.parent.plotArea.gridDashStyle[axis] in self.dashstyles):
        currindex = self.dashstyles.index(self.parent.plotArea.gridDashStyle[axis])
        self.configGridDashStyle[axis].setCurrentIndex(currindex)
      else:
        self.configGridDashStyle[axis].setCurrentIndex(0)
      if(initialize):
        for entry in self.orderstyles:
          self.configGridOrder[axis].addItem(entry)
      if(self.parent.plotArea.gridOrder[axis] in self.orderstyles):
        currindex = self.orderstyles.index(self.parent.plotArea.gridOrder[axis])
        self.configGridOrder[axis].setCurrentIndex(currindex)
      else:
        self.configGridOrder[axis].setCurrentIndex(0)
      # minor grid
      self.configGridLabel2[axis].setChecked(self.parent.plotArea.gridMinorVisible[axis])
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.gridMinorColor[axis][0:3]]
      self.configGridColor2[axis].setMyColor(*colorvalue)
      self.configGridWidth2[axis].setValue(self.parent.plotArea.gridMinorWidth[axis])
      if(initialize):
        for entry in useLineStyle:
          self.configGridStyle2[axis].addItem(entry)
      if(self.parent.plotArea.gridMinorStyle[axis] in useLineStyle):
        currindex = useLineStyle.index(self.parent.plotArea.gridMinorStyle[axis])
        self.configGridStyle2[axis].setCurrentIndex(currindex)
      else:
        self.configGridStyle2[axis].setCurrentIndex(0)
      if(initialize):
        for entry in self.dashstyles:
          self.configGridDashStyle2[axis].addItem(entry)
      if(self.parent.plotArea.gridMinorDashStyle[axis] in self.dashstyles):
        currindex = self.dashstyles.index(self.parent.plotArea.gridMinorDashStyle[axis])
        self.configGridDashStyle2[axis].setCurrentIndex(currindex)
      else:
        self.configGridDashStyle2[axis].setCurrentIndex(0)
      if(initialize):
        for entry in self.orderstyles:
          self.configGridOrder2[axis].addItem(entry)
      if(self.parent.plotArea.gridMinorOrder[axis] in self.orderstyles):
        currindex = self.orderstyles.index(self.parent.plotArea.gridMinorOrder[axis])
        self.configGridOrder2[axis].setCurrentIndex(currindex)
      else:
        self.configGridOrder2[axis].setCurrentIndex(0)
      # shady label
      self.configGridLabel3[axis].setChecked(self.parent.plotArea.gridRectVisible[axis])
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.gridRectColor[axis][0:3]]
      self.configGridRectColor[axis].setMyColor(*colorvalue)

    # splitY controls
    if(self.secondAxes):
      self.configSplitYLabel.setChecked(self.parent.plotArea.splitY)
      self.configSplitYFraction.setText(str(self.parent.plotArea.splitYFraction))
      self.configSplitYPad.setText(str(self.parent.plotArea.splitYPad))
      self.configSplitYAxesLabel.setChecked(self.parent.plotArea.innerAxesY)
      self.configSplitYTickLabel.setChecked(self.parent.plotArea.innerTicksY)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.splitYDividerColor[0:3]]
      self.configSplitYDividerLineColor.setMyColor(*colorvalue)
      self.configSplitYDividerLine.setChecked(self.parent.plotArea.splitYDivider)
      self.configSplitYDividerLineLength.setValue(self.parent.plotArea.splitYDividerLength)
      self.configSplitYDividerLineWidth.setValue(self.parent.plotArea.splitYDividerWidth)
      if(initialize):
        for entry in self.dashstyles:
          self.configSplitYDividerLineDashStyle.addItem(entry)
      if(self.parent.plotArea.splitYDividerDashStyle in self.dashstyles):
        currindex = self.dashstyles.index(self.parent.plotArea.splitYDividerDashStyle)
        self.configSplitYDividerLineDashStyle.setCurrentIndex(currindex)
      else:
        self.configSplitYDividerLineDashStyle.setCurrentIndex(0)
      self.configSplitYDividerLineAngle.setValue(self.parent.plotArea.splitYDividerAngle)
      self.locationstyles = ['left', 'right', 'both']
      if(initialize):
        for entry in self.locationstyles:
          self.configSplitYDividerLineLocation.addItem(entry)
      if(self.parent.plotArea.splitYDividerLocation in self.locationstyles):
        currindex = self.locationstyles.index(self.parent.plotArea.splitYDividerLocation)
        self.configSplitYDividerLineLocation.setCurrentIndex(currindex)
      else:
        self.configSplitYDividerLineLocation.setCurrentIndex(0)
      
    # legend config
    if(not self.secondAxes):
      self.configLegendLabel.setChecked(self.parent.plotArea.legendVisible)
      if(self.parent.plotArea.legendPlacement in self.placementstyles):
        currindex = self.placementstyles.index(self.parent.plotArea.legendPlacement)
        self.configLegendPlacement.setCurrentIndex(currindex)
      else:
        self.configLegendPlacement.setCurrentIndex(0)
      for prop in ['face', 'edge']:
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.legendColor[prop][0:3]]
        self.configLegendColor[prop].setMyColor(*colorvalue)
      self.configLegendColorLabel['face'].setChecked(self.parent.plotArea.legendFill)
      self.configLegendColorLabel['edge'].setChecked(self.parent.plotArea.legendEdge)
      self.configLegendEdgeWidth.setValue(self.parent.plotArea.legendEdgeWidth)
      self.configLegendX.setText(str(self.parent.plotArea.legendX))
      self.configLegendY.setText(str(self.parent.plotArea.legendY))
      if(self.parent.plotArea.legendHatch in self.hatchstyles):
        currindex = self.hatchstyles.index(self.parent.plotArea.legendHatch)
        self.legendComboHatchStyle.setCurrentIndex(currindex)
      else:
        self.legendComboHatchStyle.setCurrentIndex(0)
      if(self.parent.plotArea.legendHatchMultiply in self.hatchMultiplystyles):
        currindex = self.hatchMultiplystyles.index(self.parent.plotArea.legendHatchMultiply)
        self.legendComboHatchMultiplyStyle.setCurrentIndex(currindex)
      else:
        self.legendComboHatchMultiplyStyle.setCurrentIndex(0)
      self.legendHatchLineWidthSpin.setValue(self.parent.plotArea.legendHatchLinewidth)
      self.configLegendRounding.setText(str(self.parent.plotArea.legendRounding))
      self.configLegendShadowLabel.setChecked(self.parent.plotArea.legendShadow)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.legendShadowFaceColor[0:3]]
      self.configLegendShadowColor.setMyColor(*colorvalue)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.legendLabelColor[0:3]]
      self.configLegendShadowOffX.setText(str(self.parent.plotArea.legendShadowDeltaX))
      self.configLegendShadowOffY.setText(str(self.parent.plotArea.legendShadowDeltaY))
      self.configLegendLabelColor.setMyColor(*colorvalue)
      self.configLegendLinespacing.setText(str(self.parent.plotArea.legendLabelLinespacing))
      self.configLegendLabelSize.setValue(self.parent.plotArea.legendLabelSize)
      if(self.parent.plotArea.legendLabelWeight == 'bold'):
        self.configLegendLabelBold.setChecked(True)
      else:
        self.configLegendLabelBold.setChecked(False)
      if(self.parent.plotArea.legendLabelStyle == 'italic'):
        self.configLegendLabelItalic.setChecked(True)
      else:
        self.configLegendLabelItalic.setChecked(False)
      self.configLegendLabelFont.setMe(self.parent.plotArea.legendLabelFont)
      if(self.parent.plotArea.legendNumPoints in [(i + 1) for i in range(10)]):
        self.configLegendMarkerNumPoints.setCurrentIndex(self.parent.plotArea.legendNumPoints - 1)
      else:
        self.configLegendMarkerNumPoints.setCurrentIndex(0)
      if(self.parent.plotArea.legendMarkerFirst):
        self.configLegendMarkerFirstLabel.setChecked(True)
      else:
        self.configLegendMarkerFirstLabel.setChecked(False)
      self.configLegendMarkerScale.setText(str(self.parent.plotArea.legendMarkerScale))
      if(self.parent.plotArea.legendNumCol in [(i+1) for i in range(10)]):
        self.configLegendNCol.setCurrentIndex(self.parent.plotArea.legendNumCol - 1)
      else:
        self.configLegendNCol.setCurrentIndex(0)
      self.configLegendPadBorder.setText(str(self.parent.plotArea.legendBorderPad))
      self.configLegendPadRow.setText(str(self.parent.plotArea.legendLabelSpacing))
      self.configLegendPadCol.setText(str(self.parent.plotArea.legendColumnSpacing))
      self.configLegendLabelPad.setText(str(self.parent.plotArea.legendLabelPad))

    # canvas config
    if(not self.secondAxes):
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.figureColor[0:3]]
      self.configFigureColorButton.setMyColor(*colorvalue)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.canvasColor[0:3]]
      self.configCanvasColorButton.setMyColor(*colorvalue)
      
      # canvas and figure fill?
      self.configFigureLabel.setChecked(self.parent.plotArea.figureFill)
      self.configCanvasLabel.setChecked(self.parent.plotArea.canvasFill)
      self.configFrameLabel.setChecked(self.parent.plotArea.frameDraw)
      
      # canvas gradient
      self.configCanvasGradientLabel.setChecked(self.parent.plotArea.canvasGradientVisible)
      
      if(self.parent.plotArea.canvasGradientStyle == 'radial'):
        self.configCanvasGradientStyle.setCurrentIndex(1)
      else:
        self.configCanvasGradientStyle.setCurrentIndex(0)
      # control visibility of canvas gradient controls
      self.visibilityCanvasGradientControls()
      
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.canvasGradientColor1[0:3]]
      self.configCanvasGradientColor1Button.setMyColor(*colorvalue)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.canvasGradientColor2[0:3]]
      self.configCanvasGradientColor2Button.setMyColor(*colorvalue)

      self.configCanvasGradientAngle.setValue(self.parent.plotArea.canvasGradientAngle)
      self.configCanvasGradientCenterX.setText(str(self.parent.plotArea.canvasGradientCenter[0]))
      self.configCanvasGradientCenterY.setText(str(self.parent.plotArea.canvasGradientCenter[1]))
      self.configCanvasGradientWidth.setText(str(self.parent.plotArea.canvasGradientWidth))
      
      # canvas dimensions
      self.exportSizeX.setText(str(self.parent.plotArea.exportWidth))
      self.exportSizeY.setText(str(self.parent.plotArea.exportHeight))
      
      # pad graphics
      for axis in ['bottom', 'top', 'left', 'right']:
        self.exportPadEntry[axis].setText(str(self.parent.plotArea.padSize[axis]))
        
      # frame
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.frameColor[0:3]]
      self.configFrameColor.setMyColor(*colorvalue)
      self.configFrameWidth.setValue(self.parent.plotArea.frameWidth)
      if(initialize):
        self.linestyles__ = [i for i in self.linestyles if (i != 'None')]
        for entry in self.linestyles__:
          self.configFrameStyle.addItem(entry)
      if(self.parent.plotArea.frameStyle in self.linestyles__):
        currindex = self.linestyles__.index(self.parent.plotArea.frameStyle)
        self.configFrameStyle.setCurrentIndex(currindex)
      else:
        self.configFrameStyle.setCurrentIndex(0)
      if(initialize):
        for entry in self.dashstyles:
          self.configFrameDashStyle.addItem(entry)
      if(self.parent.plotArea.frameDashStyle in self.dashstyles):
        currindex = self.dashstyles.index(self.parent.plotArea.frameDashStyle)
        self.configFrameDashStyle.setCurrentIndex(currindex)
      else:
        self.configGridDashStyle.setCurrentIndex(0)

    # xkcd
    if(not self.secondAxes):
      self.configXkcdLabel.setChecked(self.parent.plotArea.xkcd)
      self.xkcdScale.setText(str(self.parent.plotArea.xkcdScale))
      self.xkcdLength.setText(str(self.parent.plotArea.xkcdLength))
      self.xkcdRandom.setText(str(self.parent.plotArea.xkcdRandomness))
      
      # path effects
      self.configPathEffectsLabel.setChecked(self.parent.plotArea.applyPathStroke)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.pathStrokeColor[0:3]]
      self.configPathEffectsColorButton.setMyColor(*colorvalue)
      self.configPathEffectsWidth.setValue(self.parent.plotArea.pathStrokeWidth)
  
      self.configPathShadowLabel.setChecked(self.parent.plotArea.applyPathShadow)
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.pathShadowColor[0:3]]
      self.configPathShadowColorButton.setMyColor(*colorvalue)
      self.configPathShadowOffX.setText(str(self.parent.plotArea.pathShadowX))
      self.configPathShadowOffY.setText(str(self.parent.plotArea.pathShadowY))
      
    # advanced graphics
    if(not self.secondAxes):
      self.configAdvancedSettingsButton.setChecked(self.advancedExport['advancedGraphics'])
  
  def connectEvents(self):
    # connects all events in entry mask
    # x label config
    if(not self.secondAxes):
      self.configXLabel.clicked.connect(partial(self.toggleAxisLabel, 'x'))
      self.configXName.editingFinished.connect(partial(self.changeAxisLabel, 'x'))
      self.configXColorButton.clicked.connect(partial(self.changeAxisLabelColor, axis = 'x'))
      self.configXSize.editingFinished.connect(partial(self.alterAxisLabelSize, entryfield = self.configXSize, axis = 'x'))
      self.configXBold.clicked.connect(partial(self.changeAxisLabelBold, axis = 'x'))
      self.configXItalic.clicked.connect(partial(self.changeAxisLabelItalic, axis = 'x'))
      ###self.configXVariant.clicked.connect(partial(self.changeAxisLabelVariant, axis = 'x'))
      self.configXFont.activated.connect(partial(self.setAxisFont, axis = 'x'))
      self.configXAlignment.activated.connect(partial(self.setAxisLabelAlignment, axis = 'x'))
      self.configXAlignmentVertical.activated.connect(partial(self.setAxisLabelAlignmentVertical, axis = 'x'))
      self.configXLinespacing.editingFinished.connect(partial(self.changeAxisLabelLinescpacing, entryfield=self.configXLinespacing, axis='x', minval=0., maxval=10.))
      self.configXPad.editingFinished.connect(partial(self.changeAxisLabelPad, entryfield = self.configXPad, axis = 'x', minval = -100.0, maxval = 100.0))
      self.configXPos.editingFinished.connect(partial(self.changeAxisLabelPos, entryfield = self.configXPos, axis = 'x', minval = -10, maxval = 10))
      self.configXAngle.editingFinished.connect(partial(self.alterAxisLabelAngle, entryfield=self.configXAngle, axis='x'))
    
    # y label config
    if(not self.secondAxes):
      targetAxis = 'y'
    else:
      targetAxis = 'y2'
    self.configYLabel.clicked.connect(partial(self.toggleAxisLabel, targetAxis))
    self.configYName.editingFinished.connect(partial(self.changeAxisLabel, targetAxis))
    self.configYColorButton.clicked.connect(partial(self.changeAxisLabelColor, axis = targetAxis))
    self.configYSize.editingFinished.connect(partial(self.alterAxisLabelSize, entryfield = self.configYSize, axis = targetAxis))
    self.configYBold.clicked.connect(partial(self.changeAxisLabelBold, axis = targetAxis))
    self.configYItalic.clicked.connect(partial(self.changeAxisLabelItalic, axis = targetAxis))
    ###self.configYVariant.clicked.connect(partial(self.changeAxisLabelVariant, axis = targetAxis))
    self.configYFont.activated.connect(partial(self.setAxisFont, axis = targetAxis))
    self.configYAlignment.activated.connect(partial(self.setAxisLabelAlignment, axis = targetAxis))
    self.configYAlignmentVertical.activated.connect(partial(self.setAxisLabelAlignmentVertical, axis = targetAxis))
    self.configYLinespacing.editingFinished.connect(partial(self.changeAxisLabelLinescpacing, entryfield=self.configYLinespacing, axis=targetAxis, minval=0., maxval=10.))
    self.configYPad.editingFinished.connect(partial(self.changeAxisLabelPad, entryfield = self.configYPad, axis = targetAxis, minval = -100.0, maxval = 100.0))
    self.configYPos.editingFinished.connect(partial(self.changeAxisLabelPos, entryfield = self.configYPos, axis = targetAxis, minval = -10, maxval = 10))
    self.configYAngle.editingFinished.connect(partial(self.alterAxisLabelAngle, entryfield=self.configYAngle, axis=targetAxis))

    # axis config
    if(not self.secondAxes):
      useAxes = ['bottom', 'top', 'left', 'right']
    else:
      useAxes = ['left2', 'right2']
    for axis in useAxes:
      self.configAxisLabel[axis].clicked.connect(partial(self.setAxisVisibility, axis = axis))
      self.configAxisColor[axis].clicked.connect(partial(self.changeAxisColor, axis = axis))
      self.configAxisWidth[axis].editingFinished.connect(partial(self.alterAxisWidth, axis=axis))
      self.configAxisStyle[axis].activated.connect(partial(self.setAxisStyle, axis = axis))
      self.configAxisDashStyle[axis].activated.connect(partial(self.setAxisDashStyle, axis = axis))
      self.configAxisPosition[axis].activated.connect(partial(self.setAxisPosition, axis = axis))
      self.configAxisPositionValue[axis].editingFinished.connect(partial(self.changeAxisPositionValue, axis = axis))
      self.configAxisPositionReset[axis].clicked.connect(partial(self.resetAxisPosition, axis = axis))
      self.configAxisBoundLabel[axis].clicked.connect(partial(self.setAxisBoundary, axis=axis))
      self.configAxisBoundLower[axis].editingFinished.connect(partial(self.changeAxisBoundary, axis=axis, lower=True))
      self.configAxisBoundUpper[axis].editingFinished.connect(partial(self.changeAxisBoundary, axis=axis, lower=False))

    # arrow config
    if(self.secondAxes):
      useAxes = ['y2']
    else:
      useAxes = ['x', 'y']
    for axis in useAxes:
      self.configArrowLabel[axis].clicked.connect(partial(self.setAxisArrow, axis = axis, item='all'))
      self.configArrowLineColorLabel[axis].clicked.connect(partial(self.setAxisArrow, axis=axis, item='edge'))
      self.configArrowFillColorLabel[axis].clicked.connect(partial(self.setAxisArrow, axis=axis, item='fill'))
      self.configArrowLineWidth[axis].editingFinished.connect(partial(self.alterArrowLineWidth, axis=axis))
      self.configArrowZ[axis].activated.connect(partial(self.changeArrowZ, axis=axis))
      self.configArrowLineColor[axis].clicked.connect(partial(self.changeArrowColor, axis = axis, item='line'))
      self.configArrowFillColor[axis].clicked.connect(partial(self.changeArrowColor, axis = axis, item='fill'))
      self.configArrowHeadWidth[axis].editingFinished.connect(partial(self.changeArrowHeadWidth, axis = axis, minval = 0.0, maxval = 5.0))
      self.configArrowHeadLength[axis].editingFinished.connect(partial(self.changeArrowHeadLength, axis = axis, minval = 0.0, maxval = 5.0))
      self.configArrowOverhang[axis].editingFinished.connect(partial(self.changeArrowOverhang, axis = axis, minval = -1.0, maxval = 1.0))
      self.configArrowOffset[axis].editingFinished.connect(partial(self.changeArrowOffset, axis = axis, minval = 0.0, maxval = 1.0))
      self.configArrowLocation[axis].activated.connect(partial(self.changeArrowLocation, axis=axis))

    # x ticks config
    if(not self.secondAxes):
      self.configTickXLabel.clicked.connect(partial(self.toggleTicksLabel, 'x'))
      self.configTickXAuto.clicked.connect(partial(self.automaticAxisTicks, axis = 'x'))
      self.configTickXEntry.editingFinished.connect(partial(self.changeAxisTicks, 'x', updateAuto=False))
      self.configTickUseData.clicked.connect(partial(self.dataAxisTicks, False))
      self.configMinorTickXLabel.clicked.connect(partial(self.toggleMinorTicks, entryfield=self.configMinorTickXLabel, axis='x'))
      self.configMinorTickXRelativeLength.editingFinished.connect(partial(self.changeMinorTickRelativeLength, entryfield = self.configMinorTickXRelativeLength, axis='x', minval = 0.1, maxval = 10.0))
      self.configTickXColorButton.clicked.connect(partial(self.changeTickLabelColor, axis = 'x'))
      self.configTickXSize.editingFinished.connect(partial(self.alterTickLabelSize, entryfield=self.configTickXSize, axis='x'))
      self.configTickXBold.clicked.connect(partial(self.changeTickLabelBold, axis = 'x'))
      self.configTickXItalic.clicked.connect(partial(self.changeTickLabelItalic, axis = 'x'))
      self.configTickXAngle.editingFinished.connect(partial(self.alterTickLabelAngle, entryfield=self.configTickXAngle, axis='x'))
      self.configTickXAlignment.activated.connect(partial(self.setTickLabelAlignment, axis = 'x'))
      self.configTickXAlignmentVertical.activated.connect(partial(self.setTickLabelAlignmentVertical, axis = 'x'))
      self.configTickXLinespacing.editingFinished.connect(partial(self.changeTickLabelLinespacing, entryfield=self.configTickXLinespacing, axis='x', minval=0., maxval=10.))
      self.configTickXPad.editingFinished.connect(partial(self.changeTickLabelPad, entryfield = self.configTickXPad, axis = 'x', minval = -100.0, maxval = 100.0))
      self.configTickXPad2.editingFinished.connect(partial(self.changeTickLabelPad2, entryfield = self.configTickXPad2, axis = 'x', minval = -100.0, maxval = 100.0))
      self.configTickXFont.activated.connect(partial(self.setTickFont, axis = 'x'))
      self.configMinorTickX.activated.connect(partial(self.setMinorTick, axis = 'x'))
      self.configTickXFormat.activated.connect(partial(self.setTickFormat, entryfield = self.configTickXFormat, axis = 'x'))
      self.configTickXFormatPrecision.editingFinished.connect(partial(self.changeTickFormatPrecision, entryfield = self.configTickXFormatPrecision, axis = 'x', minval = 0, maxval = 20))
      self.configTickXFormatTrailZero.clicked.connect(partial(self.setFormatTrailZero, entryfield=self.configTickXFormatTrailZero, axis = 'x'))
      self.configTickXFormatSeparator.clicked.connect(partial(self.setFormatSeparator, entryfield=self.configTickXFormatSeparator, axis = 'x'))
      self.configTickXFormatComma.clicked.connect(partial(self.setFormatComma, entryfield=self.configTickXFormatComma, axis = 'x'))
      self.configTickXPrefix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.configTickXPrefix, axis='x', prefix=True))
      self.configTickXPostfix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.configTickXPostfix, axis='x', prefix=False))

    # y ticks config
    if(not self.secondAxes):
      targetAxis = 'y'
    else:
      targetAxis = 'y2'
    self.configTickYLabel.clicked.connect(partial(self.toggleTicksLabel, targetAxis))
    self.configTickYAuto.clicked.connect(partial(self.automaticAxisTicks, axis = targetAxis))
    self.configTickYEntry.editingFinished.connect(partial(self.changeAxisTicks, targetAxis, updateAuto=False))
    self.configTickYUseData.clicked.connect(partial(self.dataAxisYTicks, axis=targetAxis))
    self.configMinorTickYLabel.clicked.connect(partial(self.toggleMinorTicks, entryfield=self.configMinorTickYLabel, axis=targetAxis))
    self.configMinorTickYRelativeLength.editingFinished.connect(partial(self.changeMinorTickRelativeLength, entryfield = self.configMinorTickYRelativeLength, axis=targetAxis, minval = 0.1, maxval = 10.0))
    self.configTickYColorButton.clicked.connect(partial(self.changeTickLabelColor, axis = targetAxis))
    self.configTickYSize.editingFinished.connect(partial(self.alterTickLabelSize, entryfield=self.configTickYSize, axis=targetAxis))
    self.configTickYBold.clicked.connect(partial(self.changeTickLabelBold, axis = targetAxis))
    self.configTickYItalic.clicked.connect(partial(self.changeTickLabelItalic, axis = targetAxis))
    self.configTickYAngle.editingFinished.connect(partial(self.alterTickLabelAngle, entryfield=self.configTickYAngle, axis=targetAxis))
    self.configTickYAlignment.activated.connect(partial(self.setTickLabelAlignment, axis = targetAxis))
    self.configTickYAlignmentVertical.activated.connect(partial(self.setTickLabelAlignmentVertical, axis = targetAxis))
    self.configTickYLinespacing.editingFinished.connect(partial(self.changeTickLabelLinespacing, entryfield=self.configTickYLinespacing, axis='y', minval=0., maxval=10.))
    self.configTickYPad.editingFinished.connect(partial(self.changeTickLabelPad, entryfield = self.configTickYPad, axis = targetAxis, minval = -100.0, maxval = 100.0))
    self.configTickYPad2.editingFinished.connect(partial(self.changeTickLabelPad2, entryfield = self.configTickYPad2, axis = targetAxis, minval = -100.0, maxval = 100.0))
    self.configTickYFont.activated.connect(partial(self.setTickFont, axis = targetAxis))
    self.configMinorTickY.activated.connect(partial(self.setMinorTick, axis = targetAxis))
    self.configTickYFormat.activated.connect(partial(self.setTickFormat, entryfield = self.configTickYFormat, axis = targetAxis))
    self.configTickYFormatPrecision.editingFinished.connect(partial(self.changeTickFormatPrecision, entryfield = self.configTickYFormatPrecision, axis = targetAxis, minval = 0, maxval = 20))
    self.configTickYFormatTrailZero.clicked.connect(partial(self.setFormatTrailZero, entryfield=self.configTickYFormatTrailZero, axis = targetAxis))
    self.configTickYFormatSeparator.clicked.connect(partial(self.setFormatSeparator, entryfield=self.configTickYFormatSeparator, axis = targetAxis))
    self.configTickYFormatComma.clicked.connect(partial(self.setFormatComma, entryfield=self.configTickYFormatComma, axis = targetAxis))
    self.configTickYPrefix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.configTickYPrefix, axis=targetAxis, prefix=True))
    self.configTickYPostfix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.configTickYPostfix, axis=targetAxis, prefix=False))

    if(not self.secondAxes):
      self.configTickResidYLabel.clicked.connect(partial(self.toggleTicksLabel, 'resid'))
      self.configTickResidYAuto.clicked.connect(partial(self.automaticAxisTicks, axis = 'resid'))
      self.configTickResidYEntry.editingFinished.connect(partial(self.changeAxisTicks, 'resid', updateAuto=False))
      self.configMinorTickResidLabel.clicked.connect(partial(self.toggleMinorTicks, entryfield=self.configMinorTickResidLabel, axis='resid'))
      self.configMinorTickResidRelativeLength.editingFinished.connect(partial(self.changeMinorTickRelativeLength, entryfield = self.configMinorTickResidRelativeLength, axis='resid', minval = 0.1, maxval = 10.0))
      self.configMinorTickResid.activated.connect(partial(self.setMinorTick, axis = 'resid'))
    
    # tick mark config
    if(not self.secondAxes):
      useAxes = ['bottom', 'top', 'left', 'right']
    else:
      useAxes = ['left2', 'right2']
    for axis in useAxes:
      self.configTickMarkLabel[axis].clicked.connect(partial(self.setTicksVisibility, axis=axis))
      self.configTickMarkDirection[axis].activated.connect(partial(self.setTickMarkDirection, axis=axis))
      self.configTickMarkColor[axis].clicked.connect(partial(self.changeTickMarkColor, axis=axis))
      self.configTickMarkWidth[axis].editingFinished.connect(partial(self.alterTickMarkWidth, axis=axis))
      self.configTickMarkLength[axis].editingFinished.connect(partial(self.alterTickMarkLength, axis=axis))
      self.configTickMarkLabelShow[axis].clicked.connect(partial(self.setTickMarkLabelShow, axis=axis))
    if(not self.secondAxes):
      axis = 'slaved'
      self.slavedTickMarkLabel.clicked.connect(partial(self.setTicksVisibility, axis=axis))
      self.slavedTickMarkDirection.activated.connect(partial(self.setTickMarkDirection, axis=axis))
      self.slavedTickMarkColor.clicked.connect(partial(self.changeTickMarkColor, axis=axis))
      self.slavedTickMarkWidth.editingFinished.connect(partial(self.alterTickMarkWidth, axis=axis))
      self.slavedTickMarkLength.editingFinished.connect(partial(self.alterTickMarkLength, axis=axis))
      self.slavedTickMarkLabelShow.clicked.connect(partial(self.setTickMarkLabelShow, axis=axis))

    # inner situation config
    if(not self.secondAxes):
      self.xSplitFraction.editingFinished.connect(partial(self.changeSplitFraction, 0.05, 20, True))
      self.xSplitPad.editingFinished.connect(partial(self.changeSplitPad, 0., .5 , True))
      self.configInnerLabel.clicked.connect(partial(self.toggleTicksLabel, 'x2'))
      self.configInnerAxesLabel.clicked.connect(partial(self.setInnerParameter, param='axes'))
      self.configInnerTickLabel.clicked.connect(partial(self.setInnerParameter, param='ticks'))

      self.configInnerTickXAuto.clicked.connect(partial(self.automaticAxisTicks, axis = 'x2'))
      self.configInnerTickXEntry.editingFinished.connect(partial(self.changeAxisTicks, 'x2', updateAuto=False))
      self.configInnerTickUseData.clicked.connect(partial(self.dataAxisTicks, True))

      self.configInnerTickXFormat.activated.connect(partial(self.setTickFormat, entryfield = self.configInnerTickXFormat, axis = 'x2'))
      self.configInnerTickXFormatPrecision.editingFinished.connect(partial(self.changeTickFormatPrecision, entryfield = self.configInnerTickXFormatPrecision, axis = 'x2', minval = 0, maxval = 20))
      self.configInnerTickXFormatTrailZero.clicked.connect(partial(self.setFormatTrailZero, entryfield=self.configInnerTickXFormatTrailZero, axis = 'x2'))
      self.configInnerTickXFormatSeparator.clicked.connect(partial(self.setFormatSeparator, entryfield=self.configInnerTickXFormatSeparator, axis = 'x2'))
      self.configInnerTickXFormatComma.clicked.connect(partial(self.setFormatComma, entryfield=self.configInnerTickXFormatComma, axis = 'x2'))
      self.configInnerTickXPrefix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.configInnerTickXPrefix, axis='x2', prefix=True))
      self.configInnerTickXPostfix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.configInnerTickXPostfix, axis='x2', prefix=False))

      self.configInnerMinorTickX.activated.connect(partial(self.setMinorTick, axis = 'x2'))
      self.configInnerMinorTickLabel.clicked.connect(partial(self.toggleMinorTicks, entryfield=self.configInnerMinorTickLabel, axis='x2'))

      self.configInnerDividerLine.clicked.connect(self.setSplitDivider)
      self.configInnerDividerLineColor.clicked.connect(self.changeInnerDividerLineColor)
      self.configInnerDividerLineLength.editingFinished.connect(self.alterInnerDividerLineLength)
      self.configInnerDividerLineWidth.editingFinished.connect(self.alterInnerDividerLineWidth)
      self.configInnerDividerLineDashStyle.activated.connect(self.changeInnerDividerLineDashStyle)
      self.configInnerDividerLineAngle.editingFinished.connect(self.alterInnerDividerLineAngle)
      self.configInnerDividerLineLocation.activated.connect(self.changeInnerDividerLineLocation)

    # slaved x axis config
    if(not self.secondAxes):
      self.slavedXLabel.clicked.connect(self.toggleSlavedX)
      self.slavedXReset.clicked.connect(self.resetSlavedX)
      self.slavedXPlus.clicked.connect(partial(self.transformSlavedX, '+'))
      self.slavedXMinus.clicked.connect(partial(self.transformSlavedX, '-'))
      self.slavedXTimes.clicked.connect(partial(self.transformSlavedX, '*'))
      self.slavedXDivide.clicked.connect(partial(self.transformSlavedX, '/'))
      self.slavedXPower.clicked.connect(partial(self.transformSlavedX, '**'))
      self.slavedXExponential.clicked.connect(partial(self.transformSlavedX, 'e'))
      self.slavedXNaturalLog.clicked.connect(partial(self.transformSlavedX, 'ln'))
      self.slavedXLabelShow.clicked.connect(self.toggleSlavedXLabel)
      self.configSlavedXLabel.editingFinished.connect(self.changeSlavedXLabel)
      self.slavedXColorButton.clicked.connect(partial(self.changeAxisLabelColor, axis='xs'))
      self.slavedXSize.editingFinished.connect(partial(self.alterAxisLabelSize, entryfield=self.slavedXSize, axis='xs'))
      self.slavedXBold.clicked.connect(partial(self.changeAxisLabelBold, axis='xs'))
      self.slavedXItalic.clicked.connect(partial(self.changeAxisLabelItalic, axis='xs'))
      ###self.slavedXVariant.clicked.connect(partial(self.changeAxisLabelVariant, axis='xs'))
      self.slavedXFont.activated.connect(partial(self.setAxisFont, axis='xs'))
      self.slavedXAlignment.activated.connect(partial(self.setAxisLabelAlignment, axis='xs'))
      self.slavedXAlignmentVertical.activated.connect(partial(self.setAxisLabelAlignmentVertical, axis='xs'))
      self.slavedXLinespacing.editingFinished.connect(partial(self.changeAxisLabelLinescpacing, entryfield=self.slavedXLinespacing, axis='xs', minval=0., maxval=10.))
      self.slavedXPad.editingFinished.connect(partial(self.changeAxisLabelPad, entryfield=self.slavedXPad, axis='xs', minval=-100., maxval=100.))
      self.slavedXPos.editingFinished.connect(partial(self.changeAxisLabelPos, entryfield=self.slavedXPos, axis='xs', minval=-10., maxval=10.))
      self.slavedXAngle.editingFinished.connect(partial(self.alterAxisLabelAngle, entryfield=self.slavedXAngle, axis='xs'))
      self.slavedMinorTickXLabel.clicked.connect(partial(self.toggleMinorTicks, entryfield=self.slavedMinorTickXLabel, axis='xs'))
      self.slavedMinorTickX.activated.connect(partial(self.setMinorTick, axis='xs'))
      self.slavedMinorTickXRelativeLength.editingFinished.connect(partial(self.changeMinorTickRelativeLength, entryfield = self.slavedMinorTickXRelativeLength, axis='xs', minval = 0.1, maxval = 10.0))
      self.slavedTickXColorButton.clicked.connect(partial(self.changeTickLabelColor, axis='xs'))
      self.slavedTickXSize.editingFinished.connect(partial(self.alterTickLabelSize, entryfield=self.slavedTickXSize, axis='xs'))
      self.slavedTickXBold.clicked.connect(partial(self.changeTickLabelBold, axis='xs'))
      self.slavedTickXItalic.clicked.connect(partial(self.changeTickLabelItalic, axis='xs'))
      self.slavedTickXAngle.editingFinished.connect(partial(self.alterTickLabelAngle, entryfield=self.slavedTickXAngle, axis='xs'))
      self.slavedTickXAlignment.activated.connect(partial(self.setTickLabelAlignment, axis='xs'))
      self.slavedTickXAlignmentVertical.activated.connect(partial(self.setTickLabelAlignmentVertical, axis='xs'))
      self.slavedTickXLinespacing.editingFinished.connect(partial(self.changeTickLabelLinespacing, entryfield=self.slavedTickXLinespacing, axis='xs', minval=0., maxval=10.))
      self.slavedTickXPad.editingFinished.connect(partial(self.changeTickLabelPad, entryfield = self.slavedTickXPad, axis='xs', minval = -100.0, maxval = 100.0))
      self.slavedTickXPad2.editingFinished.connect(partial(self.changeTickLabelPad2, entryfield = self.slavedTickXPad2, axis='xs', minval = -100.0, maxval = 100.0))
      self.slavedTickXFont.activated.connect(partial(self.setTickFont, axis='xs'))
      self.slavedXFormat.activated.connect(partial(self.setTickFormat, entryfield = self.slavedXFormat, axis = 'xs'))
      self.slavedXFormatPrecision.editingFinished.connect(partial(self.changeTickFormatPrecision, entryfield = self.slavedXFormatPrecision, axis = 'xs', minval = 0, maxval = 20))
      self.slavedXFormatTrailZero.clicked.connect(partial(self.setFormatTrailZero, entryfield=self.slavedXFormatTrailZero, axis = 'xs'))
      self.slavedXFormatSeparator.clicked.connect(partial(self.setFormatSeparator, entryfield=self.slavedXFormatSeparator, axis = 'xs'))
      self.slavedXFormatComma.clicked.connect(partial(self.setFormatComma, entryfield=self.slavedXFormatComma, axis = 'xs'))
      self.slavedXPrefix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.slavedXPrefix, axis='xs', prefix=True))
      self.slavedXPostfix.editingFinished.connect(partial(self.setFormatFix, entryfield=self.slavedXPostfix, axis='xs', prefix=False))
      self.slavedTickXAuto.clicked.connect(partial(self.automaticAxisTicks, axis='xs'))
      self.slavedTickXEntry.editingFinished.connect(partial(self.changeAxisTicks, 'xs', updateAuto=True))

    # grid config
    if(not self.secondAxes):
      useAxes = ['x', 'y', 'x2']
    else:
      useAxes = ['y2']
    for axis in useAxes:
      self.configGridLabel[axis].clicked.connect(partial(self.setGridVisibility, axis=axis))
      self.configGridColor[axis].clicked.connect(partial(self.changeGridColor, axis=axis))
      self.configGridWidth[axis].editingFinished.connect(partial(self.alterGridWidth, axis=axis))
      self.configGridStyle[axis].activated.connect(partial(self.setGridStyle, axis=axis))
      self.configGridDashStyle[axis].activated.connect(partial(self.setGridDashStyle, axis=axis))
      self.configGridOrder[axis].activated.connect(partial(self.setGridOrder, axis=axis))
      # minor grid
      self.configGridLabel2[axis].clicked.connect(partial(self.setGridMinorVisibility, axis=axis))
      self.configGridColor2[axis].clicked.connect(partial(self.changeGridMinorColor, axis=axis))
      self.configGridWidth2[axis].editingFinished.connect(partial(self.alterGridMinorWidth, axis=axis))
      self.configGridStyle2[axis].activated.connect(partial(self.setGridMinorStyle, axis=axis))
      self.configGridDashStyle2[axis].activated.connect(partial(self.setGridMinorDashStyle, axis=axis))
      self.configGridOrder2[axis].activated.connect(partial(self.setGridMinorOrder, axis=axis))
      # shady business
      self.configGridLabel3[axis].clicked.connect(partial(self.setGridRectVisibility, axis=axis))
      self.configGridRectColor[axis].clicked.connect(partial(self.changeGridRectColor, axis=axis))
      
    # splitY controls
    if(self.secondAxes):
      self.configSplitYLabel.clicked.connect(self.toggleSplitY)
      self.configSplitYFraction.editingFinished.connect(partial(self.changeSplitYFraction, minval=0.05, maxval=20.0))
      self.configSplitYPad.editingFinished.connect(partial(self.changeSplitYPad, 0., .5))
      self.configSplitYAxesLabel.clicked.connect(partial(self.setInnerYParameter, param='axes'))
      self.configSplitYTickLabel.clicked.connect(partial(self.setInnerYParameter, param='ticks'))

      self.configSplitYDividerLine.clicked.connect(self.setSplitYDivider)
      self.configSplitYDividerLineColor.clicked.connect(self.changeSplitYDividerLineColor)
      self.configSplitYDividerLineLength.editingFinished.connect(self.alterSplitYDividerLineLength)
      self.configSplitYDividerLineWidth.editingFinished.connect(self.alterSplitYDividerLineWidth)
      self.configSplitYDividerLineDashStyle.activated.connect(self.changeSplitYDividerLineDashStyle)
      self.configSplitYDividerLineAngle.editingFinished.connect(self.alterSplitYDividerLineAngle)
      self.configSplitYDividerLineLocation.activated.connect(self.changeSplitYDividerLineLocation)

    # legend config
    if(not self.secondAxes):
      self.configLegendLabel.clicked.connect(self.setLegend)
      self.configLegendPlacement.activated.connect(self.setLegendPlacement)
      for prop in ['face', 'edge']:
        self.configLegendColor[prop].clicked.connect(partial(self.changeLegendColor, prop))
      self.configLegendColorLabel['face'].clicked.connect(self.toggleLegendFill)
      self.configLegendColorLabel['edge'].clicked.connect(self.toggleLegendEdge)
      self.configLegendEdgeWidth.editingFinished.connect(self.alterLegendEdgeWidth)
      self.configLegendX.editingFinished.connect(partial(self.changeLegendPosition, 'x', minval=-1.0, maxval=2.0))
      self.configLegendY.editingFinished.connect(partial(self.changeLegendPosition, 'y', minval=-1.0, maxval=2.0))
      self.legendComboHatchStyle.activated.connect(partial(self.changeLegendHatch, item='hatch'))
      self.legendHatchLineWidthSpin.editingFinished.connect(partial(self.changeLegendHatch, item='linewidth'))
      self.legendComboHatchMultiplyStyle.activated.connect(partial(self.changeLegendHatch, item='multiply'))
      self.configLegendRounding.editingFinished.connect(partial(self.changeLegendRounding, minval=0.0, maxval=10.0))
      self.configLegendShadowLabel.clicked.connect(self.setLegendShadow)
      self.configLegendShadowColor.clicked.connect(self.changeLegendShadowColor)
      self.configLegendShadowOffX.editingFinished.connect(partial(self.changeLegendShadowOffset, direction='x', minval=-100.0, maxval=100.0))
      self.configLegendShadowOffY.editingFinished.connect(partial(self.changeLegendShadowOffset, direction='y', minval=-100.0, maxval=100.0))
      self.configLegendLabelColor.clicked.connect(self.changeLegendLabelColor)
      self.configLegendLinespacing.editingFinished.connect(partial(self.alterLegendLabelLinespacing, entryfield=self.configLegendLinespacing, minval=0., maxval=10.))
      self.configLegendLabelSize.editingFinished.connect(partial(self.alterLegendLabelSize, entryfield=self.configLegendLabelSize))
      self.configLegendLabelBold.clicked.connect(self.changeLegendLabelBold)
      self.configLegendLabelItalic.clicked.connect(self.changeLegendLabelItalic)
      self.configLegendLabelFont.activated.connect(self.setLegendLabelFont)
      self.configLegendNCol.activated.connect(self.setLegendNCol)
      self.configLegendMarkerFirstLabel.clicked.connect(self.setLegendMarkerFirst)
      self.configLegendMarkerScale.editingFinished.connect(partial(self.changeLegendMarkerScale, entryfield = self.configLegendMarkerScale, minval = 0.0, maxval = 5.0))
      self.configLegendMarkerNumPoints.activated.connect(self.setLegendMarkerNumPoints)
      self.configLegendPadBorder.editingFinished.connect(partial(self.changeLegendPadBorder, entryfield = self.configLegendPadBorder, minval = 0.0, maxval = 10.0))
      self.configLegendPadRow.editingFinished.connect(partial(self.changeLegendPadRow, entryfield = self.configLegendPadRow, minval = 0.0, maxval = 10.0))
      self.configLegendPadCol.editingFinished.connect(partial(self.changeLegendPadCol, entryfield = self.configLegendPadCol, minval = 0.0, maxval = 50.0))
      self.configLegendLabelPad.editingFinished.connect(partial(self.changeLegendPadLabel, entryfield=self.configLegendLabelPad, minval=0., maxval=10.))

    # canvas config
    if(not self.secondAxes):
      self.configFigureColorButton.clicked.connect(self.changeFigureColor)  
      self.configCanvasColorButton.clicked.connect(self.changeCanvasColor)
      self.configFigureLabel.clicked.connect(self.toggleFigureFill)
      self.configCanvasLabel.clicked.connect(self.toggleCanvasFill)
      self.configFrameLabel.clicked.connect(self.toggleFrameDraw)
      
      # canvas gradient
      self.configCanvasGradientLabel.clicked.connect(self.setCanvasGradient)
      self.configCanvasGradientStyle.activated.connect(self.changeCanvasGradientStyle)
      self.configCanvasGradientColor1Button.clicked.connect(partial(self.changeCanvasGradientColor, color=0))
      self.configCanvasGradientColor2Button.clicked.connect(partial(self.changeCanvasGradientColor, color=1))
      self.configCanvasGradientAngle.editingFinished.connect(self.changeCanvasGradientAngle)
      self.configCanvasGradientCenterX.editingFinished.connect(partial(self.changeCanvasGradientCenter, minval=0.0, maxval=1.0, axis='x'))
      self.configCanvasGradientCenterY.editingFinished.connect(partial(self.changeCanvasGradientCenter, minval=0.0, maxval=1.0, axis='y'))
      self.configCanvasGradientWidth.editingFinished.connect(partial(self.changeCanvasGradientWidth, minval=0.01, maxval=5.0))
      
      # canvas dimensions
      self.exportSizeX.editingFinished.connect(partial(self.checkExportSize, entryfield = self.exportSizeX, axis='x', minval = 0.05, maxval = 500.0))
      self.exportSizeY.editingFinished.connect(partial(self.checkExportSize, entryfield = self.exportSizeY, axis='y', minval = 0.05, maxval = 500.0))
      self.exportSizeCurrentButton.clicked.connect(self.useCurrentDim)
      
      # pad graphics
      for axis in ['bottom', 'left']:
        self.exportPadEntry[axis].editingFinished.connect(partial(self.changePadding, axis = axis, minval = 0.0, maxval = 0.49))
      for axis in ['top', 'right']:
        self.exportPadEntry[axis].editingFinished.connect(partial(self.changePadding, axis = axis, minval = 0.50, maxval = 1.00))
      
      # frame
      self.configFrameColor.clicked.connect(self.changeFrameColor)
      self.configFrameWidth.editingFinished.connect(self.alterFrameWidth)
      self.configFrameStyle.activated.connect(self.setFrameStyle)
      self.configFrameDashStyle.activated.connect(self.setFrameDashStyle)
    
    # xkcd
    if(not self.secondAxes):
      self.configXkcdLabel.clicked.connect(partial(self.setXkcd,True))
      self.xkcdScale.editingFinished.connect(partial(self.checkXkcdSetting, entryfield = self.xkcdScale, item='scale', minval = 0.0, maxval = 10.0))
      self.xkcdLength.editingFinished.connect(partial(self.checkXkcdSetting, entryfield = self.xkcdLength, item='length', minval = 0.0, maxval = 1000.0))
      self.xkcdRandom.editingFinished.connect(partial(self.checkXkcdSetting, entryfield = self.xkcdRandom, item='random', minval = 0.0, maxval = 10.0))
      
      # path effects
      self.configPathEffectsLabel.clicked.connect(partial(self.setPathStroke, True))
      self.configPathEffectsColorButton.clicked.connect(self.changePathStrokeColor)
      self.configPathEffectsWidth.editingFinished.connect(self.alterPathStrokeWidth)
  
      self.configPathShadowLabel.clicked.connect(partial(self.setPathShadow, True))
      self.configPathShadowColorButton.clicked.connect(self.changePathShadowColor)
      self.configPathShadowOffX.editingFinished.connect(partial(self.changePathShadowOffset, direction='x', minval=-100.0, maxval=100.0))
      self.configPathShadowOffY.editingFinished.connect(partial(self.changePathShadowOffset, direction='y', minval=-100.0, maxval=100.0))

    # advanced graphics
    if(not self.secondAxes):
      self.configAdvancedSettingsButton.clicked.connect(self.toggleGraphicsOption)

    # preview and export graphics
    if(not self.secondAxes):
      self.previewButton.clicked.connect(self.previewThis)
      self.exportButton.clicked.connect(self.exportThis)
      self.reportButton.clicked.connect(self.reportThis)
    
      # load/save style
      self.loadStyleSet.clicked.connect(partial(self.processStyleSet, 'load', 'file'))
      self.saveStyleSet.clicked.connect(partial(self.processStyleSet, 'save', 'file'))

  def processStyleSet(self, operation='load', modus='file', zoffsetData=0, zoffsetCurve=0, redraw=True, rescale=True):
    # loads/saves style set
    if(operation in ['load', 'save']):
      # items in plot style to save
      items = ['labelX', 'labelXShow', 'labelXColor', 'labelXSize', 'labelY', 'labelYShow', 'labelYColor', 'labelYSize',\
        'labelXWeight', 'labelYWeight', 'labelXStyle', 'labelYStyle', 'labelXVariant', 'labelYVariant', 'labelXAlignment', 'labelYAlignment', 'labelXLinespacing', 'labelYLinespacing',\
        'labelXAlignmentVertical', 'labelYAlignmentVertical', 'labelXPad', 'labelYPad', 'labelXPos', 'labelYPos', 'labelXAngle', 'labelYAngle',\
        'axisVisible', 'axisColor', 'axisPosition', 'axisPositionValue', 'axisBoundaryCheck', 'axisBoundary', 'axisWidth', 'axisStyle', 'axisDashStyle',\
        'ticksX', 'ticksXShow', 'ticksXColor', 'ticksXSize', 'ticksXAngle',\
        'ticksXWeight', 'ticksYWeight', 'ticksXStyle', 'ticksYStyle',\
        'ticksXAlignment', 'ticksXAlignmentVertical', 'ticksXLinespacing', 'ticksYLinespacing', 'ticksXPad', 'ticksXPad2', 'ticksXLabel', 'ticksXAuto', 'ticksXMinor', \
        'ticksXFormat', 'ticksYFormat', 'ticksY2Format', 'ticksResidYFormat',\
        'ticksXFormatPrecision', 'ticksYFormatPrecision', 'ticksY2FormatPrecision', 'ticksResidYFormatPrecision',\
        'ticksXFormatTrailZero', 'ticksYFormatTrailZero', 'ticksY2FormatTrailZero', 'ticksResidYFormatTrailZero',\
        'ticksXFormatSeparator', 'ticksYFormatSeparator', 'ticksY2FormatSeparator', 'ticksResidYFormatSeparator',\
        'ticksXFormatComma', 'ticksYFormatComma', 'ticksY2FormatComma', 'ticksResidYFormatComma',\
        'ticksXFormatPrefix', 'ticksYFormatPrefix', 'ticksY2FormatPrefix', 'ticksResidYFormatPrefix',\
        'ticksXFormatPostfix', 'ticksYFormatPostfix', 'ticksY2FormatPostfix', 'ticksResidYFormatPostfix',\
        'ticksXFormat_div', 'ticksXFormatPrecision_div', 'ticksXFormatTrailZero_div', 'ticksXFormatSeparator_div', 'ticksXFormatComma_div', 'ticksXFormatPrefix_div', 'ticksXFormatPostfix_div',\
        'ticksX_div', 'ticksXShow_div', 'ticksXAuto_div', 'ticksXLabel_div', 'ticksXMinor_div',\
        'ticksY', 'ticksYShow', 'ticksResidYShow', 'ticksYColor', 'ticksYSize', 'ticksYAngle', 'ticksYAlignment', 'ticksYAlignmentVertical', 'ticksYPad', 'ticksYPad2',\
        'ticksYAuto','ticksYMinor', 'ticksY2Minor', 'ticksResidY', 'ticksResidYAuto', 'ticksVisible', 'ticksResidYMinor', 'ticksDirection', 'ticksLabelShow', 'ticksColor', 'ticksWidth',\
        'ticksLength', 'ticksXMinorRelativeLength', 'ticksYMinorRelativeLength', 'ticksY2MinorRelativeLength', 'ticksResidYMinorRelativeLength',\
        'gridVisible', 'gridColor', 'gridWidth', 'gridStyle', 'gridDashStyle', 'gridOrder', \
        'gridMinorVisible', 'gridMinorColor', 'gridMinorWidth', 'gridMinorStyle', 'gridMinorDashStyle', 'gridMinorOrder', \
        'gridRectVisible', 'gridRectColor', \
        'labelY2', 'labelY2Show', 'labelY2Color', 'labelY2Size', 'labelY2Weight', 'labelY2Style', 'labelY2Variant',\
        'labelY2Pad', 'labelY2Pos', 'labelY2Angle', 'labelY2Alignment', 'labelY2AlignmentVertical',\
        'ticksY2', 'ticksY2Show', 'ticksY2Color',\
        'ticksY2Size', 'ticksY2Angle', 'ticksY2Alignment', 'ticksY2AlignmentVertical', 'ticksY2Pad', 'ticksY2Pad2', 'ticksYLabel', 'ticksY2Label', 'ticksY2Auto', 'ticksY2Weight', 'ticksY2Style',\
        'figureColor', 'canvasColor', 'canvasFill', 'figureFill', 'frameDraw', 'exportWidth', 'exportHeight', 'axisFont', 'tickFont', 'padSize',\
        'frameWidth', 'frameColor', 'frameStyle', 'frameDashStyle',\
        'legendVisible', 'legendPlacement', 'legendX', 'legendY', 'legendColor', 'legendEdgeWidth', 'legendShadow', 'legendShadowDeltaX', 'legendShadowDeltaY', 'legendShadowFaceColor', 'legendLabelColor',\
        'legendLabelSize', 'legendLabelLinespacing', 'legendLabelWeight', 'legendLabelStyle', 'legendHatch', 'legendHatchMultiply', 'legendHatchLinewidth', 'legendRounding', 'legendLabelFont', 'legendNumPoints',\
        'legendMarkerFirst', 'legendNumCol', 'legendMarkerScale', 'legendBorderPad', 'legendLabelSpacing', 'legendLabelPad', 'legendColumnSpacing', 'legendFill', 'legendEdge',\
        'xkcd', 'xkcdScale', 'xkcdLength', 'xkcdRandomness',\
        'applyPathStroke', 'pathStrokeWidth', 'pathStrokeColor',\
        'applyPathShadow', 'pathShadowX', 'pathShadowY', 'pathShadowColor',\
        'arrowVisible', 'arrowOverhang', 'arrowColor', 'arrowFill', 'arrowHeadLength', 'arrowHeadWidth', 'arrowOffset', 'arrowEdge', 'arrowEdgeShow', 'arrowFillShow', 'arrowLocation', 'arrowZ',\
        'canvasGradientVisible', 'canvasGradientStyle','canvasGradientColor1', 'canvasGradientColor2', 'canvasGradientAngle', 'canvasGradientWidth', 'canvasGradientCenter',\
        'splitFraction', 'splitPad', 'innerAxes', 'innerTicks', 'splitDivider', 'splitDividerLength', 'splitDividerWidth',\
        'splitDividerColor', 'splitDividerDashStyle', 'splitDividerAngle', 'splitDividerLocation',\
        'splitYFraction', 'splitYPad', 'innerAxesY', 'innerTicksY', 'splitYDivider', 'splitYDividerLength', 'splitYDividerWidth',\
        'splitYDividerColor', 'splitYDividerDashStyle', 'splitYDividerAngle', 'splitYDividerLocation', 'themeUI',\
        'slavedX', 'slavedXLabel', 'slavedXLabelShow', 'slavedLabelXColor', 'slavedLabelXSize', 'slavedLabelXWeight', 'slavedLabelXStyle',\
        'slavedLabelXVariant', 'slavedLabelXPad', 'slavedLabelXLinespacing', 'slavedLabelXPos',\
        'slavedXTransform', 'slavedXFormat', 'slavedXFormatPrecision',\
        'slavedLabelXAngle', 'slavedLabelXAlignment', 'slavedLabelXAlignmentVertical',\
        'slavedTicksXMinor', 'slavedTicksXMinorRelativeLength', 'slavedTicksXColor', 'slavedTicksXSize', 'slavedTicksXWeight',\
        'slavedTicksXStyle', 'slavedTicksXAngle', 'slavedTicksXAlignment', 'slavedTicksXAlignmentVertical',\
        'slavedTicksXLinespacing', 'slavedTicksXPad', 'slavedTicksXPad2',\
        'slavedXFormatTrailZero', 'slavedXFormatSeparator', 'slavedXFormatComma', 'slavedXFormatPrefix', 'slavedXFormatPostfix']
      dataobject = ['dataSet', 'dataError', 'dataErrorShady', 'dataBar', 'dataStack', 'dataText', 'dataViolin', 'dataScatter']
      fitobject = ['curve', 'cboot']
      residobject = ['resid', 'residBar', 'residStack', 'residText', 'residScatter']
      residobjectzero = 'residZero'
      flag = True

      if(operation == 'load'):
        # store original axis limits to counteract rescaling when applying styles
        minX, maxX, minY, maxY = self.parent.plotArea.minX, self.parent.plotArea.maxX, self.parent.plotArea.minY, self.parent.plotArea.maxY
        minResidY, maxResidY = self.parent.plotArea.minResidY, self.parent.plotArea.maxResidY
        minY2, maxY2 = self.parent.plotArea.minY2, self.parent.plotArea.maxY2
        # load from file or from string?
        if(modus == 'file'):
          filename, filter_ = QtWidgets.QFileDialog.getOpenFileName(self, filter='Style set (*.style)', directory = WORKINGDIR + PATH_SEPARATOR + 'styles' + PATH_SEPARATOR, caption='Open Style Sheet')
          filename = str(filename)
          try:
            with open(filename,'r', encoding='utf-8') as loadhandle:
              lines = loadhandle.readlines()
            # save filename for future reference
            self.currStyleFile = filename
            # display busy pointer
            QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
            QtCore.QCoreApplication.processEvents()
          except:
            if(filename):
              self.parent.statusbar.showMessage('Cannot load style file ' + filename, self.parent.STATUS_TIME)
            lines = []
            flag = False
        else:
          lines = modus.split('\n')
        
        if(flag):
          # reset stored styles
          self.parent.dataStyles, self.parent.residStyles, self.parent.fitStyles = {}, {}, {}
          
          entry = ''
          for red in lines:
            if(red.find('>>>') == 0):
              entry = red[3:].strip()
              # replace underscore in key names to avoid mixup with 
              if('000' in entry):
                entry = entry.replace('000', '_')
            elif(entry != ''):
              red = red.strip()
              # convert string input to original data
              try:
                red = literal_eval(red)
                # assign value to plot canvas
                if(hasattr(self.parent.plotArea, entry)):
                  # test whether this is a dictionary
                  if(type(red) == dict):
                    self.parent.plotArea.__dict__[entry].update(red)
                  else:
                    setattr(self.parent.plotArea, entry, red)
              except:
                self.parent.statusbar.showMessage('Failed to restore setting ' + repr(entry) + repr(red), self.parent.STATUS_TIME)
              entry = ''
              
          # loadconfig of data set and curve
          entry = ''
          for red in lines:
            if(red.find('>>>') == 0):
              entry = red[3:].strip()
            elif(entry != ''):
              if('_' in entry):
                splitentry = entry.split('_')
                entry = splitentry[0]
                try:
                  index = int(splitentry[-1])
                except:
                  index = 0
                
              if((entry in dataobject) or (entry in residobject) or (entry in fitobject + [residobjectzero])):
                red = red.strip()
                # convert string input to original data
                try:
                  red = literal_eval(red)
                  # remember styles for future objects
                  if(entry in dataobject):
                    if(not (index in self.parent.dataStyles)):
                      self.parent.dataStyles[index] = {}
                    self.parent.dataStyles[index][entry] = red
                  elif(entry in residobject):
                    if(not (index in self.parent.residStyles)):
                      self.parent.residStyles[index] = {}
                    self.parent.residStyles[index][entry] = red
                  elif(entry in fitobject):
                    if(not (index in self.parent.fitStyles)):
                      self.parent.fitStyles[index] = {}
                    self.parent.fitStyles[index][entry] = red
                  elif(entry == residobjectzero):
                    # apply styles to resid line immediately
                    if(index + zoffsetData < len(self.parent.data)):
                      for key in red:
                        self.parent.data[index + zoffsetData].setResidLineStyle(key, red[key], redraw=False)
                except:
                  self.parent.statusbar.showMessage('Failed to restore setting ' + repr(entry) + repr(red), self.parent.STATUS_TIME)
  
              entry = ''
              
          # jointly apply styles to new curve, data and residuals objects
          for index, entry in enumerate(self.parent.data[zoffsetData:]):
            entry.setDataStyleSet(index=index)
            entry.setResidStyleSet(index=index)
            # update styles via drawMe() rather than styleMe() to deal with split axes
            entry.drawMe(redraw=False, rescale=False)
            entry.drawMeResid(redraw=False, rescale=False)
          for index, entry in enumerate(self.parent.fit[zoffsetCurve:]):
            entry.setFitStyleSet(index=index)
            # update styles via drawMe() rather than styleMe() to deal with split axes
            entry.drawMe(redraw=False, rescale=False)

          # cause plot to be redrawn
          self.parent.plotArea.initPlot(initialize=False)
          # update entry fields
          self.updateFields(initialize=False)
          self.parent.graphicsarea2.updateFields(initialize=False)
          # check whether we have items on the second axes
          self.parent.objectsarea.checkSecondAxes()
              
          # rescale to original axis limits
          if(rescale):
            self.parent.plotArea.setAxisLimits(lower=minX, upper=maxX, axis='x', updateLabel=True, target='plot', redraw=False, updateGrid=True)
            self.parent.plotArea.setAxisLimits(lower=minY2, upper=maxY2, axis='y2', updateLabel=True, target='plot', redraw=False, updateGrid=True)
            self.parent.plotArea.setAxisLimits(lower=minY, upper=maxY, axis='y', updateLabel=True, target='plot', redraw=False, updateGrid=True)
            self.parent.plotArea.setAxisLimits(lower=minX, upper=maxX, axis='x', updateLabel=True, target='resid', redraw=False, updateGrid=True)
            self.parent.plotArea.setAxisLimits(lower=minResidY, upper=maxResidY, axis='y', updateLabel=True, target='resid', redraw=False, updateGrid=True)
            # adjust tick entry fields
            for axis in ['x', 'x2', 'y', 'y2', 'resid']:
              self.parent.plotArea.updateTickEntryField(axis=axis)

          # draw resid line (again) to ensure coverage of entire x range
          if(self.parent.plotArea.visibilityResidLine):
            self.parent.plotArea.handleResidZero = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero, redraw=False)
            if(self.parent.plotArea.splitShow):
              self.parent.plotArea.handleResidZero_div = self.parent.plotArea.plotResidZero(self.parent.plotArea.handleResidZero_div, redraw=False, splitX=True)

          # separately deal with legend so that it updates
          value = self.parent.plotArea.legendVisible
          self.parent.plotArea.setLegend(value=value, redraw=redraw)
          
          # update resid plot?
          if(redraw):
            self.parent.plotArea.residplotwidget.myRefresh()

          # check whether a different UI theme has been set
          if(UI_STYLE != myapp.ui.plotArea.themeUI):
            setColorScheme(value=self.parent.plotArea.themeUI, caller=None, initOnly=False)
            self.parent.plotArea.themeUI = UI_STYLE

          # restore original pointer (if needed)
          if(modus == 'file'):
            QtWidgets.QApplication.restoreOverrideCursor()
      else:
        # retrieve plot configuration
        red = ''
        for entry in items:
          if(hasattr(self.parent.plotArea, entry)):
            # replace underscore in key names to avoid mixup with 
            if('_' in entry):
              red += '>>>' + entry.replace('_', '000') + '\n'
            else:
              red += '>>>' + entry + '\n'
            tempOut = self.parent.plotArea.__dict__[entry]
            if(hasattr(tempOut, 'tolist')):
              tempOut = tempOut.tolist()
            if(type(tempOut) is str):
              red += repr(tempOut) + '\n'
            else:
              red += str(tempOut) + '\n'
        # save config of data set and curve
        for index in range(len(self.parent.data)):
          red += '>>>' + dataobject[0] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getStyle()) + '\n'
          #red += str(self.parent.data[index].getStyle()) + '\n'
          red += '>>>' + dataobject[1] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getErrorStyle()) + '\n'
          #red += str(self.parent.data[index].getErrorStyle()) + '\n'
          red += '>>>' + dataobject[2] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getErrorShadyStyle()) + '\n'
          #red += str(self.parent.data[index].getErrorShadyStyle()) + '\n'
          red += '>>>' + dataobject[3] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getBarStyle()) + '\n'
          #red += str(self.parent.data[index].getBarStyle()) + '\n'
          red += '>>>' + dataobject[4] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getStackStyle()) + '\n'
          #red += str(self.parent.data[index].getStackStyle()) + '\n'
          red += '>>>' + dataobject[5] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getTextStyle()) + '\n'
          #red += str(self.parent.data[index].getTextStyle()) + '\n'
          red += '>>>' + dataobject[6] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getViolinStyle()) + '\n'
          #red += str(self.parent.data[index].getViolinStyle()) + '\n'
          red += '>>>' + dataobject[7] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getScatterStyle()) + '\n'
          #red += str(self.parent.data[index].getScatterStyle()) + '\n'
          red += '>>>' + residobject[0] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getResidStyle()) + '\n'
          #red += str(self.parent.data[index].getResidStyle()) + '\n'
          red += '>>>' + residobject[1] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getResidBarStyle()) + '\n'
          #red += str(self.parent.data[index].getResidBarStyle()) + '\n'
          red += '>>>' + residobject[2] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getResidStackStyle()) + '\n'
          #red += str(self.parent.data[index].getResidStackStyle()) + '\n'
          red += '>>>' + residobject[3] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getResidTextStyle()) + '\n'
          #red += str(self.parent.data[index].getResidTextStyle()) + '\n'
          red += '>>>' + residobject[4] + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getResidScatterStyle()) + '\n'
          #red += str(self.parent.data[index].getResidScatterStyle()) + '\n'
          red += '>>>' + residobjectzero + '_' + str(index) + '\n'
          red += repr(self.parent.data[index].getResidLineStyle()) + '\n'
          #red += str(self.parent.data[index].getResidLineStyle()) + '\n'
        
        for index in range(len(self.parent.fit)):
          red += '>>>' + fitobject[0] + '_' + str(index) + '\n'
          red += repr(self.parent.fit[index].getStyle()) + '\n'
          #red += str(self.parent.fit[index].getStyle()) + '\n'
          red += '>>>' + fitobject[1] + '_' + str(index) + '\n'
          red += repr(self.parent.fit[index].getBootStyle()) + '\n'
          #red += str(self.parent.fit[index].getBootStyle()) + '\n'

        if (modus == 'file'):
          usedir = WORKINGDIR + PATH_SEPARATOR + 'styles' + PATH_SEPARATOR
          if(self.currStyleFile != None):
            usedir = self.currStyleFile
          filename, filter_ = QtWidgets.QFileDialog.getSaveFileName(self, filter='Style set (*.style)', directory = usedir, caption='Save Style Sheet')
          filename = str(filename)
          try:
            with open(filename,'w', encoding='utf-8') as savehandle:
              savehandle.write(red)
            # save filename for future reference
            self.currStyleFile = filename
          except:
            self.parent.statusbar.showMessage('Cannot write style file ' + filename, self.parent.STATUS_TIME)
        else:
          return red

  def magicTickstring(self, ticks=[]):
    # ensures better formatting of tick values in tickstring
    if(not len(ticks)):
      return ''

    # define nested helper function
    def formatThis(number):
      NUMBER_SWITCH = 1e3
      FORMAT_DECIMAL = '{:.3f}'
      FORMAT_SCIENTIFIC = '{:.3e}'
      # determine return string
      try:
        if(number == 0.0):
          return '0'
        
        if(np.isfinite(float(number))):
          if((np.abs(number) >= 10 * NUMBER_SWITCH) or (np.abs(number) < 1. / NUMBER_SWITCH)):
            numberstr = FORMAT_SCIENTIFIC.format(number)
            numberstr = numberstr.lower()
            # chop away trailing zeros and decimal dots
            pre, post = numberstr.split('e')
            while pre.endswith('0'):
              pre = pre[:-1]
            if(pre.endswith('.')):
              pre = pre[:-1]
            numberstr = pre + 'e' + post
          else:
            numberstr = FORMAT_DECIMAL.format(number)
            # chop away trailing zeros and decimal dots
            while numberstr.endswith('0'):
              numberstr = numberstr[:-1]
            if(numberstr.endswith('.')):
              numberstr = numberstr[:-1]
        else:
          numberstr = str(number)
        
        return numberstr
      except:
        return ''
    
    tickli = [formatThis(i) for i in ticks]
    tickstr = ', '.join([i for i in tickli if len(i)])
    return tickstr

  def changeSplitFraction(self, minval=0, maxval=1, redraw=True):
    # check paramter boundaries
    try:
      value = float(self.xSplitFraction.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.xSplitFraction.setText(str(value))

    self.parent.plotArea.changeSplitFraction(value=value, redraw=redraw)

  def changeSplitPad(self, minval=0, maxval=1, redraw=True):
    # check paramter boundaries
    try:
      value = float(self.xSplitPad.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.xSplitPad.setText(str(value))

    self.parent.plotArea.changeSplitPad(value=value, redraw=redraw)

  def alterInnerDividerLineAngle(self):
    # change angle of split x axis divider lines
    try:
      value = float(self.configInnerDividerLineAngle.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configInnerDividerLineAngle.setValue(value)

    self.parent.plotArea.updateSplitDivider(param='angle', value=value, redraw=True)

  def changeInnerDividerLineLocation(self):
    # change location of split x axis divider lines
    style = str(self.configInnerDividerLineLocation.currentText())
    self.parent.plotArea.updateSplitDivider(param='location', value=style, redraw=True)

  def changeInnerDividerLineDashStyle(self):
    # change dash style of split x axis divider lines
    style = str(self.configInnerDividerLineDashStyle.currentText())
    self.parent.plotArea.updateSplitDivider(param='dash', value=style, redraw=True)

  def alterInnerDividerLineWidth(self):
    # change width of split x axis divider lines
    try:
      value = float(self.configInnerDividerLineWidth.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configInnerDividerLineWidth.setValue(value)

    self.parent.plotArea.updateSplitDivider(param='width', value=value, redraw=True)

  def alterInnerDividerLineLength(self):
    # change length of split x axis divider lines
    try:
      value = float(self.configInnerDividerLineLength.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configInnerDividerLineLength.setValue(value)

    self.parent.plotArea.updateSplitDivider(param='length', value=value, redraw=True)

  def changeInnerDividerLineColor(self):
    # change color of split x axis divider lines
    prevColor = [int(255. * i) for i in self.parent.plotArea.splitDividerColor]
    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.updateSplitDivider(param='color', value=value, redraw=True)
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.splitDividerColor[0:3]]
      self.configInnerDividerLineColor.setMyColor(*colorvalue)

  def alterSplitYDividerLineAngle(self):
    # change angle of split y axis divider lines
    try:
      value = float(self.configSplitYDividerLineAngle.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configSplitYDividerLineAngle.setValue(value)

    self.parent.plotArea.updateSplitYDivider(param='angle', value=value, redraw=True)

  def changeSplitYDividerLineLocation(self):
    # change location of split y axis divider lines
    style = str(self.configSplitYDividerLineLocation.currentText())
    self.parent.plotArea.updateSplitYDivider(param='location', value=style, redraw=True)

  def changeSplitYDividerLineDashStyle(self):
    # change dash style of split y axis divider lines
    style = str(self.configSplitYDividerLineDashStyle.currentText())
    self.parent.plotArea.updateSplitYDivider(param='dash', value=style, redraw=True)

  def alterSplitYDividerLineWidth(self):
    # change width of split y axis divider lines
    try:
      value = float(self.configSplitYDividerLineWidth.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configSplitYDividerLineWidth.setValue(value)

    self.parent.plotArea.updateSplitYDivider(param='width', value=value, redraw=True)

  def alterSplitYDividerLineLength(self):
    # change length of split y axis divider lines
    try:
      value = float(self.configSplitYDividerLineLength.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configSplitYDividerLineLength.setValue(value)

    self.parent.plotArea.updateSplitYDivider(param='length', value=value, redraw=True)

  def changeSplitYDividerLineColor(self):
    # change color of split y axis divider lines
    prevColor = [int(255. * i) for i in self.parent.plotArea.splitYDividerColor]
    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.updateSplitYDivider(param='color', value=value, redraw=True)
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.splitYDividerColor[0:3]]
      self.configSplitYDividerLineColor.setMyColor(*colorvalue)

  def toggleSplitY(self):
    # toggles split y axes
    state = self.configSplitYLabel.isChecked()
    self.parent.plotArea.setSplitY(value=state, redraw=True)

  def changeSplitYFraction(self, minval=0, maxval=1, redraw=True):
    # check paramter boundaries
    try:
      value = float(self.configSplitYFraction.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.configSplitYFraction.setText(str(value))

    self.parent.plotArea.changeSplitYFraction(value=value, redraw=redraw)

  def changeSplitYPad(self, minval=0, maxval=0.5, redraw=True):
    # check paramter boundaries
    try:
      value = float(self.configSplitYPad.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.configSplitYPad.setText(str(value))

    self.parent.plotArea.changeSplitYPad(value=value, redraw=redraw)

  def setInnerYParameter(self, param='axes'):
    if(param in ['axes', 'ticks']):
      if(param == 'axes'):
        state = self.configSplitYAxesLabel.isChecked()
      else:
        state = self.configSplitYTickLabel.isChecked()
        
      self.parent.plotArea.setInnerYParameter(param=param, state=state, redraw=True)
          
  def setSplitYDivider(self):
    # toggles display of split divider lines
    state = self.configSplitYDividerLine.isChecked()
    self.parent.plotArea.setSplitYDivider(value=state, redraw=True)

  def setSplitDivider(self):
    # toggles display of split divider lines
    state = self.configInnerDividerLine.isChecked()
    self.parent.plotArea.setSplitDivider(value=state, redraw=True)

  def setInnerParameter(self, param='axes'):
    if(param in ['axes', 'ticks']):
      if(param == 'axes'):
        state = self.configInnerAxesLabel.isChecked()
      else:
        state = self.configInnerTickLabel.isChecked()
        
      self.parent.plotArea.setInnerParameter(param=param, state=state, redraw=True)

  def transformSlavedX(self, operation='+'):
    # appends operation for transformation of x axis
    if(operation in ['+', '-', '/', '*', '**', 'e', 'ln']):
      try:
        number = float(self.slavedXNumber.text())
        nuTransform = self.parent.plotArea.transformSlavedX(operation=operation, number=number, redraw=True)

        text = ''
        for entry in nuTransform:
          if(len(text)):
            text += ', '
          if(entry[0] in ['e', 'ln']):
            text += '[' + entry[0] + ']'
          else:
            text += repr(entry)
        self.slavedXEntry.setText(text)
      except:
        pass

  def toggleSlavedX(self):
    # toggles whether to display slaved x values on upper axis
    state = self.slavedXLabel.isChecked()
    if(state):
      for entry in self.toggleItemsTransformed:
        if((not (entry in self.advancedGraphicsSettings)) or self.advancedExport['advancedGraphics']):
          entry.show()
    else:
      for entry in self.toggleItemsTransformed:
        entry.hide()
    self.parent.plotArea.toggleSlavedX(state=state, redraw=True)
          
  def resetSlavedX(self):
    # resets formula for slaved x transformation
    self.slavedXEntry.setText('')
    self.parent.plotArea.resetSlavedX(redraw=True)

  def toggleSlavedXLabel(self):
    # toggles whether to display label for slaved x axis
    state = self.slavedXLabelShow.isChecked()
    self.parent.plotArea.toggleSlavedXLabel(state=state, redraw=True)
          
  def changeSlavedXLabel(self):
    # alters label for slaved x axis
    value = self.configSlavedXLabel.text()
    self.parent.plotArea.changeSlavedXLabel(value=value, redraw=True)

  def changePadding(self, axis='bottom', minval=0, maxval=1):
    # adjusts padding around figure
    if(axis in ['bottom', 'top', 'left', 'right']):
      # check paramter boundaries
      try:
        value = float(self.exportPadEntry[axis].text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        self.exportPadEntry[axis].setText(str(value))
        
      self.parent.plotArea.setPadding(value=value, axis=axis, redraw=True, target='plot')
      self.parent.plotArea.setPadding(value=value, axis=axis, redraw=True, target='resid')

  def alterArrowLineWidth(self, axis='x'):
    # changes arrow edge width
    if(axis in ['x', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configArrowLineWidth[axis].value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        self.configArrowLineWidth[axis].setValue(value)
        
      self.parent.plotArea.setAxisArrowLineWidth(value=value, axis=axis, redraw=True)

  def changeArrowHeadWidth(self, axis='x', minval = 0.0, maxval = 1.0):
    # changes arrow head width
    if(axis in ['x', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configArrowHeadWidth[axis].text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        self.configArrowHeadWidth[axis].setText(str(value))
        
      self.parent.plotArea.setAxisArrowHeadWidth(value=value, axis=axis, redraw=True)

  def changeArrowHeadLength(self, axis='x', minval = 0.0, maxval = 1.0):
    # changes arrow head length
    if(axis in ['x', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configArrowHeadLength[axis].text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        self.configArrowHeadLength[axis].setText(str(value))
        
      self.parent.plotArea.setAxisArrowHeadLength(value=value, axis=axis, redraw=True)

  def changeArrowOverhang(self, axis='x', minval = -1.0, maxval = 1.0):
    # changes arrow head overhang
    if(axis in ['x', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configArrowOverhang[axis].text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        self.configArrowOverhang[axis].setText(str(value))
        
      self.parent.plotArea.setAxisArrowOverhang(value=value, axis=axis, redraw=True)

  def changeArrowOffset(self, axis='x', minval = 0.0, maxval = 1.0):
    # changes arrow head offset
    if(axis in ['x', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configArrowOffset[axis].text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        self.configArrowOffset[axis].setText(str(value))
        
      self.parent.plotArea.setAxisArrowOffset(value=value, axis=axis, redraw=True)

  def changeArrowLocation(self, axis='x'):
    # changes arrow head location
    if(axis in ['x', 'y', 'y2']):
      location = self.configArrowLocation[axis].currentText()
      self.parent.plotArea.setAxisArrowLocation(value=location, axis=axis, redraw=True)

  def changeArrowZ(self, axis='x'):
    # changes arrow head location
    if(axis in ['x', 'y', 'y2']):
      location = self.configArrowZ[axis].currentText()
      self.parent.plotArea.setAxisArrowZ(value=location, axis=axis, redraw=True)

  def changeArrowColor(self, axis='x', item='fill'):
    # sets arrow color(s)
    if((axis in ['x', 'y', 'y2']) and (item in ['fill', 'line'])):
      # get current color
      if(item == 'line'):
        prevColor = [int(255. * i) for i in self.parent.plotArea.arrowColor[axis]]
      else:
        prevColor = [int(255. * i) for i in self.parent.plotArea.arrowFill[axis]]
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setAxisArrowColor(value=value, axis=axis, item=item, redraw=True)
        # update color button
        if(item == 'line'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.arrowColor[axis][0:3]]
          self.configArrowLineColor[axis].setMyColor(*colorvalue)
        else:
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.arrowFill[axis][0:3]]
          self.configArrowFillColor[axis].setMyColor(*colorvalue)

  def setAxisArrow(self, axis='x', item='all'):
    # toggles arrow visibility
    if(axis in ['x', 'y', 'y2']):
      if(item in ['all', 'edge', 'fill']):
        if(item == 'all'):
          state = self.configArrowLabel[axis].isChecked()
        elif(item == 'edge'):
          state = self.configArrowLineColorLabel[axis].isChecked()
        else:
          state = self.configArrowFillColorLabel[axis].isChecked()
          
        for target in ['plot', 'resid']:
          self.parent.plotArea.setAxisArrow(state=state, axis=axis, item=item, redraw=True, target=target)

  def setPathStroke(self, redraw=True):
    # toggles path effects
    state = self.configPathEffectsLabel.isChecked()
    self.parent.plotArea.setPathStroke(state=state, redraw=redraw)

  def changePathStrokeColor(self):
    # changes color of path stroke
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.pathStrokeColor]
    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setPathStrokeColor(value=value, redraw=True)
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.pathStrokeColor[0:3]]
      self.configPathEffectsColorButton.setMyColor(*colorvalue)

  def alterPathStrokeWidth(self):
    # changes path stroke width
    # check paramter boundaries
    try:
      value = float(self.configPathEffectsWidth.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configPathEffectsWidth.setValue(value)
      
    self.parent.plotArea.setPathStrokeWidth(value=value, redraw=True)

  def setPathShadow(self, redraw=True):
    # toggles path effects
    state = self.configPathShadowLabel.isChecked()
    self.parent.plotArea.setPathShadow(state=state, redraw=redraw)

  def changePathShadowColor(self):
    # changes color of path shadow
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.pathShadowColor]
    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setPathShadowColor(value=value, redraw=True)
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.pathShadowColor[0:3]]
      self.configPathShadowColorButton.setMyColor(*colorvalue)

  def changePathShadowOffset(self, direction = 'x', minval = 0.0, maxval = 100.0):
    # changes path shadow offset
    if(direction in ['x', 'y']):
      # check paramter boundaries
      if(direction == 'x'):
        try:
          value = float(self.configPathShadowOffX.text())
          originalvalue = value
        except:
          value = 0.0
          originalvalue = 1.0
      else:
        try:
          value = float(self.configPathShadowOffY.text())
          originalvalue = value
        except:
          value = 0.0
          originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        if(direction == 'x'):
          self.configPathShadowOffX.setText(str(value))
        else:
          self.configPathShadowOffY.setText(str(value))
        
      self.parent.plotArea.setPathShadowOffset(value=value, direction=direction, redraw=True)

  def setFrame(self):
    # toggles visibility of frame
    state = self.configFrameCheck.isChecked()
    self.parent.plotArea.setFrame(value=state, redraw=True, target='plot')
    self.parent.plotArea.setFrame(value=state, redraw=True, target='resid')

  def alterFrameWidth(self):
    # changes frame line width
    # check paramter boundaries
    try:
      value = float(self.configFrameWidth.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configFrameWidth.setValue(value)
      
    self.parent.plotArea.setFrameWidth(value=value, redraw=True, target='plot')
    self.parent.plotArea.setFrameWidth(value=value, redraw=True, target='resid')

  def setFrameStyle(self):
    # sets frame style
    style = str(self.configFrameStyle.currentText())
    self.parent.plotArea.setFrameStyle(value=style, redraw=True, target='plot')
    self.parent.plotArea.setFrameStyle(value=style, redraw=True, target='resid')

  def setFrameDashStyle(self):
    # sets frame dash style
    style = str(self.configFrameDashStyle.currentText())
    self.parent.plotArea.setFrameDashStyle(value=style, redraw=True, target='plot')
    self.parent.plotArea.setFrameDashStyle(value=style, redraw=True, target='resid')

  def changeFrameColor(self):
    # sets frame color
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.frameColor]

    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setFrameColor(value=value, redraw=True, target='plot')
      self.parent.plotArea.setFrameColor(value=value, redraw=True, target='resid')
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.frameColor[0:3]]
      self.configFrameColor.setMyColor(*colorvalue)

  def setXkcd(self, redraw=True):
    # toggles Xkcd
    state = self.configXkcdLabel.isChecked()
    self.parent.plotArea.xkcdify(state=state, redraw=redraw)

  def setCanvasGradient(self):
    # toggles visibility of canvas gradient
    state = self.configCanvasGradientLabel.isChecked()
    self.parent.plotArea.setCanvasGradient(state=state, redraw=True, target='plot')
    self.parent.plotArea.setCanvasGradient(state=state, redraw=True, target='resid')

  def changeCanvasGradientStyle(self):
    # sets style of canvas gradient
    value = self.configCanvasGradientStyle.currentText()
    self.parent.plotArea.setCanvasGradientStyle(value=value, redraw=True, target='plot')
    self.parent.plotArea.setCanvasGradientStyle(value=value, redraw=True, target='resid')
    # additionally control visibility of canvas gradient controls
    self.visibilityCanvasGradientControls()

  def changeCanvasGradientWidth(self, minval=0.01, maxval=3.0):
    # changes width of canvas gradient
    # check paramter boundaries
    try:
      value = float(self.configCanvasGradientWidth.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.configCanvasGradientWidth.setText(str(value))
      
    self.parent.plotArea.setCanvasGradientWidth(value=value, redraw=True, target='plot')
    self.parent.plotArea.setCanvasGradientWidth(value=value, redraw=True, target='resid')

  def changeCanvasGradientCenter(self, minval=0.0, maxval=1.0, axis='x'):
    # changes center position of canvas gradient
    if(axis == 'x'):
      item = self.configCanvasGradientCenterX
    else:
      item = self.configCanvasGradientCenterY
    # check paramter boundaries
    try:
      value = float(item.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      item.setText(str(value))
      
    self.parent.plotArea.setCanvasGradientCenter(value=value, redraw=True, target='plot', axis=axis)
    self.parent.plotArea.setCanvasGradientCenter(value=value, redraw=True, target='resid', axis=axis)

  def changeCanvasGradientAngle(self):
    # changes angle of canvas gradient
    # check paramter boundaries
    try:
      value = float(self.configCanvasGradientAngle.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configCanvasGradientAngle.setValue(value)
      
    self.parent.plotArea.setCanvasGradientAngle(value=value, redraw=True, target='plot')
    self.parent.plotArea.setCanvasGradientAngle(value=value, redraw=True, target='resid')

  def changeCanvasGradientColor(self, color=0):
    # changes color of path stroke
    # get current color
    if(color):
      prevColor = [int(255. * i) for i in self.parent.plotArea.canvasGradientColor2]
    else:
      prevColor = [int(255. * i) for i in self.parent.plotArea.canvasGradientColor1]
    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setCanvasGradientColor(value=value, redraw=True, target='plot', color=color)
      self.parent.plotArea.setCanvasGradientColor(value=value, redraw=True, target='resid', color=color)
      # update color button
      if(color):
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.canvasGradientColor2[0:3]]
        self.configCanvasGradientColor2Button.setMyColor(*colorvalue)
      else:
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.canvasGradientColor1[0:3]]
        self.configCanvasGradientColor1Button.setMyColor(*colorvalue)

  def setLegend(self):
    # toggles visibility of legend
    state = self.configLegendLabel.isChecked()
    self.parent.plotArea.setLegend(value=state, redraw=True, target='plot')

  def setLegendShadow(self):
    # toggles visibility of legend shadow
    state = self.configLegendShadowLabel.isChecked()
    self.parent.plotArea.setLegendShadow(value=state, redraw=True, target='plot')

  def setLegendPlacement(self):
    # toggles visibility of legend
    placement = str(self.configLegendPlacement.currentText())
    self.parent.plotArea.setLegendPlacement(value=placement, redraw=True, target='plot')

  def changeLegendColor(self, prop='face'):
    # sets color of legend box
    if(prop in ['face', 'edge']):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.legendColor[prop]]
  
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setLegendColor(value=value, prop=prop, redraw=True, target='plot')
        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.legendColor[prop][0:3]]
        self.configLegendColor[prop].setMyColor(*colorvalue)

  def changeLegendRounding(self, minval=0, maxval=1):
    # changes rounding of legend box
    # check paramter boundaries
    try:
      value = float(self.configLegendRounding.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      self.configLegendRounding.setText(str(value))
      
    self.parent.plotArea.setLegendRounding(value=value, redraw=True, target='plot')

  def alterLegendLabelLinespacing(self, entryfield=None, minval=0, maxval=1):
    # changes linespacing of legend entries
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setLegendLabelLinespacing(value=value, redraw=True, target='plot')

  def changeLegendPosition(self, axis='x', minval=0, maxval=1):
    # changes position of legend box
    if(axis in ['x', 'y']):
      if(axis == 'x'):
        targetField = self.configLegendX
      else:
        targetField = self.configLegendY
    
    # check paramter boundaries
    try:
      value = float(targetField.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    
    # check whether we should switch legend placement to custom
    switch = False
    if((axis == 'x') and (value != self.parent.plotArea.legendX)):
      switch = True
    elif((axis == 'y') and (value != self.parent.plotArea.legendY)):
      switch = True
    if(switch and ('custom' in self.placementstyles)):
      currindex = self.placementstyles.index('custom')
      self.configLegendPlacement.setCurrentIndex(currindex)
    
    # update parameters
    if (value != originalvalue):
      targetField.setText(str(value))
      
    self.parent.plotArea.setLegendPosition(axis=axis, value=value, redraw=True, target='plot')

  def changeLegendHatch(self, item='hatch'):
    # toggles visibility of legend
    if(item == 'linewidth'):
      value = float(self.legendHatchLineWidthSpin.value())
    elif(item == 'hatch'):
      value = str(self.legendComboHatchStyle.currentText())
    else:
      value = int(self.legendComboHatchMultiplyStyle.currentText())
    self.parent.plotArea.setLegendHatchParameter(item=item, value=value, redraw=True, target='plot')

  def changeLegendShadowOffset(self, direction = 'x', minval = -100.0, maxval = 100.0):
    # changes figure legend shadow offset
    if(direction in ['x', 'y']):
      # check paramter boundaries
      if(direction == 'x'):
        try:
          value = float(self.configLegendShadowOffX.text())
          originalvalue = value
        except:
          value = 0.0
          originalvalue = 1.0
      else:
        try:
          value = float(self.configLegendShadowOffY.text())
          originalvalue = value
        except:
          value = 0.0
          originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        if(direction == 'x'):
          self.configPathShadowOffX.setText(str(value))
        else:
          self.configPathShadowOffY.setText(str(value))

      self.parent.plotArea.setLegendShadowParameter(item=direction, value=value, redraw=True, target='plot')

  def changeLegendShadowColor(self):
    # sets color of legend shadow
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.legendShadowFaceColor]

    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setLegendShadowParameter(item='color', value=value, redraw=True, target='plot')
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.legendShadowFaceColor[0:3]]
      self.configLegendShadowColor.setMyColor(*colorvalue)

  def changeLegendLabelColor(self):
    # sets color of legend labels
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.legendLabelColor]

    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setLegendLabelColor(value=value, redraw=True, target='plot')
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.legendLabelColor[0:3]]
      self.configLegendLabelColor.setMyColor(*colorvalue)

  def alterLegendEdgeWidth(self):
    # changes width of legend edge
    # check paramter boundaries
    try:
      value = float(self.configLegendEdgeWidth.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      self.configLegendEdgeWidth.setValue(value)
      
    self.parent.plotArea.setLegendEdgeWidth(value=value, redraw=True, target='plot')

  def changeLegendPadBorder(self, entryfield=None, minval=0, maxval=1):
    # changes border padding of legend
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setLegendPadBorder(value=value, redraw=True, target='plot')

  def changeLegendPadRow(self, entryfield=None, minval=0, maxval=1):
    # changes row padding of legend
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setLegendPadRow(value=value, redraw=True, target='plot')

  def changeLegendPadCol(self, entryfield=None, minval=0, maxval=1):
    # changes column padding of legend
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setLegendPadCol(value=value, redraw=True, target='plot')

  def changeLegendPadLabel(self, entryfield=None, minval=0, maxval=1):
    # changes padding between symbol and label in legend
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setLegendPadLabel(value=value, redraw=True, target='plot')

  def alterLegendLabelSize(self, entryfield=None):
    # changes font size of legend label
    try:
      value = float(entryfield.value())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    # update parameters
    if (value != originalvalue):
      entryfield.setValue(value)
      
    self.parent.plotArea.setLegendLabelSize(value=value, redraw=True, target='plot')

  def changeLegendLabelBold(self):
    # changes formatting of legend label
    isChecked = self.configLegendLabelBold.isChecked()
    if(isChecked):
      value = 'bold'
    else:
      value = 'normal'
      
    self.parent.plotArea.setLegendLabelBold(value=value, redraw=True, target='plot')

  def changeLegendLabelItalic(self):
    # changes formatting of legend label
    isChecked = self.configLegendLabelItalic.isChecked()
    if(isChecked):
      value = 'italic'
    else:
      value = 'normal'
      
    self.parent.plotArea.setLegendLabelItalic(value=value, redraw=True, target='plot')

  def setLegendLabelFont(self):
    # sets legend label font
    useFont = str(self.configLegendLabelFont.currentText())
      
    if(useFont in self.parent.fontNames):
      self.parent.plotArea.setLegendLabelFont(value=useFont, redraw=True, target='plot')

  def setLegendNCol(self):
    # sets number of columns in legend
    value = self.configLegendNCol.currentIndex() + 1
    self.parent.plotArea.setLegendNCol(value=value, redraw=True, target='plot')

  def setLegendMarkerFirst(self):
    # toggles whether to display marker first in legend
    state = self.configLegendMarkerFirstLabel.isChecked()
    self.parent.plotArea.setLegendMarkerFirst(value=state, redraw=True, target='plot')

  def changeLegendMarkerScale(self, entryfield=None, minval=0, maxval=1):
    # changes relative size of legend marker
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setLegendMarkerScale(value=value, redraw=True, target='plot')

  def setLegendMarkerNumPoints(self):
    # sets number of marker points in legend
    value = self.configLegendMarkerNumPoints.currentIndex() + 1
    self.parent.plotArea.setLegendMarkerNumPoints(value=value, redraw=True, target='plot')

  def toggleLegendFill(self):
    # toggles whether legend filled or not
    state = self.configLegendColorLabel['face'].isChecked()
    self.parent.plotArea.toggleLegendFill(value=state, redraw=True, target='plot')

  def toggleLegendEdge(self):
    # toggles whether legend boxed or not
    state = self.configLegendColorLabel['edge'].isChecked()
    self.parent.plotArea.toggleLegendEdge(value=state, redraw=True, target='plot')

  def setGridRectVisibility(self, axis='x'):
    # toggles grid shading visibility
    if(axis in ['x', 'x2', 'y', 'y2']):
      state = self.configGridLabel3[axis].isChecked()
      self.parent.plotArea.setGridRectVisibility(value=state, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridRectVisibility(value=state, axis=axis, redraw=True, target='resid')

  def changeGridRectColor(self, axis='x'):
    # sets grid shading color
    if(axis in ['x', 'x2', 'y', 'y2']):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.gridRectColor[axis]]
  
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setGridRectColor(value=value, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'x2', 'y']):
          self.parent.plotArea.setGridRectColor(value=value, axis=axis, redraw=True, target='resid')
        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.gridRectColor[axis][0:3]]
        self.configGridRectColor[axis].setMyColor(*colorvalue)

  def setGridVisibility(self, axis='x'):
    # toggles grid visibility
    if(axis in ['x', 'x2', 'y', 'y2']):
      state = self.configGridLabel[axis].isChecked()
      self.parent.plotArea.setGridVisibility(value=state, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridVisibility(value=state, axis=axis, redraw=True, target='resid')

  def changeGridColor(self, axis='x'):
    # sets grid color
    if(axis in ['x', 'x2', 'y', 'y2']):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.gridColor[axis]]
  
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setGridColor(value=value, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'x2', 'y']):
          self.parent.plotArea.setGridColor(value=value, axis=axis, redraw=True, target='resid')
        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.gridColor[axis][0:3]]
        self.configGridColor[axis].setMyColor(*colorvalue)

  def alterGridWidth(self, axis='x'):
    # changes grid line width
    if(axis in ['x', 'x2', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configGridWidth[axis].value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        self.configGridWidth[axis].setValue(value)
        
      self.parent.plotArea.setGridWidth(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridWidth(value=value, axis=axis, redraw=True, target='resid')

  def setGridOrder(self, axis = 'x'):
    # sets grid style
    if(axis in ['x', 'x2', 'y', 'y2']):
      order = str(self.configGridOrder[axis].currentText())
      index = self.configGridOrder[axis].currentIndex()
     
      self.parent.plotArea.setGridOrder(value=order, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridOrder(value=order, axis=axis, redraw=True, target='resid')

  def setGridStyle(self, axis = 'x'):
    # sets grid style
    if(axis in ['x', 'x2', 'y', 'y2']):
      style = str(self.configGridStyle[axis].currentText())
      self.parent.plotArea.setGridStyle(value=style, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridStyle(value=style, axis=axis, redraw=True, target='resid')

  def setGridDashStyle(self, axis = 'x'):
    # sets grid style
    if(axis in ['x', 'x2', 'y', 'y2']):
      style = str(self.configGridDashStyle[axis].currentText())
      self.parent.plotArea.setGridDashStyle(value=style, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridDashStyle(value=style, axis=axis, redraw=True, target='resid')

  def setGridMinorVisibility(self, axis='x'):
    # toggles minor grid visibility
    if(axis in ['x', 'x2', 'y', 'y2']):
      state = self.configGridLabel2[axis].isChecked()
      self.parent.plotArea.setGridMinorVisibility(value=state, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridMinorVisibility(value=state, axis=axis, redraw=True, target='resid')

  def changeGridMinorColor(self, axis='x'):
    # sets minor grid color
    if(axis in ['x', 'x2', 'y', 'y2']):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.gridMinorColor[axis]]
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setGridMinorColor(value=value, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'x2', 'y']):
          self.parent.plotArea.setGridMinorColor(value=value, axis=axis, redraw=True, target='resid')
        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.gridMinorColor[axis][0:3]]
        self.configGridColor2[axis].setMyColor(*colorvalue)

  def alterGridMinorWidth(self, axis='x'):
    # changes minor grid line width
    if(axis in ['x', 'x2', 'y', 'y2']):
      # check paramter boundaries
      try:
        value = float(self.configGridWidth2[axis].value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        self.configGridWidth2[axis].setValue(value)
        
      self.parent.plotArea.setGridMinorWidth(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridMinorWidth(value=value, axis=axis, redraw=True, target='resid')

  def setGridMinorOrder(self, axis = 'x'):
    # sets minor grid order
    if(axis in ['x', 'x2', 'y', 'y2']):
      order = str(self.configGridOrder2[axis].currentText())
      index = self.configGridOrder2[axis].currentIndex()
     
      self.parent.plotArea.setGridMinorOrder(value=order, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridMinorOrder(value=order, axis=axis, redraw=True, target='resid')

  def setGridMinorStyle(self, axis = 'x'):
    # sets minor grid style
    if(axis in ['x', 'x2', 'y', 'y2']):
      style = str(self.configGridStyle2[axis].currentText())
      self.parent.plotArea.setGridMinorStyle(value=style, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridMinorStyle(value=style, axis=axis, redraw=True, target='resid')

  def setGridMinorDashStyle(self, axis = 'x'):
    # sets minor grid dash style
    if(axis in ['x', 'x2', 'y', 'y2']):
      style = str(self.configGridDashStyle2[axis].currentText())
      self.parent.plotArea.setGridMinorDashStyle(value=style, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'x2', 'y']):
        self.parent.plotArea.setGridMinorDashStyle(value=style, axis=axis, redraw=True, target='resid')

  def changeAxisColor(self, axis='left'):
    # sets axis color
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.axisColor[axis]]
  
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
          self.parent.plotArea.setAxisColor(value=value, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
          self.parent.plotArea.setAxisColor(value=value, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setAxisColor(value=value, axis=axis, redraw=True, target='plot')
        if(not (axis in ['left2', 'right2'])):
          self.parent.plotArea.setAxisColor(value=value, axis=axis, redraw=True, target='resid')
        elif(self.parent.plotArea.splitY):
          self.parent.plotArea.setAxisColor(value=value, axis=axis[:-1], redraw=True, target='resid')
        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.axisColor[axis][0:3]]
        self.configAxisColor[axis].setMyColor(*colorvalue)
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
          self.parent.graphicsarea2.configAxisColor[axis + '2'].setMyColor(*colorvalue)
        if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
          self.parent.graphicsarea.configAxisColor[axis[:-1]].setMyColor(*colorvalue)

  def setTickFont(self, axis='x'):
    # sets tick font
    if(axis in ['x', 'y', 'y2', 'xs']):
      if(axis == 'x'):
        useFont = str(self.configTickXFont.currentText())
      elif(axis == 'xs'):
        useFont = str(self.slavedTickXFont.currentText())
      else:
        useFont = str(self.configTickYFont.currentText())
      
      if(useFont in self.parent.fontNames):
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y')):
          index = self.configTickYFont.currentIndex()
          self.parent.graphicsarea2.configTickYFont.setCurrentIndex(index)
          self.parent.plotArea.setTickFont(value=useFont, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis == 'y2')):
          index = self.configTickYFont.currentIndex()
          self.parent.graphicsarea.configTickYFont.setCurrentIndex(index)
          self.parent.plotArea.setTickFont(value=useFont, axis='y', redraw=False, target='plot')
        self.parent.plotArea.setTickFont(value=useFont, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setTickFont(value=useFont, axis=axis, redraw=True, target='resid')
        elif(self.parent.plotArea.splitY):
          self.parent.plotArea.setTickFont(value=useFont, axis=axis[:-1], redraw=True, target='resid')

  def setMinorTick(self, axis='x'):
    # sets minor tick number
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'xs']):
      if(axis == 'x'):
        value = self.configMinorTickX.currentIndex() + 2
        toggleButton = self.configMinorTickXLabel
      elif(axis == 'x2'):
        value = self.configInnerMinorTickX.currentIndex() + 2
        toggleButton = self.configInnerMinorTickLabel
      elif(axis == 'xs'):
        value = self.slavedMinorTickX.currentIndex() + 2
        toggleButton = self.slavedMinorTickXLabel
      elif(axis in ['y', 'y2']):
        value = self.configMinorTickY.currentIndex() + 2
        toggleButton = self.configMinorTickYLabel
      else:
        value = self.configMinorTickResid.currentIndex() + 2
        toggleButton = self.configMinorTickResidLabel

      if(axis in ['x', 'x2', 'xs']):
        self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='plot')
        self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='resid')
      elif(axis == 'y'):
        # check for split y axis and slave setting if needed
        '''
        # better disable slaving of minor tick number -- bound to cause problems
        if(self.parent.plotArea.splitY and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          index = self.configMinorTickY.currentIndex()
          self.parent.graphicsarea2.configMinorTickY.setCurrentIndex(index)
          self.parent.plotArea.setMinorTick(value=value, axis=axis + '2', redraw=False, target='plot')
        '''
        self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='plot')
      elif(axis == 'y2'):
        self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='plot')
      else:
        self.parent.plotArea.setMinorTick(value=value, axis='y', redraw=True, target='resid')
        
      # silently toggle on check button
      toggleButton.setChecked(True)

  def toggleTicksLabel(self, axis='x'):
    # toggles visibility of tick labels
    if(axis in ['x', 'x2', 'y', 'y2', 'resid']):
      if(axis == 'x'):
        state = self.configTickXLabel.isChecked()
      elif(axis == 'x2'):
        state = self.configInnerLabel.isChecked()
      elif(axis in ['y', 'y2']):
        state = self.configTickYLabel.isChecked()
      else:
        state = self.configTickResidYLabel.isChecked()
        
      if(axis == 'resid'):
        self.parent.plotArea.toggleTicksLabel(value=state, axis='y', redraw=True, target='resid')
      else:
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y')):
          self.parent.graphicsarea2.configTickYLabel.setChecked(state)
          self.parent.plotArea.toggleTicksLabel(value=state, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis == 'y2')):
          self.parent.graphicsarea.configTickYLabel.setChecked(state)
          self.parent.plotArea.toggleTicksLabel(value=state, axis='y', redraw=False, target='plot')
        self.parent.plotArea.toggleTicksLabel(value=state, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'x2']):
          self.parent.plotArea.toggleTicksLabel(value=state, axis=axis, redraw=True, target='resid')

  def toggleAxisLabel(self, axis='x'):
    # toggles visibility of axis label
    if(axis in ['x', 'y', 'y2']):
      if(axis == 'x'):
        state = self.configXLabel.isChecked()
      else:
        state = self.configYLabel.isChecked()
      self.parent.plotArea.toggleAxisLabel(value=state, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y']):
        self.parent.plotArea.toggleAxisLabel(value=state, axis=axis, redraw=True, target='resid')

  def setAxisLabelAlignment(self, axis='x'):
    # sets alignment of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      flag = False
      if(axis == 'x'):
        useAlignment = str(self.configXAlignment.currentText())
        if(useAlignment in self.alignHorizontal):
          flag = True
      elif(axis == 'xs'):
        useAlignment = str(self.slavedXAlignment.currentText())
        if(useAlignment in self.alignHorizontal):
          flag = True
      else:
        useAlignment = str(self.configYAlignment.currentText())
        if(useAlignment in self.alignHorizontal):
          flag = True
      
      if(flag):
        self.parent.plotArea.setAxisLabelAlignment(value=useAlignment, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setAxisLabelAlignment(value=useAlignment, axis=axis, redraw=True, target='resid')

  def setAxisLabelAlignmentVertical(self, axis='x'):
    # sets vertical alignment of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      flag = False
      if(axis == 'x'):
        useAlignment = str(self.configXAlignmentVertical.currentText())
        if(useAlignment in self.alignVertical):
          flag = True
      elif(axis == 'xs'):
        useAlignment = str(self.slavedXAlignmentVertical.currentText())
        if(useAlignment in self.alignVertical):
          flag = True
      else:
        useAlignment = str(self.configYAlignmentVertical.currentText())
        if(useAlignment in self.alignVertical):
          flag = True
      
      if(flag):
        self.parent.plotArea.setAxisLabelAlignmentVertical(value=useAlignment, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setAxisLabelAlignmentVertical(value=useAlignment, axis=axis, redraw=True, target='resid')

  def changeAxisLabelBold(self, axis='x'):
    # changes formatting of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      if(axis == 'x'):
        isChecked = self.configXBold.isChecked()
      elif(axis == 'xs'):
        isChecked = self.slavedXBold.isChecked()
      else:
        isChecked = self.configYBold.isChecked()
        
      if(isChecked):
        value = 'bold'
      else:
        value = 'normal'
        
      self.parent.plotArea.setAxisLabelBold(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelBold(value=value, axis=axis, redraw=True, target='resid')

  def changeAxisLabelItalic(self, axis='x'):
    # changes formatting of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      if(axis == 'x'):
        isChecked = self.configXItalic.isChecked()
      elif(axis == 'xs'):
        isChecked = self.slavedXItalic.isChecked()
      else:
        isChecked = self.configYItalic.isChecked()
        
      if(isChecked):
        value = 'italic'
      else:
        value = 'normal'
        
      self.parent.plotArea.setAxisLabelItalic(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelItalic(value=value, axis=axis, redraw=True, target='resid')

  def changeAxisLabelVariant(self, axis='x'):
    # changes formatting of axis label
    if(axis in ['x', 'y', 'y2']):
      if(axis == 'x'):
        isChecked = self.configXVariant.isChecked()
      else:
        isChecked = self.configYVariant.isChecked()
        
      if(isChecked):
        value = 'small-caps'
      else:
        value = 'normal'
        
      self.parent.plotArea.setAxisLabelVariant(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y']):
        self.parent.plotArea.setAxisLabelVariant(value=value, axis=axis, redraw=True, target='resid')

  def setAxisFont(self, axis='x'):
    # sets axis font
    if(axis in ['x', 'y', 'y2', 'xs']):
      if(axis == 'x'):
        useFont = str(self.configXFont.currentText())
      elif(axis == 'xs'):
        useFont = str(self.slavedXFont.currentText())
      else:
        useFont = str(self.configYFont.currentText())
      
      if(useFont in self.parent.fontNames):
        self.parent.plotArea.setAxisFont(value=useFont, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          	self.parent.plotArea.setAxisFont(value=useFont, axis=axis, redraw=True, target='resid')

  def setAxisStyle(self, axis='left'):
    # sets axis style
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      style = str(self.configAxisStyle[axis].currentText())
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        index = self.configAxisStyle[axis].currentIndex()
        self.parent.graphicsarea2.configAxisStyle[axis + '2'].setCurrentIndex(index)
        self.parent.plotArea.setAxisStyle(value=style, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        index = self.configAxisStyle[axis].currentIndex()
        self.parent.graphicsarea.configAxisStyle[axis[:-1]].setCurrentIndex(index)
        self.parent.plotArea.setAxisStyle(value=style, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setAxisStyle(value=style, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisStyle(value=style, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setAxisStyle(value=style, axis=axis[:-1], redraw=True, target='resid')

  def setAxisDashStyle(self, axis='left'):
    # sets axis style
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      style = str(self.configAxisDashStyle[axis].currentText())
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        index = self.configAxisDashStyle[axis].currentIndex()
        self.parent.graphicsarea2.configAxisDashStyle[axis + '2'].setCurrentIndex(index)
        self.parent.plotArea.setAxisDashStyle(value=style, axis=axis + '2', redraw=False, target='plot')
      self.parent.plotArea.setAxisDashStyle(value=style, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisDashStyle(value=style, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setAxisDashStyle(value=style, axis=axis[:-1], redraw=True, target='resid')

  def setAxisPosition(self, axis='left'):
    # sets axis position
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      style = str(self.configAxisPosition[axis].currentText())
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        index = self.configAxisPosition[axis].currentIndex()
        self.parent.graphicsarea2.configAxisPosition[axis + '2'].setCurrentIndex(index)
        self.parent.plotArea.setAxisPosition(value=style, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        index = self.configAxisPosition[axis].currentIndex()
        self.parent.graphicsarea.configAxisPosition[axis[:-1]].setCurrentIndex(index)
        self.parent.plotArea.setAxisPosition(value=style, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setAxisPosition(value=style, axis=axis, redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['bottom', 'top'])):
        # phew, we will need to update setAxisPositionHelper() to accommodate this option
        self.parent.plotArea.setAxisPositionHelper(axis=axis, plotobject=self.parent.plotArea.dataplotwidget, axisobject=self.parent.plotArea.ax, target='plot', secondAxes=False, splitX=False, splitY=True)
        self.parent.plotArea.setAxisPositionHelper(axis=axis, plotobject=self.parent.plotArea.dataplotwidget, axisobject=self.parent.plotArea.ax_div, target='plot', secondAxes=False, splitX=False, splitY=True)
      self.parent.plotArea.dataplotwidget.myRefresh()
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisPosition(value=style, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setAxisPosition(value=style, axis=axis[:-1], redraw=True, target='resid')

  def changeAxisPositionValue(self, axis='left', minval=-np.inf, maxval=np.inf):
    # changes axis position value
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # check paramter boundaries
      try:
        value = float(self.configAxisPositionValue[axis].text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        self.configAxisPositionValue[axis].setText(str(value))
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        self.parent.graphicsarea2.configAxisPositionValue[axis + '2'].setText(str(value))
        self.parent.plotArea.setAxisPositionValue(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        self.parent.graphicsarea.configAxisPositionValue[axis[:-1]].setText(str(value))
        self.parent.plotArea.setAxisPositionValue(value=value, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setAxisPositionValue(value=value, axis=axis, redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['bottom', 'top'])):
        # phew, we will need to update setAxisPositionHelper() to accommodate this option
        self.parent.plotArea.setAxisPositionHelper(axis=axis, plotobject=self.parent.plotArea.dataplotwidget, axisobject=self.parent.plotArea.ax, target='plot', secondAxes=False, splitX=False, splitY=True)
        self.parent.plotArea.setAxisPositionHelper(axis=axis, plotobject=self.parent.plotArea.dataplotwidget, axisobject=self.parent.plotArea.ax_div, target='plot', secondAxes=False, splitX=False, splitY=True)
      self.parent.plotArea.dataplotwidget.myRefresh()
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisPositionValue(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setAxisPositionValue(value=value, axis=axis[:-1], redraw=True, target='resid')

  def resetAxisPosition(self, axis='left'):
    # resets axis position to original one
    # first update entry fields
    originalPosition = {'left':('axes', 0), 'right':('axes', 1.0), 'bottom':('axes', 0), 'top':('axes', 1.0), 'left2':('axes', 0), 'right2':('axes', 1.0)}
    if(axis in originalPosition):
      value = str(self.configAxisPosition[axis].currentText())
      if(value != originalPosition[axis][0]):
        if(originalPosition[axis][0] in self.positionstyles):
          self.configAxisPosition[axis].blockSignals(True)
          currindex = self.positionstyles.index(originalPosition[axis][0])
          self.configAxisPosition[axis].setCurrentIndex(currindex)
          self.configAxisPosition[axis].blockSignals(False)
          # check for split y axis and slave setting if needed
          if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
            self.parent.graphicsarea2.configAxisPosition[axis + '2'].setCurrentIndex(currindex)
          if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
            self.parent.graphicsarea.configAxisPosition[axis[:-1]].setCurrentIndex(currindex)
      try:
        value = float(self.configAxisPositionValue[axis].text())
      except:
        value = -9
      if(value != originalPosition[axis][1]):
        self.configAxisPositionValue[axis].blockSignals(True)
        self.configAxisPositionValue[axis].setText(str(originalPosition[axis][1]))
        self.configAxisPositionValue[axis].blockSignals(False)
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
          self.parent.graphicsarea2.configAxisPositionValue[axis + '2'].setText(str(originalPosition[axis][1]))
        if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
          self.parent.graphicsarea.configAxisPositionValue[axis[:-1]].setText(str(originalPosition[axis][1]))
      
    # reset the axisn
    # check for split y axis and slave setting if needed
    if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
      self.parent.plotArea.resetAxisPosition(axis=axis + '2', redraw=False)
    if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
      self.parent.plotArea.resetAxisPosition(axis=axis[:-1], redraw=False)
    self.parent.plotArea.resetAxisPosition(axis=axis, redraw=False)
    if(self.parent.plotArea.splitY and (axis in ['bottom', 'top'])):
      # phew, we will need to update setAxisPositionHelper() to accommodate this option
      self.parent.plotArea.setAxisPositionHelper(axis=axis, plotobject=self.parent.plotArea.dataplotwidget, axisobject=self.parent.plotArea.ax, target='plot', secondAxes=False, splitX=False, splitY=True)
      self.parent.plotArea.setAxisPositionHelper(axis=axis, plotobject=self.parent.plotArea.dataplotwidget, axisobject=self.parent.plotArea.ax_div, target='plot', secondAxes=False, splitX=False, splitY=True)
    self.parent.plotArea.dataplotwidget.myRefresh()
    self.parent.plotArea.residplotwidget.myRefresh()

  def alterAxisWidth(self, axis='left'):
    # changes axis width
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # check paramter boundaries
      try:
        value = float(self.configAxisWidth[axis].value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        self.configAxisWidth[axis].setValue(value)
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        self.parent.graphicsarea2.configAxisWidth[axis + '2'].setValue(value)
        self.parent.plotArea.setAxisWidth(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        self.parent.graphicsarea.configAxisWidth[axis[:-1]].setValue(value)
        self.parent.plotArea.setAxisWidth(value=value, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setAxisWidth(value=value, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisWidth(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setAxisWidth(value=value, axis=axis[:-1], redraw=True, target='resid')

  def changeAxisBoundary(self, axis='left', lower=True):
    # changes axis boundary
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      if(lower):
        entryfield = self.configAxisBoundLower[axis]
      else:
        entryfield = self.configAxisBoundUpper[axis]
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      # check whether redraw should be issued (only whne boundaries are active)
      if(self.configAxisBoundLabel[axis].isChecked()):
        redraw = True
      else:
        redraw = False
        
      self.parent.plotArea.setAxisBoundaryValue(value=value, lower=lower, axis=axis, redraw=redraw, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisBoundaryValue(value=value, lower=lower, axis=axis, redraw=redraw, target='resid')

  def setAxisBoundary(self, axis='left'):
    # toggles axis boundary
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      value = self.configAxisBoundLabel[axis].isChecked()
      self.parent.plotArea.setAxisBoundary(value=value, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisBoundary(value=value, axis=axis, redraw=True, target='resid')

  def setAxisVisibility(self, axis='left'):
    # toggles axis visibility
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      state = self.configAxisLabel[axis].isChecked()
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        self.parent.graphicsarea2.configAxisLabel[axis + '2'].setChecked(state)
        self.parent.plotArea.setAxisVisibility(value=state, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        self.parent.graphicsarea.configAxisLabel[axis[:-1]].setChecked(state)
        self.parent.plotArea.setAxisVisibility(value=state, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setAxisVisibility(value=state, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setAxisVisibility(value=state, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setAxisVisibility(value=state, axis=axis[:-1], redraw=True, target='resid')

  def setTickMarkDirection(self, axis='left'):
    # sets tick mark direction
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      style = str(self.configTickMarkDirection[axis].currentText())
      index = self.configTickMarkDirection[axis].currentIndex()
      # update parameters
      if(axis in ['left', 'right']):
        for entry in ['left', 'right']:
          # have to temporarily disable event logging
          self.configTickMarkDirection[entry].blockSignals(True)
          self.configTickMarkDirection[entry].setCurrentIndex(index)
          self.configTickMarkDirection[entry].blockSignals(False)
      elif(axis in ['top', 'bottom']):
        for entry in ['top', 'bottom']:
          # have to temporarily disable event logging
          self.configTickMarkDirection[entry].blockSignals(True)
          self.configTickMarkDirection[entry].setCurrentIndex(index)
          self.configTickMarkDirection[entry].blockSignals(False)
      else:
        for entry in ['left2', 'right2']:
          # have to temporarily disable event logging
          self.configTickMarkDirection[entry].blockSignals(True)
          self.configTickMarkDirection[entry].setCurrentIndex(index)
          self.configTickMarkDirection[entry].blockSignals(False)

      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        for entry in ['left2', 'right2']:
          self.parent.graphicsarea2.configTickMarkDirection[entry].setCurrentIndex(index)
        self.parent.plotArea.setTickMarkDirection(value=style, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        for entry in ['left', 'right']:
          self.parent.graphicsarea.configTickMarkDirection[entry].setCurrentIndex(index)
        self.parent.plotArea.setTickMarkDirection(value=style, axis=axis[:-1], redraw=False, target='plot')

      self.parent.plotArea.setTickMarkDirection(value=style, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setTickMarkDirection(value=style, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickMarkDirection(value=style, axis=axis[:-1], redraw=True, target='resid')
    elif(axis == 'slaved'):
      style = self.slavedTickMarkDirection.currentText()
      self.parent.plotArea.setTickMarkDirection(value=style, axis='slaved', redraw=True, target='plot')
      self.parent.plotArea.setTickMarkDirection(value=style, axis='slaved', redraw=True, target='resid')

  def setTickMarkLabelShow(self, axis='left'):
    # toggles ticks visibility
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      state = self.configTickMarkLabelShow[axis].isChecked()

      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        self.parent.graphicsarea2.configTickMarkLabelShow[axis + '2'].setChecked(state)
        self.parent.plotArea.setTickMarkLabelShow(value=state, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        self.parent.graphicsarea.configTickMarkLabelShow[axis[:-1]].setChecked(state)
        self.parent.plotArea.setTickMarkLabelShow(value=state, axis=axis + '2', redraw=False, target='plot')
      self.parent.plotArea.setTickMarkLabelShow(value=state, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setTickMarkLabelShow(value=state, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickMarkLabelShow(value=state, axis=axis[:-1], redraw=True, target='resid')
    elif(axis == 'slaved'):
      state = self.slavedTickMarkLabelShow.isChecked()
      self.parent.plotArea.setTickMarkLabelShow(value=state, axis='slaved', redraw=True, target='plot')
      self.parent.plotArea.setTickMarkLabelShow(value=state, axis='slaved', redraw=True, target='resid')

  def setTicksVisibility(self, axis='left'):
    # toggles ticks visibility
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        splitY = True
      else:
        splitY = False

      state = self.configTickMarkLabel[axis].isChecked()
      if(splitY):
        self.parent.graphicsarea2.configTickMarkLabel[axis + '2'].setChecked(state)
        self.parent.plotArea.setTickMarkVisibility(value=state, axis=axis + '2', redraw=False, target='plot')
      if((axis in ['left2', 'right2']) and self.parent.plotArea.splitY):
        self.parent.graphicsarea.configTickMarkLabel[axis[:-1]].setChecked(state)
        self.parent.plotArea.setTickMarkVisibility(value=state, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setTickMarkVisibility(value=state, axis=axis, redraw=False, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setTickMarkVisibility(value=state, axis=axis, redraw=False, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickMarkVisibility(value=state, axis=axis[:-1], redraw=False, target='resid')

      # need to reapply tick formatting as there is the possiblity we switched from left to right of from top to bottom or vice versa
      if(axis in ['top', 'bottom']):
        axis = 'x'
        tickLabelColor = self.parent.plotArea.ticksXColor
        tickLabelSize = self.parent.plotArea.ticksXSize
        tickLabelWeight = self.parent.plotArea.ticksXWeight
        tickLabelStyle = self.parent.plotArea.ticksXStyle
        tickLabelAngle = self.parent.plotArea.ticksXAngle
        tickLabelAlignment = self.parent.plotArea.ticksXAlignment
        tickLabelAlignmentVertical = self.parent.plotArea.ticksXAlignmentVertical
        tickLabelFont = self.parent.plotArea.tickFont[axis]
        useShow, useShow_resid = self.parent.plotArea.ticksXShow, self.parent.plotArea.ticksXShow_resid
        ##### need to deal with div plot as well
      elif(axis in ['left', 'right']):
        axis = 'y'
        tickLabelColor = self.parent.plotArea.ticksYColor
        tickLabelSize = self.parent.plotArea.ticksYSize
        tickLabelWeight = self.parent.plotArea.ticksYWeight
        tickLabelStyle = self.parent.plotArea.ticksYStyle
        tickLabelAngle = self.parent.plotArea.ticksYAngle
        tickLabelAlignment = self.parent.plotArea.ticksYAlignment
        tickLabelAlignmentVertical = self.parent.plotArea.ticksYAlignmentVertical
        tickLabelFont = self.parent.plotArea.tickFont[axis]
        useShow, useShow_resid = self.parent.plotArea.ticksYShow, self.parent.plotArea.ticksResidYShow
      else:
        axis = 'y2'
        tickLabelColor = self.parent.plotArea.ticksY2Color
        tickLabelSize = self.parent.plotArea.ticksY2Size
        tickLabelWeight = self.parent.plotArea.ticksY2Weight
        tickLabelStyle = self.parent.plotArea.ticksY2Style
        tickLabelAngle = self.parent.plotArea.ticksY2Angle
        tickLabelAlignment = self.parent.plotArea.ticksY2Alignment
        tickLabelAlignmentVertical = self.parent.plotArea.ticksY2AlignmentVertical
        tickLabelFont = self.parent.plotArea.tickFont[axis]
        useShow, useShow_resid = self.parent.plotArea.ticksY2Show, None
      
      if(axis == 'y2'):
        usePlots = ['plot']
      else:
        usePlots = ['plot', 'resid']
      for target in usePlots:
        self.parent.plotArea.setTickLabelColor(value=tickLabelColor, axis=axis, redraw=False, target=target)
        self.parent.plotArea.setTickLabelSize(value=tickLabelSize, axis=axis, redraw=False, target=target)
        self.parent.plotArea.setTickLabelBold(value=tickLabelWeight, axis=axis, redraw=False, target=target)
        self.parent.plotArea.setTickLabelItalic(value=tickLabelStyle, axis=axis, redraw=False, target=target)
        self.parent.plotArea.setTickLabelAngle(value=tickLabelAngle, axis=axis, redraw=False, target=target)
        self.parent.plotArea.setTickLabelAlignment(value=tickLabelAlignment, axis=axis, redraw = False, target=target)
        self.parent.plotArea.setTickLabelAlignmentVertical(value=tickLabelAlignmentVertical, axis=axis, redraw = False, target=target)
        self.parent.plotArea.setTickFont(value=tickLabelFont, axis=axis, redraw=False, target=target)
        if(target == 'plot'):
          self.parent.plotArea.toggleTicksLabel(value=useShow, axis=axis, redraw=False, target=target)
        else:
          self.parent.plotArea.toggleTicksLabel(value=useShow_resid, axis=axis, redraw=False, target=target)
          
      # check for split y axis and slave setting if needed
      if(splitY):
        self.parent.plotArea.setTickLabelColor(value=tickLabelColor, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelSize(value=tickLabelSize, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelBold(value=tickLabelWeight, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelItalic(value=tickLabelStyle, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAngle(value=tickLabelAngle, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAlignment(value=tickLabelAlignment, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAlignmentVertical(value=tickLabelAlignmentVertical, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.setTickFont(value=tickLabelFont, axis=axis + '2', redraw=False, target='plot')
        self.parent.plotArea.toggleTicksLabel(value=useShow, axis=axis + '2', redraw=False, target='plot')
      elif((axis in ['left2', 'right2']) and self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelColor(value=tickLabelColor, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickLabelSize(value=tickLabelSize, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickLabelBold(value=tickLabelWeight, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickLabelItalic(value=tickLabelStyle, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAngle(value=tickLabelAngle, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAlignment(value=tickLabelAlignment, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAlignmentVertical(value=tickLabelAlignmentVertical, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickFont(value=tickLabelFont, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.toggleTicksLabel(value=useShow, axis=axis[:-1], redraw=False, target='plot')

      # redraw
      self.parent.plotArea.dataplotwidget.myRefresh()
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.residplotwidget.myRefresh()
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.residplotwidget.myRefresh()
    elif(axis == 'slaved'):
      state = self.slavedTickMarkLabel.isChecked()
      self.parent.plotArea.setTickMarkVisibility(value=state, axis='slaved', redraw=True, target='plot')
      self.parent.plotArea.setTickMarkVisibility(value=state, axis='slaved', redraw=True, target='resid')
      
  def alterTickMarkLength(self, axis='left'):
    # changes tick mark width
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # check paramter boundaries
      try:
        value = float(self.configTickMarkLength[axis].value())
      except:
        value = 0.0
      # update parameters
      if(axis in ['left', 'right']):
        self.configTickMarkLength['left'].setValue(value)
        self.configTickMarkLength['right'].setValue(value)
      elif(axis in ['left2', 'right2']):
        self.configTickMarkLength['left2'].setValue(value)
        self.configTickMarkLength['right2'].setValue(value)
      else:
        self.configTickMarkLength['top'].setValue(value)
        self.configTickMarkLength['bottom'].setValue(value)
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        self.parent.graphicsarea2.configTickMarkLength['left2'].setValue(value)
        self.parent.graphicsarea2.configTickMarkLength['right2'].setValue(value)
        self.parent.plotArea.setTickMarkLength(value=value, axis=axis + '2', redraw=False, target='plot', forceRedraw=False)
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        self.parent.graphicsarea.configTickMarkLength['left'].setValue(value)
        self.parent.graphicsarea.configTickMarkLength['right'].setValue(value)
        self.parent.plotArea.setTickMarkLength(value=value, axis=axis[:-1], redraw=False, target='plot', forceRedraw=False)
      self.parent.plotArea.setTickMarkLength(value=value, axis=axis, redraw=True, target='plot', forceRedraw=False)
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setTickMarkLength(value=value, axis=axis, redraw=True, target='resid', forceRedraw=False)
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickMarkLength(value=value, axis=axis[:-1], redraw=True, target='resid', forceRedraw=False)
    elif(axis == 'slaved'):
      # check paramter boundaries
      try:
        value = float(self.slavedTickMarkLength.value())
      except:
        value = 0.0
        
      self.parent.plotArea.setTickMarkLength(value=value, axis='slaved', redraw=True, target='plot')
      self.parent.plotArea.setTickMarkLength(value=value, axis='slaved', redraw=True, target='resid')

  def alterTickMarkWidth(self, axis='left'):
    # changes tick mark width
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # check paramter boundaries
      try:
        value = float(self.configTickMarkWidth[axis].value())
      except:
        value = 0.0
      # update parameters
      if(axis in ['left', 'right']):
        self.configTickMarkWidth['left'].setValue(value)
        self.configTickMarkWidth['right'].setValue(value)
      elif(axis in ['left2', 'right2']):
        self.configTickMarkWidth['left2'].setValue(value)
        self.configTickMarkWidth['right2'].setValue(value)
      else:
        self.configTickMarkWidth['top'].setValue(value)
        self.configTickMarkWidth['bottom'].setValue(value)
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
        self.parent.graphicsarea2.configTickMarkWidth['left2'].setValue(value)
        self.parent.graphicsarea2.configTickMarkWidth['right2'].setValue(value)
        self.parent.plotArea.setTickMarkWidth(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
        self.parent.graphicsarea.configTickMarkWidth['left'].setValue(value)
        self.parent.graphicsarea.configTickMarkWidth['right'].setValue(value)
        self.parent.plotArea.setTickMarkWidth(value=value, axis=axis[:-1], redraw=False, target='plot')
      self.parent.plotArea.setTickMarkWidth(value=value, axis=axis, redraw=True, target='plot')
      if(not (axis in ['left2', 'right2'])):
        self.parent.plotArea.setTickMarkWidth(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickMarkWidth(value=value, axis=axis[:-1], redraw=True, target='resid')
    elif(axis == 'slaved'):
      # check paramter boundaries
      try:
        value = float(self.slavedTickMarkWidth.value())
      except:
        value = 0.0
        
      self.parent.plotArea.setTickMarkWidth(value=value, axis='slaved', redraw=True, target='plot')
      self.parent.plotArea.setTickMarkWidth(value=value, axis='slaved', redraw=True, target='resid')

  def changeTickMarkColor(self, axis='x'):
    # changes color of tick marks
    if(axis in ['left', 'right', 'top', 'bottom', 'left2', 'right2']):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.ticksColor[axis]]
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis in ['left', 'right'])):
          self.parent.plotArea.setTickMarkColor(value=value, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis in ['left2', 'right2'])):
          self.parent.plotArea.setTickMarkColor(value=value, axis=axis[:-1], redraw=False, target='plot')
        self.parent.plotArea.setTickMarkColor(value=value, axis=axis, redraw=True, target='plot')
        if(not (axis in ['left2', 'right2'])):
          self.parent.plotArea.setTickMarkColor(value=value, axis=axis, redraw=True, target='resid')
        elif(self.parent.plotArea.splitY):
          self.parent.plotArea.setTickMarkColor(value=value, axis=axis[:-1], redraw=True, target='resid')
        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksColor[axis][0:3]]
        if(axis in ['left', 'right']):
          self.configTickMarkColor['left'].setMyColor(*colorvalue)
          self.configTickMarkColor['right'].setMyColor(*colorvalue)
          # check for split y axis and slave setting if needed
          if(self.parent.plotArea.splitY):
            self.parent.graphicsarea2.configTickMarkColor['left2'].setMyColor(*colorvalue)
            self.parent.graphicsarea2.configTickMarkColor['right2'].setMyColor(*colorvalue)
        elif(axis in ['left2', 'right2']):
          self.configTickMarkColor['left2'].setMyColor(*colorvalue)
          self.configTickMarkColor['right2'].setMyColor(*colorvalue)
          if(self.parent.plotArea.splitY):
            self.parent.graphicsarea.configTickMarkColor['left'].setMyColor(*colorvalue)
            self.parent.graphicsarea.configTickMarkColor['right'].setMyColor(*colorvalue)
        else:
          self.configTickMarkColor['top'].setMyColor(*colorvalue)
          self.configTickMarkColor['bottom'].setMyColor(*colorvalue)
    elif(axis == 'slaved'):
      # get current color
      prevColor = [int(255. * i) for i in self.parent.plotArea.ticksColor[axis]]
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setTickMarkColor(value=value, axis='slaved', redraw=True, target='plot')
        self.parent.plotArea.setTickMarkColor(value=value, axis='slaved', redraw=True, target='resid')

        # update color button
        colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksColor[axis][0:3]]
        self.slavedTickMarkColor.setMyColor(*colorvalue)

  def automaticAxisTicks(self, axis='x'):
    # automatically sets axis ticks
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'xs']):
      if(axis in ['x', 'x2']):
        if(((axis == 'x') and self.configTickXAuto.isChecked()) or ((axis == 'x2') and self.configInnerTickXAuto.isChecked())):
          nuTicks = self.parent.plotArea.setAutoTicks(axis=axis, redraw=True, target='resid')
          nuTicks = self.parent.plotArea.setAutoTicks(axis=axis, redraw=True, target='plot')
          # update entryfield
          tickstr = self.magicTickstring(nuTicks)
          if(axis == 'x'):
            self.configTickXEntry.setText(tickstr)
          else:
            self.configInnerTickXEntry.setText(tickstr)
        else:
          # activate custom ticks
          self.changeAxisTicks(axis=axis, updateAuto=False)
      elif(axis in ['y', 'y2']):
        if(self.configTickYAuto.isChecked()):
          nuTicks = self.parent.plotArea.setAutoTicks(axis=axis, redraw=True, target='plot')
          # update entryfield
          tickstr = self.magicTickstring(nuTicks)
          self.configTickYEntry.setText(tickstr)
        else:
          # activate custom ticks
          self.changeAxisTicks(axis=axis, updateAuto=False)
      elif(axis in ['resid']):
        if(self.configTickResidYAuto.isChecked()):
          nuTicks = self.parent.plotArea.setAutoTicks(axis=axis, redraw=True, target='resid')
          # update entryfield
          tickstr = self.magicTickstring(nuTicks)
          self.configTickResidYEntry.setText(tickstr)
        else:
          # activate custom ticks
          self.changeAxisTicks(axis=axis, updateAuto=False)
      elif(axis in ['xs']):
        # copy ticks from primary axis
        if(self.slavedTickXAuto.isChecked()):
          nuticks = self.configTickXEntry.text()
          self.slavedTickXEntry.setText(nuticks)

        # activate custom ticks
        self.parent.plotArea.slavedTicksXAuto = self.slavedTickXAuto.isChecked()
        self.changeAxisTicks(axis=axis, updateAuto=False)

  def dataAxisYTicks(self, axis='y'):
    # set x ticks to label values
    currDataSet = self.parent.activeData
    self.parent.plotArea.setDataAxisYTicks(currDataSet, redraw=True, target='plot', axis=axis)

  def dataAxisTicks(self, splitX=False):
    # set x ticks to label values
    currDataSet = self.parent.activeData
    self.parent.plotArea.setDataAxisTicks(currDataSet, redraw=True, target='plot', splitX=splitX)
    self.parent.plotArea.setDataAxisTicks(currDataSet, redraw=True, target='resid', splitX=splitX)

  def useCurrentDim(self):
    # sets export dimensions to current dimensions on screen
    # obtain current dimensions
    currwidth, currheight = self.parent.plotArea.matplot.get_size_inches()
    self.exportSizeX.setText('{:4.2f}'.format(currwidth))
    self.exportSizeY.setText('{:4.2f}'.format(currheight))
    self.parent.plotArea.setExportSize(width=currwidth, height=currheight, redraw=True)

  def checkXkcdSetting(self, entryfield=None, item='scale', minval=0, maxval=1):
    # restrains dimensions for figure export
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    self.parent.plotArea.setXkcdSetting(value=value, item=item, redraw=True)
    
  def checkExportSize(self, entryfield=None, axis='x', minval=0, maxval=1):
    # restrains dimensions for figure export
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    if(axis=='x'):
      self.parent.plotArea.setExportSize(width=value, redraw=True)
    elif(axis=='y'):
      self.parent.plotArea.setExportSize(height=value, redraw=True)
        
  def changeAxisTicks(self, axis='x', updateAuto=False):
    if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'xs']):
      if(axis == 'x'):
        entryfield = self.configTickXEntry
      elif(axis == 'xs'):
        entryfield = self.slavedTickXEntry
      elif(axis in ['y', 'y2']):
        entryfield = self.configTickYEntry
      elif(axis == 'resid'):
        entryfield = self.configTickResidYEntry
      else:
        entryfield = self.configInnerTickXEntry
      
      tickstr = str(entryfield.text())
      # process new tick string
      nuTicks = tickstr.split(',')
      nuTicks = [i.strip() for i in nuTicks]
      
      # convert to floats
      nuTicks_num = []
      for entry in nuTicks:
        if(self.parent.isNumber(entry)):
          nuTicks_num.append(float(entry))
      nuTicks_num = np.array(sorted(nuTicks_num))

      # update entryfield
      tickstr = self.magicTickstring(nuTicks_num)
      entryfield.setText(tickstr)
      
      # set ticks in plot
      if(axis == 'x'):
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='plot')
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='resid')
      elif(axis == 'xs'):
        # need to update this here to avoid problems with setAxisTicks() call
        if(updateAuto):
          self.parent.plotArea.slavedTicksXAuto = False
          self.slavedTickXAuto.setChecked(False)
        
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='plot')
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='resid')
      elif(axis in ['y', 'y2']):
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='plot')
      elif(axis == 'resid'):
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='resid')
      else:
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='plot')
        self.parent.plotArea.setAxisTicks(value=nuTicks_num, axis=axis, redraw=True, target='resid')

  def toggleMinorTicks(self, entryfield=None, axis='x'):
    # turns on/off display of minor ticks
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'resid', 'x2', 'xs']):
        value = entryfield.isChecked()
        if(value):
          # retrieve minor ticks value
          if(axis == 'x'):
            value = self.configMinorTickX.currentIndex() + 2
          elif(axis == 'x2'):
            value = self.configInnerMinorTickX.currentIndex() + 2
          elif(axis == 'xs'):
            value = self.slavedMinorTickX.currentIndex() + 2
          elif(axis in ['y', 'y2']):
            value = self.configMinorTickY.currentIndex() + 2
          else:
            value = self.configMinorTickResid.currentIndex() + 2
        else:
          # this will turn off the ticklis
          value = 0
  
        # set the minor ticks
        if(axis in ['x', 'x2', 'xs']):
          self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='plot')
          self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='resid')
        elif(axis == 'y'):
          # check for split y axis and slave setting if needed
          '''
          if(self.parent.plotArea.splitY):
            toggled = entryfield.isChecked()
            self.parent.graphicsarea2.configMinorTickYLabel.setChecked(toggled)
            self.parent.plotArea.setMinorTick(value=value, axis=axis + '2', redraw=False, target='plot')
          '''
          self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='plot')
        elif(axis == 'y2'):
          '''
          if(self.parent.plotArea.splitY):
            toggled = entryfield.isChecked()
            self.parent.graphicsarea.configMinorTickYLabel.setChecked(toggled)
            self.parent.plotArea.setMinorTick(value=value, axis='y', redraw=False, target='plot')
          '''
          self.parent.plotArea.setMinorTick(value=value, axis=axis, redraw=True, target='plot')
        else:
          self.parent.plotArea.setMinorTick(value=value, axis='y', redraw=True, target='resid')

  def changeMinorTickRelativeLength(self, entryfield=None, axis='x', minval=0.1, maxval=10.0):
    # check paramter boundaries
    try:
      value = float(entryfield.text())
      originalvalue = value
    except:
      value = 0.0
      originalvalue = 1.0
    value = np.min((value, maxval))
    value = np.max((value, minval))
    # update parameters
    if (value != originalvalue):
      entryfield.setText(str(value))
      
    if(axis in ['x', 'xs']):
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis, redraw=True, target='plot')
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis, redraw=True, target='resid')
    elif(axis == 'y'):
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY):
        self.parent.graphicsarea2.configMinorTickYRelativeLength.setText(str(value))
        self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis + '2', redraw=False, target='plot')
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis, redraw=True, target='plot')
    elif(axis == 'y2'):
      if(self.parent.plotArea.splitY):
        self.parent.graphicsarea.configMinorTickYRelativeLength.setText(str(value))
        self.parent.plotArea.setMinorTickRelativeLength(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis, redraw=True, target='plot')
    elif(axis == 'resid'):
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis='y', redraw=True, target='resid')
    else:
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis, redraw=True, target='plot')
      self.parent.plotArea.setMinorTickRelativeLength(value=value, axis=axis, redraw=True, target='resid')

  def changeTickLabelPad(self, entryfield=None, axis='x', minval=0, maxval=1):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYPad.setText(str(value))
        self.parent.plotArea.setTickLabelPad(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYPad.setText(str(value))
        self.parent.plotArea.setTickLabelPad(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelPad(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelPad(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelPad(value=value, axis=axis[:-1], redraw=True, target='resid')
      
  def changeTickLabelPad2(self, entryfield=None, axis='x', minval=0, maxval=1):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYPad2.setText(str(value))
        self.parent.plotArea.setTickLabelPad2(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYPad2.setText(str(value))
        self.parent.plotArea.setTickLabelPad2(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelPad2(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelPad2(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelPad2(value=value, axis=axis[:-1], redraw=True, target='resid')

  def changeTickLabelLinespacing(self, entryfield=None, axis='x', minval=0, maxval=1):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYLinespacing.setText(str(value))
        self.parent.plotArea.setTickLabelLinespacing(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYLinespacing.setText(str(value))
        self.parent.plotArea.setTickLabelLinespacing(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelLinespacing(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelLinespacing(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelLinespacing(value=value, axis=axis[:-1], redraw=True, target='resid')
      
  def changeTickLabelBold(self, axis='x'):
    # changes formatting of tick label
    if(axis in ['x', 'y', 'y2', 'xs']):
      if(axis == 'x'):
        isChecked = self.configTickXBold.isChecked()
      elif(axis == 'xs'):
        isChecked = self.slavedTickXBold.isChecked()
      else:
        isChecked = self.configTickYBold.isChecked()
        
      if(isChecked):
        value = 'bold'
      else:
        value = 'normal'
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYBold.setChecked(isChecked)
        self.parent.plotArea.setTickLabelBold(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYBold.setChecked(isChecked)
        self.parent.plotArea.setTickLabelBold(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelBold(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelBold(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelBold(value=value, axis=axis[:-1], redraw=True, target='resid')

  def changeTickLabelItalic(self, axis='x'):
    # changes formatting of tick label
    if(axis in ['x', 'y', 'y2', 'xs']):
      if(axis == 'x'):
        isChecked = self.configTickXItalic.isChecked()
      elif(axis == 'xs'):
        isChecked = self.slavedTickXItalic.isChecked()
      else:
        isChecked = self.configTickYItalic.isChecked()
        
      if(isChecked):
        value = 'italic'
      else:
        value = 'normal'
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYItalic.setChecked(isChecked)
        self.parent.plotArea.setTickLabelItalic(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYItalic.setChecked(isChecked)
        self.parent.plotArea.setTickLabelItalic(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelItalic(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelItalic(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelItalic(value=value, axis=axis[:-1], redraw=True, target='resid')

  def setTickLabelAlignment(self, axis='x'):
    # sets alignment of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      flag = False
      if(axis == 'x'):
        useAlignment = str(self.configTickXAlignment.currentText())
        if(useAlignment in self.alignHorizontal):
          flag = True
      elif(axis == 'xs'):
        useAlignment = str(self.slavedTickXAlignment.currentText())
        if(useAlignment in self.alignHorizontal):
          flag = True
      else:
        useAlignment = str(self.configTickYAlignment.currentText())
        if(useAlignment in self.alignHorizontal):
          flag = True
      
      if(flag):
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y')):
          index = self.configTickYAlignment.currentIndex()
          self.parent.graphicsarea2.configTickYAlignment.setCurrentIndex(index)
          self.parent.plotArea.setTickLabelAlignment(value=useAlignment, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis == 'y2')):
          index = self.configTickYAlignment.currentIndex()
          self.parent.graphicsarea.configTickYAlignment.setCurrentIndex(index)
          self.parent.plotArea.setTickLabelAlignment(value=useAlignment, axis='y', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAlignment(value=useAlignment, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setTickLabelAlignment(value=useAlignment, axis=axis, redraw=True, target='resid')
        elif(self.parent.plotArea.splitY):
          self.parent.plotArea.setTickLabelAlignment(value=useAlignment, axis=axis[:-1], redraw=True, target='resid')

  def setTickLabelAlignmentVertical(self, axis='x'):
    # sets vertical alignment of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      flag = False
      if(axis == 'x'):
        useAlignment = str(self.configTickXAlignmentVertical.currentText())
        if(useAlignment in self.alignVertical):
          flag = True
      elif(axis == 'xs'):
        useAlignment = str(self.slavedTickXAlignmentVertical.currentText())
        if(useAlignment in self.alignVertical):
          flag = True
      else:
        useAlignment = str(self.configTickYAlignmentVertical.currentText())
        if(useAlignment in self.alignVertical):
          flag = True
      
      if(flag):
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y')):
          index = self.configTickYAlignmentVertical.currentIndex()
          self.parent.graphicsarea2.configTickYAlignmentVertical.setCurrentIndex(index)
          self.parent.plotArea.setTickLabelAlignmentVertical(value=useAlignment, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis == 'y2')):
          index = self.configTickYAlignmentVertical.currentIndex()
          self.parent.graphicsarea.configTickYAlignmentVertical.setCurrentIndex(index)
          self.parent.plotArea.setTickLabelAlignmentVertical(value=useAlignment, axis='y', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelAlignmentVertical(value=useAlignment, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setTickLabelAlignmentVertical(value=useAlignment, axis=axis, redraw=True, target='resid')
        elif(self.parent.plotArea.splitY):
          self.parent.plotArea.setTickLabelAlignmentVertical(value=useAlignment, axis=axis[:-1], redraw=True, target='resid')

  def alterTickLabelAngle(self, entryfield=None, axis='x'):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYAngle.setValue(value)
        self.parent.plotArea.setTickLabelAngle(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYAngle.setValue(value)
        self.parent.plotArea.setTickLabelAngle(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelAngle(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelAngle(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelAngle(value=value, axis=axis[:-1], redraw=True, target='resid')

  def alterTickLabelSize(self, entryfield=None, axis='x'):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
        
      # check for split y axis and slave setting if needed
      if(self.parent.plotArea.splitY and (axis == 'y')):
        self.parent.graphicsarea2.configTickYSize.setValue(value)
        self.parent.plotArea.setTickLabelSize(value=value, axis=axis + '2', redraw=False, target='plot')
      if(self.parent.plotArea.splitY and (axis == 'y2')):
        self.parent.graphicsarea.configTickYSize.setValue(value)
        self.parent.plotArea.setTickLabelSize(value=value, axis='y', redraw=False, target='plot')
      self.parent.plotArea.setTickLabelSize(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setTickLabelSize(value=value, axis=axis, redraw=True, target='resid')
      elif(self.parent.plotArea.splitY):
        self.parent.plotArea.setTickLabelSize(value=value, axis=axis[:-1], redraw=True, target='resid')

  def setTickFormat(self, entryfield=None, axis='x'):
    # sets format of tick labels
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        flag = False
        useOption = str(entryfield.currentText())
        if(useOption in self.formatOptions):
          flag = True
          self.updateTickFormatFields(axis=axis, tickFormat=useOption, slaved=False)
          # check for split y axis and slave setting if needed
          if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
            self.updateTickFormatFields(axis=axis + '2', tickFormat=useOption, slaved=True)
          if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
            self.updateTickFormatFields(axis='y', tickFormat=useOption, slaved=True)
        
        if(flag):
          # check for split y axis and slave setting if needed
          '''
          # better not slave the format settings which may have rather different requirements on split axes
          if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
            index = self.configTickYFormat.currentIndex()
            self.parent.graphicsarea2.configTickYFormat.setCurrentIndex(index)
            self.parent.plotArea.setTickFormat(axis=axis + '2', value=useOption, redraw=False)
          if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
            index = self.configTickYFormat.currentIndex()
            self.parent.graphicsarea.configTickYFormat.setCurrentIndex(index)
            self.parent.plotArea.setTickFormat(axis='y', value=useOption, redraw=False)
          '''
          self.parent.plotArea.setTickFormat(axis=axis, value=useOption, redraw=True)
          if((axis == 'y') or ((axis == 'y2') and self.parent.plotArea.splitY)):
            self.parent.plotArea.setTickFormat(axis='resid', value=useOption, redraw=True)
          if(axis == 'x'):
            self.parent.plotArea.setTickFormat(axis='residx', value=useOption, redraw=True)
          if(axis == 'x2'):
            self.parent.plotArea.setTickFormat(axis='residx2', value=useOption, redraw=True)
          if(axis == 'xs'):
            self.parent.plotArea.setTickFormat(axis='xs', value=useOption, redraw=True)

  def updateTickFormatFields(self, axis='x', tickFormat='default', slaved=False):
    # changes activity of entry fields for tick label formatting
    if(axis == 'x'):
      items = ('configTickXFormatPrecision', 'configTickXFormatTrailZero', 'configTickXFormatSeparator', 'configTickXFormatComma', 'configTickXPrefix', 'configTickXPostfix')
    elif(axis == 'x2'):
      items = ('configInnerTickXFormatPrecision', 'configInnerTickXFormatTrailZero', 'configInnerTickXFormatSeparator', 'configInnerTickXFormatComma', 'configInnerTickXPrefix', 'configInnerTickXPostfix')
    elif(axis in ['y', 'y2']):
      items = ('configTickYFormatPrecision', 'configTickYFormatTrailZero', 'configTickYFormatSeparator', 'configTickYFormatComma', 'configTickYPrefix', 'configTickYPostfix')
    elif(axis == 'xs'):
      items = ('slavedXFormatPrecision', 'slavedXFormatTrailZero', 'slavedXFormatSeparator', 'slavedXFormatComma', 'slavedXPrefix', 'slavedXPostfix')
    else:
      items = ()
    
    # do this for split y axis
    if(slaved):
      target = self.parent.graphicsarea2
      if(target == self):
        target = self.parent.graphicsarea
    else:
      target = self
      
    for itemName in items:
      if(hasattr(target, itemName)):
        item = target.__dict__[itemName]
        if(hasattr(item, 'setEnabled')):
          item.setEnabled(tickFormat != 'default')

  def setFormatTrailZero(self, entryfield=None, axis='x'):
    # sets trailing zeros of tick labels
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        value = entryfield.isChecked()
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea2.configTickYFormatTrailZero.setChecked(value)
          self.parent.plotArea.setFormatTrailZero(axis=axis + '2', value=value, redraw=False)
        if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea.configTickYFormatTrailZero.setChecked(value)
          self.parent.plotArea.setFormatTrailZero(axis='y', value=value, redraw=False)
        self.parent.plotArea.setFormatTrailZero(axis=axis, value=value, redraw=True)
        if((axis == 'y') or ((axis == 'y2') and self.parent.plotArea.splitY)):
          self.parent.plotArea.setFormatTrailZero(axis='resid', value=value, redraw=True)
        if(axis == 'x'):
          self.parent.plotArea.setFormatTrailZero(axis='residx', value=value, redraw=True)
        if(axis == 'x2'):
          self.parent.plotArea.setFormatTrailZero(axis='residx2', value=value, redraw=True)
        if(axis == 'xs'):
          self.parent.plotArea.setFormatTrailZero(axis='residxs', value=value, redraw=True)

  def setFormatComma(self, entryfield=None, axis='x'):
    # sets comma as decimal sign in tick labels
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        value = entryfield.isChecked()
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea2.configTickYFormatComma.setChecked(value)
          self.parent.plotArea.setFormatComma(axis=axis + '2', value=value, redraw=False)
        if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea.configTickYFormatComma.setChecked(value)
          self.parent.plotArea.setFormatComma(axis='y', value=value, redraw=False)
        self.parent.plotArea.setFormatComma(axis=axis, value=value, redraw=True)
        if((axis == 'y') or ((axis == 'y2') and self.parent.plotArea.splitY)):
          self.parent.plotArea.setFormatComma(axis='resid', value=value, redraw=True)
        if(axis == 'x'):
          self.parent.plotArea.setFormatComma(axis='residx', value=value, redraw=True)
        if(axis == 'x2'):
          self.parent.plotArea.setFormatComma(axis='residx2', value=value, redraw=True)
        if(axis == 'xs'):
          self.parent.plotArea.setFormatComma(axis='residxs', value=value, redraw=True)

  def setFormatSeparator(self, entryfield=None, axis='x'):
    # sets separating 1000s of tick labels
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        value = entryfield.isChecked()
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea2.configTickYFormatSeparator.setChecked(value)
          self.parent.plotArea.setFormatSeparator(axis=axis + '2', value=value, redraw=False)
        if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea.configTickYFormatSeparator.setChecked(value)
          self.parent.plotArea.setFormatSeparator(axis='y', value=value, redraw=False)
        self.parent.plotArea.setFormatSeparator(axis=axis, value=value, redraw=True)
        if((axis == 'y') or ((axis == 'y2') and self.parent.plotArea.splitY)):
          self.parent.plotArea.setFormatSeparator(axis='resid', value=value, redraw=True)
        if(axis == 'x'):
          self.parent.plotArea.setFormatSeparator(axis='residx', value=value, redraw=True)
        if(axis == 'x2'):
          self.parent.plotArea.setFormatSeparator(axis='residx2', value=value, redraw=True)
        if(axis == 'xs'):
          self.parent.plotArea.setFormatSeparator(axis='residxs', value=value, redraw=True)

  def setFormatFix(self, entryfield=None, axis='x', prefix=True):
    # sets pre/postfix of axis labels
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        value = entryfield.text()
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          if(prefix):
            self.parent.graphicsarea2.configTickYPrefix.setText(value)
          else:
            self.parent.graphicsarea2.configTickYPostfix.setText(value)
          self.parent.plotArea.setFormatFix(axis=axis + '2', value=value, redraw=False, prefix=prefix)
        if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          if(prefix):
            self.parent.graphicsarea.configTickYPrefix.setText(value)
          else:
            self.parent.graphicsarea.configTickYPostfix.setText(value)
          self.parent.plotArea.setFormatFix(axis='y', value=value, redraw=False, prefix=prefix)
        self.parent.plotArea.setFormatFix(axis=axis, value=value, redraw=True, prefix=prefix)
        if((axis == 'y') or ((axis == 'y2') and self.parent.plotArea.splitY)):
          self.parent.plotArea.setFormatFix(axis='resid', value=value, redraw=True, prefix=prefix)
        if(axis == 'x'):
          self.parent.plotArea.setFormatFix(axis='residx', value=value, redraw=True, prefix=prefix)
        if(axis == 'x2'):
          self.parent.plotArea.setFormatFix(axis='residx2', value=value, redraw=True, prefix=prefix)

  def changeTickFormatPrecision(self, entryfield=None, axis='x', minval=0, maxval=20):
    # sets precision in format of tick labels
    if(entryfield != None):
      if(axis in ['x', 'y', 'y2', 'x2', 'xs']):
        # check paramter boundaries
        try:
          value = int(entryfield.text())
          originalvalue = value
        except:
          value = 0
          originalvalue = 1
        value = np.min((value, maxval))
        value = np.max((value, minval))
        # update parameters
        if (value != originalvalue):
          entryfield.setText(str(value))

        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea2.configTickYFormatPrecision.setText(str(value))
          self.parent.plotArea.setTickFormatPrecision(axis=axis + '2', value=value, redraw=False)
        if(self.parent.plotArea.splitY and (axis == 'y2') and (self.parent.plotArea.modeY == self.parent.plotArea.modeY2)):
          self.parent.graphicsarea.configTickYFormatPrecision.setText(str(value))
          self.parent.plotArea.setTickFormatPrecision(axis='y', value=value, redraw=False)
        self.parent.plotArea.setTickFormatPrecision(axis=axis, value=value, redraw=True)
        if((axis == 'y') or ((axis == 'y2') and self.parent.plotArea.splitY)):
          self.parent.plotArea.setTickFormatPrecision(axis='resid', value=value, redraw=True)
        if(axis == 'x'):
          self.parent.plotArea.setTickFormatPrecision(axis='residx', value=value, redraw=True)
        if(axis == 'x2'):
          self.parent.plotArea.setTickFormatPrecision(axis='residx2', value=value, redraw=True)
        if(axis == 'xs'):
          self.parent.plotArea.setTickFormatPrecision(axis='residxs', value=value, redraw=True)

  def alterAxisLabelAngle(self, entryfield=None, axis='x'):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
        
      self.parent.plotArea.setAxisLabelAngle(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelAngle(value=value, axis=axis, redraw=True, target='resid')

  def changeAxisLabelLinescpacing(self, entryfield=None, axis='x', minval=0, maxval=1):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      self.parent.plotArea.setAxisLabelLinespacing(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelLinespacing(value=value, axis=axis, redraw=True, target='resid')

  def changeAxisLabelPad(self, entryfield=None, axis='x', minval=0, maxval=1):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      self.parent.plotArea.setAxisLabelPad(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelPad(value=value, axis=axis, redraw=True, target='resid')

  def changeAxisLabelPos(self, entryfield=None, axis='x', minval=0, maxval=1):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
        
      self.parent.plotArea.setAxisLabelPos(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelPos(value=value, axis=axis, redraw=True, target='resid')

  def alterAxisLabelSize(self, entryfield=None, axis='x'):
    if(axis in ['x', 'y', 'y2', 'xs']):
      # check paramter boundaries
      try:
        value = float(entryfield.value())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      # update parameters
      if (value != originalvalue):
        entryfield.setValue(value)
        
      self.parent.plotArea.setAxisLabelSize(value=value, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y', 'xs']):
        self.parent.plotArea.setAxisLabelSize(value=value, axis=axis, redraw=True, target='resid')

  def changeFigureColor(self):
    # changes color of canvas
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.figureColor]

    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setFigureColor(value=value, redraw=True, target='plot', silent=False)
      self.parent.plotArea.setFigureColor(value=value, redraw=True, target='resid', silent=True)
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.figureColor[0:3]]
      self.configFigureColorButton.setMyColor(*colorvalue)

  def toggleFigureFill(self):
    # toggles whether figure filled or not
    state = self.configFigureLabel.isChecked()
    self.parent.plotArea.toggleFigureFill(value=state, redraw=True, target='plot', silent=False)
    self.parent.plotArea.toggleFigureFill(value=state, redraw=True, target='resid', silent=True)
    
  def toggleFrameDraw(self):
    # toggles whether figure frame is drawn or not
    state = self.configFrameLabel.isChecked()
    self.parent.plotArea.toggleFrameDraw(value=state, redraw=True, target='plot')
    self.parent.plotArea.toggleFrameDraw(value=state, redraw=True, target='resid')

  def toggleCanvasFill(self):
    # toggles whether canvas filled or not
    state = self.configCanvasLabel.isChecked()
    self.parent.plotArea.toggleCanvasFill(value=state, redraw=True, target='plot')
    self.parent.plotArea.toggleCanvasFill(value=state, redraw=True, target='resid')
    
  def changeCanvasColor(self):
    # changes color of canvas
    # get current color
    prevColor = [int(255. * i) for i in self.parent.plotArea.canvasColor]

    prevColor = QtGui.QColor(*prevColor)
    # call QColor dialog
    nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
    if (nuColor.isValid()):
      value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
      value = [i / 255. for i in value]
      self.parent.plotArea.setCanvasColor(value=value, redraw=True, target='plot')
      self.parent.plotArea.setCanvasColor(value=value, redraw=True, target='resid')
      # update color button
      colorvalue = [int(i * 255.) for i in self.parent.plotArea.canvasColor[0:3]]
      self.configCanvasColorButton.setMyColor(*colorvalue)
    
  def changeTickLabelColor(self, axis='x'):
    # changes color of axis ticks
    if(axis in ['x', 'y', 'y2', 'xs']):
      # get current color
      if(axis == 'x'):
        prevColor = [int(255. * i) for i in self.parent.plotArea.ticksXColor]
      elif(axis == 'xs'):
        prevColor = [int(255. * i) for i in self.parent.plotArea.slavedTicksXColor]
      else:
        prevColor = [int(255. * i) for i in self.parent.plotArea.ticksYColor]
  
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        # check for split y axis and slave setting if needed
        if(self.parent.plotArea.splitY and (axis == 'y')):
          self.parent.plotArea.setTickLabelColor(value=value, axis=axis + '2', redraw=False, target='plot')
        if(self.parent.plotArea.splitY and (axis == 'y2')):
          self.parent.plotArea.setTickLabelColor(value=value, axis='y', redraw=False, target='plot')
        self.parent.plotArea.setTickLabelColor(value=value, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setTickLabelColor(value=value, axis=axis, redraw=True, target='resid')
        # update color button
        if (axis == 'x'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksXColor[0:3]]
          self.configTickXColorButton.setMyColor(*colorvalue)
        elif (axis == 'xs'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.slavedTicksXColor[0:3]]
          self.slavedTickXColorButton.setMyColor(*colorvalue)
        elif(axis == 'y'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksYColor[0:3]]
          self.configTickYColorButton.setMyColor(*colorvalue)
          # check for split y axis and slave setting if needed
          if(self.parent.plotArea.splitY):
            self.parent.graphicsarea2.configTickYColorButton.setMyColor(*colorvalue)
        elif(axis == 'y2'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.ticksY2Color[0:3]]
          self.configTickYColorButton.setMyColor(*colorvalue)
          if(self.parent.plotArea.splitY):
            self.parent.graphicsarea.configTickYColorButton.setMyColor(*colorvalue)

  def changeAxisLabelColor(self, axis='x'):
    # changes color of axis label
    if(axis in ['x', 'y', 'y2', 'xs']):
      # get current color
      if(axis == 'x'):
        prevColor = [int(255. * i) for i in self.parent.plotArea.labelXColor]
      elif(axis == 'xs'):
        prevColor = [int(255. * i) for i in self.parent.plotArea.slavedLabelXColor]
      elif(axis == 'y'):
        prevColor = [int(255. * i) for i in self.parent.plotArea.labelYColor]
      else:
        prevColor = [int(255. * i) for i in self.parent.plotArea.labelY2Color]
  
      prevColor = QtGui.QColor(*prevColor)
      # call QColor dialog
      nuColor = QtWidgets.QColorDialog.getColor(prevColor, self, 'Set Color', QtWidgets.QColorDialog.ColorDialogOption.ShowAlphaChannel)
      if (nuColor.isValid()):
        value = [nuColor.red(), nuColor.green(), nuColor.blue(), nuColor.alpha()]
        value = [i / 255. for i in value]
        self.parent.plotArea.setAxisLabelColor(value=value, axis=axis, redraw=True, target='plot')
        if(axis in ['x', 'y', 'xs']):
          self.parent.plotArea.setAxisLabelColor(value=value, axis=axis, redraw=True, target='resid')
        # update color button
        if (axis == 'x'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.labelXColor[0:3]]
          self.configXColorButton.setMyColor(*colorvalue)
        elif (axis == 'xs'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.slavedLabelXColor[0:3]]
          self.slavedXColorButton.setMyColor(*colorvalue)
        elif(axis == 'y'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.labelYColor[0:3]]
          self.configYColorButton.setMyColor(*colorvalue)
        elif(axis == 'y2'):
          colorvalue = [int(i * 255.) for i in self.parent.plotArea.labelY2Color[0:3]]
          self.configYColorButton.setMyColor(*colorvalue)

  def changeAxisLabel(self, axis='x'):
    # updates axis label
    if(axis in ['x', 'y', 'y2']):
      if(axis == 'x'):
        labeltext = str(self.configXName.text())
        # encode/recode to process newlines correctly
        #labeltext = labeltext.encode('utf-8').decode('unicode-escape')
        labeltext2 = labeltext
      else:
        labeltext = str(self.configYName.text())
        #labeltext = labeltext.encode('utf-8').decode('unicode-escape')
        labeltext2 = labeltext
 
      self.parent.plotArea.setAxisLabel(labeltext=labeltext, axis=axis, redraw=True, target='plot')
      if(axis in ['x', 'y']):
        self.parent.plotArea.setAxisLabel(labeltext=labeltext2, axis=axis, redraw=True, target='resid')

  def toggleGraphicsOption(self):
    # toggles display of advanced graphics
    value = self.configAdvancedSettingsButton.isChecked()
    self.advancedExport['advancedGraphics'] = value
    
    self.parent.graphicsarea.setAdvancedGraphics(value=value)
    if(hasattr(self.parent, 'graphicsarea2')):
      self.parent.graphicsarea2.setAdvancedGraphics(value=value)

    # scroll to bottom
    QtCore.QCoreApplication.processEvents()
    scrollbar = self.parent.graphicsarea.containerScroll.verticalScrollBar()
    scrollbar.setValue(scrollbar.maximum())
    QtCore.QCoreApplication.processEvents()

  def previewThis(self):
    # generates and displays a figure preview
    filename = WORKINGDIR + PATH_SEPARATOR + TEMP_PREVIEWFILE
    if('linux' in platform):
      filename = HOMEDIR + PATH_SEPARATOR + TEMP_PREVIEWFILE
    
    # save main graphics
    # obtain target dimensions
    #targetwidth = float(self.exportSizeX.text())
    #targetheight = float(self.exportSizeY.text())
    # obtain current dimensions
    currwidth, currheight = self.parent.plotArea.matplot.get_size_inches()
    
    # set new dimensions and save
    ### disable this call to preview pic with current canvas size
    ###self.parent.plotArea.matplot.set_size_inches(targetwidth, targetheight)
    success = False
    if(self.parent.plotArea.figureFill):
      useFaceColor = self.parent.plotArea.figureColor
    else:
      useFaceColor = 'none'
    if(self.parent.plotArea.frameDraw):
      useFrameColor = self.parent.plotArea.frameColor
    else:
      useFrameColor = 'none'
    try:
      self.parent.plotArea.matplot.savefig(filename, format='png', dpi=self.advancedExport['plotDPI'] / 2., facecolor=useFaceColor, edgecolor=useFrameColor)
      success = True
    except:
      # some kind of error occurred
      self.parent.statusbar.showMessage('Cannot generate temporary file ' + filename, self.parent.STATUS_TIME)
      
    # revert to old dimensions
    ###self.parent.plotArea.matplot.set_size_inches(currwidth, currheight)
      
    # issue plot redraws b/c for some reason display vanishes
    self.parent.plotArea.dataplotwidget.myRefresh()
    
    # now open a new window to display the preview image
    if(success):
      if(not (hasattr(self.parent, 'previewWindow'))):
        self.parent.previewWindow = PreviewWindow()
        self.parent.previewWindow.setWindowTitle('Preview')
    
        self.centralwidget = QWidgetMac(self.parent.previewWindow)
        self.centralwidget.setMinimumSize(QtCore.QSize(320, 240))
        self.parent.previewWindow.setCentralWidget(self.centralwidget)
        
        self.vLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
        self.vLayout.setContentsMargins(*[2]*4)
        
        # export button
        self.clipboardButton = QPushButtonMac()
        self.clipboardButton.setText('Copy to Clipboard')
        self.clipboardButton.setMaximumSize(QtCore.QSize(150, int(BASE_SIZE)))
        self.clipboardButton.setMinimumSize(QtCore.QSize(150, int(BASE_SIZE)))
        self.clipboardButton.clicked.connect(self.copyImageClipboard)
        self.vLayout.addWidget(self.clipboardButton)
  
        # generate QLabel for display
        self.displayPreview = QtWidgets.QLabel()
        self.vLayout.addWidget(self.displayPreview)
        self.parent.previewWindow.setDisplayPreview(self.displayPreview)
        
      # update picture
      self.displayPicture = QtGui.QPixmap(filename)
      currSize = self.displayPicture.size()
      # scale to compensate DPI ratio self.advancedExport['plotDPI'] / 2. vs 100
      self.scaledPicture = self.displayPicture.scaled(int(currSize.width() / (self.advancedExport['plotDPI'] / 200.)), int(currSize.height() / (self.advancedExport['plotDPI'] / 200.)), QtCore.Qt.AspectRatioMode.KeepAspectRatio)
      self.displayPreview.resize(int(self.scaledPicture.width()), int(self.scaledPicture.height()))
      self.displayPreview.setPixmap(self.scaledPicture)
      # explicitly update windows size
      self.parent.previewWindow.setFixedSize(self.vLayout.sizeHint())
      
      # apply styles and show
      if(QSTYLE != None):
        self.parent.previewWindow.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        EXTRASTYLE = '\nQWidget#preview {background-color: transparent;}'
        self.parent.previewWindow.setStyleSheet(QSTYLESHEET + EXTRASTYLE)
      self.parent.previewWindow.show()
      self.parent.previewWindow.activateWindow()
      self.parent.previewWindow.focusNextChild()

  def copyImageClipboard(self):
    # copies preview image to clipboard
    data = QtCore.QMimeData()
    # first check whether we still find the temp image file generated for the preview
    filename = WORKINGDIR + PATH_SEPARATOR + TEMP_PREVIEWFILE
    if(isfile(filename)):
      url = QtCore.QUrl.fromLocalFile(filename)
      data.setUrls([url])

    # for good measure also add a regular bitmap image and let downstream applications chose which to use
    if(hasattr(self, 'displayPicture')):
      image = self.displayPicture.toImage()
      data.setImageData(image)
      
    # check whether we managed to retrieve any image
    if(len(data.formats())):
      QtWidgets.QApplication.clipboard().setMimeData(data)
      
  def setAdvancedExport(self, key=None, value=None):
    # updates advanced export settings
    if(key in self.advancedExport):
      self.advancedExport[key] = value

  def setAdvancedGraphics(self, value=True):
    # toggles advanced graphics options
    self.advancedExport['advancedGraphics'] = value
    for entry in self.advancedGraphicsSettings:
      if(value):
        if(hasattr(entry, 'show')):
          if(not hasattr(self, 'toggleItemsTransformed')):
            # these are the items on the graphics tab for the second axes
            entry.show()
          elif((not (entry in self.toggleItemsTransformed)) or self.slavedXLabel.isChecked()):
            entry.show()
      elif(hasattr(entry, 'hide')):
        entry.hide()
    # special treatment for splitAxes control
    value =  value and self.parent.plotArea.splitShow
    for item in ['configInnerBox2', 'configInnerBox3', 'configInnerBox5', 'configInnerBox6', 'configInnerMinorTickBox', 'configGridBox2', 'configGridBox3']:
      if(hasattr(self, item)):
        entry = self.__dict__[item]
        if(item in ['configGridBox2', 'configGridBox3']):
          if('x2' in entry):
            entry = entry['x2']
          else:
            continue
        if(value):
          if(hasattr(entry, 'show')):
            entry.show()
        elif(hasattr(entry, 'hide')):
          entry.hide()

  def reportThis(self):
    # generates an HTML report
    self.parent.resultsarea.exportWrapper(modeHTMLOnly=True)

  def exportThis(self):
    global REMEMBERDIR
    # exports current figure and residuals
    filter_options = ['PDF files (*.pdf)', 'Scalable vector graphic (*.svg)', 'Postscript (*.ps)', 'Encapsulated postscript (*.eps)', 'PNG image (*.png)', 'Python script (*.py)', 'All files (*.*)']
    format_options = ['pdf', 'svg', 'ps', 'eps', 'png', 'py', 'pdf']
    filterstring = ';;'.join(filter_options)
    # get save file name
    usedir = REMEMBERDIR
    if(self.currExportFile != None):
      usedir = self.currExportFile
    if(not self.exportFilter in filter_options):
      self.exportFilter = ''
    filename, filter_ = QtWidgets.QFileDialog.getSaveFileName(self, filter=filterstring, directory=usedir, caption='Export Graphics', initialFilter=self.exportFilter)
    self.exportFilter = filter_
    filename = str(filename)
    if(PATH_SEPARATOR in filename):
      REMEMBERDIR = filename.split(PATH_SEPARATOR)[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    elif('/' in filename):
      REMEMBERDIR = filename.split('/')[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    
    # evaluate file format
    if(filter_ in filter_options):
      index = filter_options.index(filter_)
      format_ = format_options[index]
    else:
      # use PDF as default
      format_ = 'pdf'
    
    if(filename != ''):
      # check whether save as Python script selected
      if(format_ != 'py'):
        # enable text export to SVG
        if (format_ == 'svg'):
          if(self.advancedExport['SVGtxt2path']):
            matplotlib.rcParams.update({'svg.fonttype': 'path'})
          else:
            matplotlib.rcParams.update({'svg.fonttype': 'none'})
        # save main graphics
        # obtain target dimensions
        targetwidth = float(self.exportSizeX.text())
        targetheight = float(self.exportSizeY.text())
        # obtain current dimensions
        currwidth, currheight = self.parent.plotArea.matplot.get_size_inches()
        
        if(self.parent.plotArea.figureFill):
          useFaceColor = self.parent.plotArea.figureColor
        else:
          useFaceColor = 'none'
        if(self.parent.plotArea.frameDraw):
          useFrameColor = self.parent.plotArea.frameColor
        else:
          useFrameColor = 'none'
        # set new dimensions and save
        self.parent.plotArea.matplot.set_size_inches(targetwidth, targetheight)
        try:
          self.parent.plotArea.matplot.savefig(filename, format=format_, dpi=self.advancedExport['plotDPI'], facecolor=useFaceColor, edgecolor=useFrameColor)
          # save filename for future reference
          self.currExportFile = filename
        except:
          # some kind of error occurred
          self.parent.statusbar.showMessage('Cannot save graphics file ' + filename, self.parent.STATUS_TIME)
        
        # revert to old dimensions
        self.parent.plotArea.matplot.set_size_inches(currwidth, currheight)
        
        # save resid graphics?
        if(self.advancedExport['plotResid']):
          if('.' in filename):
            filename_split = filename.split('.')
            filename = '.'.join(filename_split[:-1])
            filename += '_resid.' + filename_split[-1]
          else:
            filename += '_resid'
          # obtain target dimensions
          try:
            relativeSizes = self.parent.plotArea.masterwidget.sizes()
            sizeRatio = 1. * relativeSizes[0] / relativeSizes[1]
          except:
            sizeRatio = 4.0
          targetwidth = float(self.exportSizeX.text())
          targetheight = float(self.exportSizeY.text()) / sizeRatio
          # obtain current dimensions
          currwidth, currheight = self.parent.plotArea.residplot.get_size_inches()
          
          # set new dimensions and save
          self.parent.plotArea.residplot.set_size_inches(targetwidth, targetheight)
          try:
            self.parent.plotArea.residplot.savefig(filename, format=format_, dpi=self.advancedExport['plotDPI'], facecolor=useFaceColor, edgecolor=useFrameColor)
          except:
            # some kind of error occurred
            self.parent.statusbar.showMessage('Cannot save graphics file ' + filename, self.parent.STATUS_TIME)
          
          # revert to old dimensions
          self.parent.plotArea.residplot.set_size_inches(currwidth, currheight)
          
        # issue plot redraws b/c for some reason display vanishes
        self.parent.plotArea.dataplotwidget.myRefresh()
        self.parent.plotArea.residplotwidget.myRefresh()
      else:
        # prepare settings for later output
        settingsAxes = 'facecolor,xlabel,xscale,xticks,xlim,ylabel,yscale,yticks,ylim'.split(',')
        # do not include visible for axes sub as it interferes with gridlines
        settingsAxesSub = 'label_position,ticks_position'.split(',')
        settingsSpines = 'edgecolor,extents,linewidth,linestyle,visible'.split(',')
        settingsLabels = 'size,color,position,rotation,fontweight,fontstyle,variant,horizontalalignment,verticalalignment,visible'.split(',')
        
        settingsTicks = {'fontweight': 'Weight', 'fontstyle': 'Style', 'rotation': 'Angle'}
        settingsTicks.update({'horizontalalignment': 'Alignment', 'verticalalignment': 'AlignmentVertical', 'fontsize': 'Size'})
        
        settingsGridlines = {'visible': 'gridVisible', 'linewidth': 'gridWidth', 'linestyle': 'gridStyle',\
                             'color': 'gridColor', 'dash_capstyle': 'gridDashStyle'}

        formatPlot = 'linewidth,linestyle,dash_capstyle,color,markerfacecolor,markerfacecoloralter,markeredgecolor'.split(',')
        formatPlot += 'markeredgewidth,markersize,marker,fillstyle,markevery,gapcolor,zorder'.split(',')
        
        formatScatter = 'facecolor,edgecolor,linewidth,sizes,marker,zorder'.split(',')

        formatErr = 'color,linewidth,linestyle,marker,markerfacecolor,markerfacecoloralt,markeredgecolor'.split(',')
        formatErr += 'markersize,fillstyle,zorder'.split(',')
        
        formatBar = 'linewidth,linestyle,capstyle,facecolor,facecolorAlt,edgecolor,hatch,hatch_linewidth,zorder'.split(',')
        formatStack = 'linewidth,linestyle,facecolor,facecolorAlt,edgecolor,hatch,zorder'.split(',')
        
        formatCanvas = 'facecolor,edgecolor'.split(',')
        formatCanvasPatch = 'facecolor'.split(',')
        
        # save graphics as Python script
        pythonOutput = '##############################################\n# plot script generated by Fit-o-mat         #\n'
        pythonOutput += '# version ' + VERSION + '                              #\n'
        pythonOutput += '''# by A.M. (andreas.moeglich@uni-bayreuth.de) #
##############################################

# Note: not all Fit-o-mat graphics are implemented in this output (too cumbersome and error-prone)
# hence, differences in appearance are fully expected

'''
        pythonOutput += '# this file generated with:\n'
        pythonOutput += '# Python3 version ' + '.'.join([str(i) for i in version_info[0:3]]) + '\n'
        pythonOutput += '# matplotlib version ' + matplotlib.__version__ + '\n'
        pythonOutput += '''

# initialization
import matplotlib      
import matplotlib.pyplot as plt
from matplotlib import patheffects as PathEffects
import numpy as np
from numpy import abs, arccos, arcsin, arctan, exp, cos, cosh, log, log2, log10, power, sin, sinh, sqrt, tan, tanh

# create figures for data/curves and residuals
matplot = plt.figure()
residplot = plt.figure()

# define axes system      
ax = matplot.add_subplot(111)
ax.yaxis.set_label_position('left')
'''
        # check whether we should display split axis
        if(self.parent.plotArea.splitShow):
          pythonOutput += '''
# define split axes system      
ax_div = matplot.add_subplot(111)
'''

        # check whether we should display second axis
        if(self.parent.plotArea.isSecondAxesActive()):
          pythonOutput += '''
# define 2nd axes system      
ax2 = matplot.add_subplot(111)
ax2.yaxis.set_label_position('right')
ax2.yaxis.set_ticks_position('right')
'''
          # check whether we should display split axis
          if(self.parent.plotArea.splitShow):
            pythonOutput += '''
# define split axes system      
ax2_div = matplot.add_subplot(111)
'''
        # and now initialize the resid plot
        pythonOutput += '''
# define resid axes system      
ax_resid = residplot.add_subplot(111)
ax_resid.yaxis.set_label_position('left')
'''

        # check whether we should display split axis
        if(self.parent.plotArea.splitShow):
          pythonOutput += '''
# define split resid axes system      
ax_resid_div = residplot.add_subplot(111)
'''

        # general plot formatting
        targetAxes = ['ax']
        if(self.parent.plotArea.splitShow):
          targetAxes += ['ax_div']
        if(self.parent.plotArea.isSecondAxesActive()):
          targetAxes += ['ax2']
          if(self.parent.plotArea.splitShow):
            targetAxes += ['ax2_div']

        targetAxesResid = ['ax_resid']
        if(self.parent.plotArea.splitShow):
          targetAxesResid += ['ax_resid_div']
        
        # set up axis systems dimensions
        for axis in targetAxes + targetAxesResid:
          thisAxis = self.parent.plotArea.__dict__[axis]
          if(hasattr(thisAxis, 'get_position')):
            method = getattr(thisAxis, 'get_position')
            value = repr(method())
            value = value.replace('Bbox', 'matplotlib.transforms.Bbox')
            pythonOutput += axis + '.set_position(' + value + ')\n'

        for axis in targetAxes + targetAxesResid:
          for setting in settingsAxes:
            currItem = self.parent.plotArea.__dict__[axis]
            # x label is not correctly extracted from plot as we have it on ax2
            # manually fix this issue
            if((axis == 'ax') and (setting in ['xlabel']) and (not self.parent.plotArea.isSecondAxesActive())):
              currItem = self.parent.plotArea.ax2
            if(hasattr(currItem, 'get_' + setting)):
              method = getattr(currItem, 'get_' + setting)
              value = repr(method())
              # need to weed out superfluous array declarations
              value = value.replace('array', '')
              pythonOutput += axis + '.set_' + setting + '(' + value + ')\n'
              
          # patch settings
          value = repr(self.parent.plotArea.canvasColor)
          value = value.replace('array', '')
          pythonOutput += axis + '.patch.set_facecolor(' + value + ')\n'
          
          # axes labels
          for xy in ['x', 'y']:
            currLabel = self.parent.plotArea.__dict__[axis].__dict__[xy + 'axis'].label
            # x label is not correctly extracted from plot as we have it on ax2
            # manually fix this issue
            if((axis == 'ax') and (xy == 'x')):
              currLabel = self.parent.plotArea.ax2.xaxis.label
            for setting in settingsLabels:
              if(hasattr(currLabel, 'get_' + setting)):
                method = getattr(currLabel, 'get_' + setting)
                value = repr(method())
                pythonOutput += axis + '.' + xy + 'axis.label.set_' + setting + '(' + value + ')\n'
                
            for setting in settingsAxesSub:
              currItem = self.parent.plotArea.__dict__[axis].__dict__[xy + 'axis']
              if(hasattr(currItem, 'get_' + setting)):
                method = getattr(currItem, 'get_' + setting)
                value = repr(method())
                value = value.replace('unknown', 'none')
                pythonOutput += axis + '.' + xy + 'axis.set_' + setting + '(' + value + ')\n'
              
          # spines
          for spine in self.parent.plotArea.__dict__[axis].spines:
            for setting in settingsSpines:
              if(hasattr(spine, 'get_' + setting)):
                method = getattr(spine, 'get_' + setting)
                value = repr(method())
                value = value.replace('array', '')
                pythonOutput += axis + '.spines[' + spine + '].set_' + setting + '(' + value + ')\n'

          # tick labels
          for xy in ['x', 'y']:
            pythonOutput += 'tickLabels = ' + axis + '.get_' + xy + 'ticklabels(which=\'both\')\n'
            pythonOutput += 'tickLabels.append(' + axis + '.' + xy + 'axis.get_offset_text())\n'
            pythonOutput += 'for entry in tickLabels:\n'
            for setting in settingsTicks:
              pythonOutput += '\tentry.set_' + setting + '(' + repr(self.parent.plotArea.__dict__['ticks' + xy.upper() + settingsTicks[setting]]) + ')\n'
            pythonOutput += '\tentry.set_fontname(' + repr(self.parent.plotArea.tickFont[xy]) + ')\n'
            
          # tick marks
          tWidth = {'x': self.parent.plotArea.ticksWidth['bottom'], 'y': self.parent.plotArea.ticksWidth['left']}
          tLength = {'x': self.parent.plotArea.ticksLength['bottom'], 'y': self.parent.plotArea.ticksLength['left']}
          tColor = {'x': self.parent.plotArea.ticksColor['bottom'], 'y': self.parent.plotArea.ticksColor['left']}
          tDirection = {'x': self.parent.plotArea.ticksDirection['bottom'], 'y': self.parent.plotArea.ticksDirection['left']}
          tRelative = {'x': self.parent.plotArea.ticksXMinorRelativeLength, 'y': self.parent.plotArea.ticksYMinorRelativeLength}
          for xy in ['x', 'y']:
            pythonOutput += axis + '.tick_params(axis=' + repr(xy) + ', color=' + repr(tColor[xy]) + ', width=' + repr(tWidth[xy]) + ', direction=' + repr(tDirection[xy]) + ', length=' + repr(tLength[xy]) + ', which=\'major\')\n'
            pythonOutput += axis + '.tick_params(axis=' + repr(xy) + ', color=' + repr(tColor[xy]) + ', width=' + repr(tWidth[xy]) + ', direction=' + repr(tDirection[xy]) + ', length=' + repr(tLength[xy] * tRelative[xy]) + ', which=\'minor\')\n'
            
          # grid lines
          use_x, use_y = 'x', 'y'
          if(axis.startswith('ax2')):
            use_y = 'y2'
          if(axis.endswith('div')):
            use_x = 'x2'
          
          use_xy = {'x': use_x, 'y': use_y}
          for xy in ['x', 'y']:
            pythonOutput += 'gridlines = ' + axis + '.get_' + xy + 'gridlines()\n'
            pythonOutput += 'for entry in gridlines:\n'
            for setting in settingsGridlines:
              if(hasattr(self.parent.plotArea, settingsGridlines[setting])):
                value = repr(self.parent.plotArea.__dict__[settingsGridlines[setting]][use_xy[xy]])
                pythonOutput += '\tentry.set_' + setting + '(' + value +')\n'

        # deal with inner situation
        if(self.parent.plotArea.splitShow):
          pythonOutput += '\n# deal with split axes\n'
          pythonOutput += '# axis spines\n'
          for axis in ['ax', 'ax_resid']:
            pythonOutput += axis + '.spines["right"].set_visible(' + repr(self.parent.plotArea.innerAxes and self.parent.plotArea.axisVisible['right']) + ')\n'
          for axis in ['ax_div', 'ax_resid_div']:
            pythonOutput += axis + '.spines["left"].set_visible(' + repr(self.parent.plotArea.innerAxes and self.parent.plotArea.axisVisible['left']) + ')\n'
          
          if(self.parent.plotArea.isSecondAxesActive()):
            pythonOutput += 'ax2.spines["right"].set_visible(' + repr(self.parent.plotArea.innerAxes and self.parent.plotArea.axisVisible['right']) + ')\n'
            pythonOutput += 'ax2_div.spines["left"].set_visible(' + repr(self.parent.plotArea.innerAxes and self.parent.plotArea.axisVisible['left']) + ')\n'
            
          pythonOutput += '\n# axis ticks and labels\n'
          for axis in ['ax', 'ax_resid']:
            pythonOutput += axis + '.yaxis.set_tick_params(which="both", left=' + repr(self.parent.plotArea.ticksVisible['left']) +\
              ', right=' + repr(self.parent.plotArea.innerTicks and self.parent.plotArea.ticksVisible['right']) + ')\n'
          for axis in ['ax_div', 'ax_resid_div']:
            pythonOutput += axis + '.yaxis.set_tick_params(which="both", left=' + repr(self.parent.plotArea.innerTicks and self.parent.plotArea.ticksVisible['left']) +\
              ', right=' + repr(self.parent.plotArea.ticksVisible['right']) + ')\n'

          pythonOutput += 'ax.yaxis.set_tick_params(which="both", labelleft=' + repr(self.parent.plotArea.ticksLabelShow['left'] and self.parent.plotArea.ticksYShow) + ', labelright=False)\n'
          pythonOutput += 'ax_div.yaxis.set_tick_params(which="both", labelleft=False, labelright=' + repr(self.parent.plotArea.ticksLabelShow['right'] and self.parent.plotArea.ticksYShow) + ')\n'
          pythonOutput += 'ax_resid.yaxis.set_tick_params(which="both", labelleft=' + repr(self.parent.plotArea.ticksLabelShow['left'] and self.parent.plotArea.ticksResidYShow) + ', labelright=False)\n'
          pythonOutput += 'ax_resid_div.yaxis.set_tick_params(which="both", labelleft=False, labelright=' + repr(self.parent.plotArea.ticksLabelShow['right'] and self.parent.plotArea.ticksResidYShow) + ')\n'
          
          if(self.parent.plotArea.isSecondAxesActive()):
            pythonOutput += 'ax2.yaxis.set_tick_params(which="both", left=' + repr(self.parent.plotArea.ticksVisible['left2']) +\
              ', right=' + repr(self.parent.plotArea.innerTicks and self.parent.plotArea.ticksVisible['right2']) + ')\n'
            pythonOutput += 'ax2_div.yaxis.set_tick_params(which="both", left=' + repr(self.parent.plotArea.innerTicks and self.parent.plotArea.ticksVisible['left2']) +\
              ', right=' + repr(self.parent.plotArea.ticksVisible['right2']) + ')\n'
          
            pythonOutput += 'ax2.yaxis.set_tick_params(which="both", labelleft=' + repr(self.parent.plotArea.ticksLabelShow['left2'] and self.parent.plotArea.ticksY2Show) + ', labelright=False)\n'
            pythonOutput += 'ax2_div.yaxis.set_tick_params(which="both", labelleft=False, labelright=' + repr(self.parent.plotArea.ticksLabelShow['right2'] and self.parent.plotArea.ticksY2Show) + ')\n'

        # deal with inner situation encore
        if(self.parent.plotArea.isSecondAxesActive()):
          for axis in ['ax', 'ax_div']:
            if(axis in targetAxes):
              pythonOutput += axis + '.spines["bottom"].set_visible(' + repr(self.parent.plotArea.innerAxesY and self.parent.plotArea.axisVisible['bottom']) + ')\n'
          for axis in ['ax2', 'ax2_div']:
            if(axis in targetAxes):
              pythonOutput += axis + '.spines["top"].set_visible(' + repr(self.parent.plotArea.innerAxesY and self.parent.plotArea.axisVisible['top']) + ')\n'

          for axis in ['ax', 'ax_div']:
            if(axis in targetAxes):
              pythonOutput += axis + '.xaxis.set_tick_params(which="both", top=' + repr(self.parent.plotArea.ticksVisible['top']) +\
                ', bottom=' + repr(self.parent.plotArea.innerTicksY and self.parent.plotArea.ticksVisible['bottom']) + ')\n'
              pythonOutput += axis + '.xaxis.set_tick_params(which="both", labelbottom=False)\n'
          for axis in ['ax2', 'ax2_div']:
            if(axis in targetAxes):
              pythonOutput += axis + '.xaxis.set_tick_params(which="both", top=' + repr(self.parent.plotArea.innerTicksY and self.parent.plotArea.ticksVisible['top']) +\
                ', bottom=' + repr(self.parent.plotArea.ticksVisible['bottom']) + ')\n'
        
        # determine which data items we have
        for index, data in enumerate(self.parent.data):
          if(data.visibility or data.visbilityResid):
            # awesome, we have some data that we can operate on
            pythonOutput += '\n\n##############\n'
            pythonOutput += '# data no. ' + str(index) + ' #\n'
            pythonOutput += '##############\n'

            # implement check for axis visibility
            if(data.onBothAxes):
              theseAxes = ['ax', 'ax2', 'ax_div', 'ax2_div']
            elif(data.onSecondAxes):
              theseAxes = ['ax2', 'ax2_div']
            else:
              theseAxes = ['ax', 'ax_div']
            if(not self.parent.plotArea.splitShow):
              for kill in ['ax_div', 'ax2_div']:
                if(kill in theseAxes):
                  theseAxes.remove(kill)

            # write plot data
            dataset = data.reportState()
            currName = 'data_' + str(index)
            pythonOutput += currName + ' = {}\n'
            for entry in ['x', 'y', 'yerr', 'resid']:
              if(entry in dataset):
                pythonOutput += currName + '[' + repr(entry) + '] = np.array(' + self.wrapString(repr(dataset[entry])) + ')\n'
            pythonOutput += '\n'

            if(data.visibility):
              if(('x' in dataset) and ('y' in dataset)):
                if(data.handleData != None):
                  for currAxis in theseAxes:
                    pythonOutput += 'handleData_' + str(index) + '_' + currAxis + ', = ' + currAxis + '.plot(' + currName + '["x"], ' + currName + '["y"])\n'
  
                  pythonOutput += '# formatting\n'
                  axisItems = ['handleData_' + str(index) + '_' + currAxis for currAxis in theseAxes]
                  pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                  # formatting
                  for entry in formatPlot:
                    if(hasattr(data.handleData, 'get_' + entry)):
                      if(hasattr(data.handleData, 'set_' + entry)):
                        method = getattr(data.handleData, 'get_' + entry)
                        pythonOutput += '\taxis.set_' + entry + '(' + repr(method()) + ')\n'
                  pythonOutput += '\n'
                  
                if(data.handleScatter != None):
                  for currAxis in theseAxes:
                    pythonOutput += 'handleScatter_' + str(index) + '_' + currAxis + ' = ' + currAxis + '.scatter(' + currName + '["x"], ' + currName + '["y"])\n'
  
                  pythonOutput += '# formatting\n'
                  axisItems = ['handleScatter_' + str(index) + '_' + currAxis for currAxis in theseAxes]
                  pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                  # formatting
                  for entry in formatScatter:
                    if(hasattr(data.handleScatter, 'get_' + entry)):
                      if(hasattr(data.handleScatter, 'set_' + entry)):
                        method = getattr(data.handleScatter, 'get_' + entry)
                        value = repr(method())
                        value = value.replace('array', '')
                        pythonOutput += '\taxis.set_' + entry + '(' + value + ')\n'
                  pythonOutput += '\n'
  
                if(data.handleErr != None):
                  if(len(data.handleErr[1])):
                    for currAxis in theseAxes:
                      pythonOutput += 'handleErr_' + str(index) + '_' + currAxis + ' = ' + currAxis + '.errorbar(' + currName + '["x"], ' + currName + '["y"], yerr=' + currName + '["yerr"], capsize=1)\n'
  
                    pythonOutput += '# formatting\n'
                    axisItems = ['handleErr_' + str(index) + '_' + currAxis for currAxis in theseAxes]
                    pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                    pythonOutput += '\taxis[0].set_linestyle("None")\n'
                    for entry in formatErr:
                      for item in [1, 2]:
                        if(len(data.handleErr[item])):
                          if(hasattr(data.handleErr[item][0], 'get_' + entry)):
                            if(hasattr(data.handleErr[item][0], 'set_' + entry)):
                              method = getattr(data.handleErr[item][0], 'get_' + entry)
                              value = repr(method())
                              # need to weed out superfluous array declarations
                              value = value.replace('array', '')
                              pythonOutput += '\tfor entry in axis[' + str(item) + ']:\n'
                              pythonOutput += '\t\tentry.set_' + entry + '(' + value + ')\n'
                    pythonOutput += '\n'
    
                if(data.handleBar != None):
                  if(len(data.handleBar.patches)):
                    width = data.Barstyle['width']
                    for currAxis in theseAxes:
                      pythonOutput += 'handleBar_' + str(index) + '_' + currAxis + ' = ' + currAxis + '.bar(' + currName + '["x"] + ' + str(data.Barstyle['offset']) + ', ' + currName + '["y"], align="center", width=' + str(width) + ')\n'
  
                    pythonOutput += '# formatting\n'
                    axisItems = ['handleBar_' + str(index) + '_' + currAxis for currAxis in theseAxes]
                    pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                    for entry in formatBar:
                      if(hasattr(data.handleBar[0], 'get_' + entry)):
                        if(hasattr(data.handleBar[0], 'set_' + entry)):
                          method = getattr(data.handleBar[0], 'get_' + entry)
                          value = repr(method())
                          # need to weed out superfluous array declarations
                          value = value.replace('array', '')
                          pythonOutput += '\tfor entry in axis.patches:\n'
                          pythonOutput += '\t\tentry.set_' + entry + '(' + value + ')\n'
                    pythonOutput += '\n'
    
                if(data.handleStack != None):
                  for currAxis in theseAxes:
                    pythonOutput += 'handleStack_' + str(index) + '_' + currAxis + ', = ' + currAxis + '.stackplot(' + currName + '["x"], ' + currName + '["y"])\n'
  
                  pythonOutput += '# formatting\n'
                  axisItems = ['handleStack_' + str(index) + '_' + currAxis for currAxis in theseAxes]
                  pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                  # formatting
                  for entry in formatStack:
                    if(hasattr(data.handleStack, 'get_' + entry)):
                      if(hasattr(data.handleStack, 'set_' + entry)):
                        method = getattr(data.handleStack, 'get_' + entry)
                        value = repr(method())
                        # need to weed out superfluous array declarations
                        value = value.replace('array', '')
                        pythonOutput += '\taxis.set_' + entry + '(' + value + ')\n'
                  pythonOutput += '\n'
                        
            # the residuals items
            if(data.visibilityResid):
              if(('x' in dataset) and ('resid' in dataset)):
                if(data.handleResid != None):
                  for currAxis in targetAxesResid:
                    pythonOutput += 'handleResid_' + str(index) + '_' + currAxis + ', = ' + currAxis + '.plot(' + currName + '["x"], ' + currName + '["resid"])\n'
  
                  pythonOutput += '# formatting\n'
                  axisItems = ['handleResid_' + str(index) + '_' + currAxis for currAxis in targetAxesResid]
                  pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                  # formatting
                  for entry in formatPlot:
                    if(hasattr(data.handleResid, 'get_' + entry)):
                      if(hasattr(data.handleResid, 'set_' + entry)):
                        method = getattr(data.handleResid, 'get_' + entry)
                        pythonOutput += '\taxis.set_' + entry + '(' + repr(method()) + ')\n'
                  pythonOutput += '\n'

                if(data.handleResidScatter != None):
                  for currAxis in targetAxesResid:
                    pythonOutput += 'handleResidScatter_' + str(index) + '_' + currAxis + ' = ' + currAxis + '.scatter(' + currName + '["x"], ' + currName + '["resid"])\n'
  
                  pythonOutput += '# formatting\n'
                  axisItems = ['handleResidScatter_' + str(index) + '_' + currAxis for currAxis in targetAxesResid]
                  pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                  # formatting
                  for entry in formatScatter:
                    if(hasattr(data.handleResidScatter, 'get_' + entry)):
                      if(hasattr(data.handleResidScatter, 'set_' + entry)):
                        method = getattr(data.handleResidScatter, 'get_' + entry)
                        value = repr(method())
                        value = value.replace('array', '')
                        pythonOutput += '\taxis.set_' + entry + '(' + value + ')\n'
                  pythonOutput += '\n'
  
                if(data.handleResidBar != None):
                  if(len(data.handleResidBar.patches)):
                    width = data.ResidBarstyle['width']
                    for currAxis in targetAxesResid:
                      pythonOutput += 'handleResidBar_' + str(index) + '_' + currAxis + ' = ' + currAxis + '.bar(' + currName + '["x"] + ' + str(data.Barstyle['offset']) + ', ' + currName + '["resid"], align="center", width=' + str(width) + ')\n'
  
                    pythonOutput += '# formatting\n'
                    axisItems = ['handleResidBar_' + str(index) + '_' + currAxis for currAxis in targetAxesResid]
                    pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                    for entry in formatBar:
                      if(hasattr(data.handleResidBar[0], 'get_' + entry)):
                        if(hasattr(data.handleResidBar[0], 'set_' + entry)):
                          method = getattr(data.handleResidBar[0], 'get_' + entry)
                          value = repr(method())
                          # need to weed out superfluous array declarations
                          value = value.replace('array', '')
                          pythonOutput += '\tfor entry in axis.patches:\n'
                          pythonOutput += '\t\tentry.set_' + entry + '(' + value + ')\n'
                    pythonOutput += '\n'
    
                if(data.handleResidStack != None):
                  for currAxis in targetAxesResid:
                    pythonOutput += 'handleResidStack_' + str(index) + '_' + currAxis + ', = ' + currAxis + '.stackplot(' + currName + '["x"], ' + currName + '["resid"])\n'
  
                  pythonOutput += '# formatting\n'
                  axisItems = ['handleResidStack_' + str(index) + '_' + currAxis for currAxis in targetAxesResid]
                  pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
                  # formatting
                  for entry in formatStack:
                    if(hasattr(data.handleResidStack, 'get_' + entry)):
                      if(hasattr(data.handleResidStack, 'set_' + entry)):
                        method = getattr(data.handleResidStack, 'get_' + entry)
                        value = repr(method())
                        # need to weed out superfluous array declarations
                        value = value.replace('array', '')
                        pythonOutput += '\taxis.set_' + entry + '(' + value + ')\n'
                  pythonOutput += '\n'

        # determine which fit items we have
        NUMBER_OF_POINTS = 1000
        for index, curve in enumerate(self.parent.fit):
          if(curve.visibility):
            # awesome, we have some data that we can operate on
            pythonOutput += '\n\n###############\n'
            pythonOutput += '# curve no. ' + str(index) + ' #\n'
            pythonOutput += '###############\n'

            # write function
            curveData = curve.reportState()
            param = curveData['paramAll']
            pythonOutput += '# define function\n'
            pythonOutput += 'def ffunc_' + str(index) + '(x, ' + ', '.join(curveData['paramNames']) + '):\n'
            pythonOutput += '\t' + '\n\t'.join(curveData['ffuncstr_base'].split('\n'))
            pythonOutput += '\n\treturn y\n\n'
            pythonOutput += 'param_' + str(index) + ' = ' + repr(param) + '\n'

            # implement check for axis visibility
            if(curve.onBothAxes):
              theseAxes = ['ax', 'ax2', 'ax_div', 'ax2_div']
            elif(data.onSecondAxes):
              theseAxes = ['ax2', 'ax2_div']
            else:
              theseAxes = ['ax', 'ax_div']
            if(not self.parent.plotArea.splitShow):
              for kill in ['ax_div', 'ax2_div']:
                if(kill in theseAxes):
                  theseAxes.remove(kill)

            # plot curve
            for currAxis in theseAxes:
              if(currAxis in ['ax', 'ax2']):
                minX, maxX = self.parent.plotArea.minX, self.parent.plotArea.maxX
                modeX = self.parent.plotArea.modeX
              else:
                minX, maxX = self.parent.plotArea.minX_div, self.parent.plotArea.maxX_div
                modeX = self.parent.plotArea.modeX_div
                
              if(modeX == 'linear'):
                pythonOutput += 'xv = np.linspace(' + str(minX) + ', ' + str(maxX) + ', ' + str(NUMBER_OF_POINTS) + ')\n'
              else:
                pythonOutput += 'xv = np.linspace(' + str(np.log(minX)) + ', ' + str(np.log(maxX)) + ', ' + str(NUMBER_OF_POINTS) + ')\n'
                pythonOutput += 'xv = np.exp(xv)\n'

              # plot curve
              pythonOutput += 'handlePlot_' + str(index) + '_' + currAxis + ', = ' + currAxis + '.plot(xv, ffunc_' + str(index) + '(xv'
              if(len(param)):
                pythonOutput += ', *param_' + str(index) + '))\n'
              else:
                pythonOutput += '))\n'

            # formatting
            pythonOutput += '# formatting\n'
            axisItems = ['handlePlot_' + str(index) + '_' + currAxis for currAxis in theseAxes]
            pythonOutput += 'for axis in [' + ', '.join(axisItems) + ']:\n'
            for entry in formatPlot:
              if(hasattr(curve.handlePlot, 'get_' + entry)):
                if(hasattr(curve.handlePlot, 'set_' + entry)):
                  method = getattr(curve.handlePlot, 'get_' + entry)
                  pythonOutput += '\taxis.set_' + entry + '(' + repr(method()) + ')\n'

        # apply plot settings
        pythonOutput += '\n\n###########################\n'
        pythonOutput += '# general plot formatting #\n'
        pythonOutput += '###########################\n'
  
        # set window size
        targetwidth = float(self.exportSizeX.text())
        targetheight = float(self.exportSizeY.text())
        pythonOutput += 'matplot.set_size_inches('+ repr(targetwidth) + ', ' + repr(targetheight) +')\n'
        pythonOutput += 'residplot.set_size_inches('+ repr(targetwidth) + ', ' + repr(targetheight / 4.0) +')\n'

        for plot in ['matplot', 'residplot']:
          for entry in formatCanvas:
            if(hasattr(self.parent.plotArea.__dict__[plot], 'get_' + entry)):
              if(hasattr(self.parent.plotArea.__dict__[plot], 'set_' + entry)):
                method = getattr(self.parent.plotArea.__dict__[plot], 'get_' + entry)
                pythonOutput += plot + '.set_' + entry + '(' + repr(method()) + ')\n'
          if(hasattr(self.parent.plotArea.__dict__[plot], 'patch')):
            for entry in formatCanvasPatch:
              if(hasattr(self.parent.plotArea.__dict__[plot].patch, 'get_' + entry)):
                if(hasattr(self.parent.plotArea.__dict__[plot].patch, 'set_' + entry)):
                  method = getattr(self.parent.plotArea.__dict__[plot].patch, 'get_' + entry)
                  pythonOutput += plot + '.patch.set_' + entry + '(' + repr(method()) + ')\n'
  
        # plot the data and show
        pythonOutput += '\n\n##########################\n'
        pythonOutput += '# draw and display plots #\n'
        pythonOutput += '##########################\n'
        pythonOutput += 'plt.draw()\nplt.show()\n'
        
        # save output to file
        try:
          with open(filename, 'w', encoding='utf-8') as writehandle:
            writehandle.write(pythonOutput)
          # save filename for future reference
          self.currExportFile = filename
        except:
          self.parent.statusbar.showMessage('Error writing Python output file ' + filename, self.parent.STATUS_TIME)
  
  def wrapString(self, source, limit=80, breakat=',', delimiter='\n\t'):
    # wraps long string into several lines
    outstring = ''
    
    # test whether input is a string or numpy array or list
    if(type(source) in [list, np.ndarray]):
      source = '[' + ', '.join([str(i) for i in source]) + ']'
      
    while(len(source)):
      if((len(source) <= limit) or (source[limit:].find(breakat) == -1)):
        outstring += source
        source = ''
      else:
        breakposition = limit + source[limit:].find(breakat)
        outstring += source[:breakposition + 1] + delimiter
        source = source[breakposition + 1:]

    return outstring

class myQMessageBox(QtWidgets.QMessageBox):
  def __init__(self, argument=None, parent=None):
    super(myQMessageBox, self).__init__(argument)
    self.parent = parent
    # add checkbox
    self.discardCheckBox = QCheckBoxToolTip()
    self.discardCheckBox.setText('Do not ask again')
    self.setCheckBox(self.discardCheckBox)

  def exec(self, *args, **kwargs):
    # override exec to return additional argument
    return QtWidgets.QMessageBox.exec(self, *args, **kwargs), not self.discardCheckBox.isChecked()

class OptionMenu(KuhMenu):
  def __init__(self, parent = None):
    super(OptionMenu, self).__init__()
    self.parent = parent
      
    # float validator
    self.validFloat = MyValidFloat()
    self.validInt = MyValidInt()

    # set up GUI
    self.buildRessource()

  def mousePressEvent(self, event):
    # check whether click event is within widget
    if(not self.underMouse()):
      # pass through event (and thereby close the QMenu)
      KuhMenu.mousePressEvent(self, event)

  def buildRessource(self):
    # build gui for setting options
    self.hLayout0 = QtWidgets.QHBoxLayout(self)
    self.hLayout0.setContentsMargins(*[0]*4)
    self.hLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    
    self.exportOptionsGroup = QWidgetMac()
    self.hLayout0.addWidget(self.exportOptionsGroup)    
    self.vLayout = QtWidgets.QVBoxLayout(self.exportOptionsGroup)
    self.vLayout.setContentsMargins(*[0]*4)
    self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout.setSpacing(2)
    
    # heading
    self.exportSettingLabel = QtWidgets.QLabel()
    useFont = self.exportSettingLabel.font()
    useFont.setBold(True)
    self.exportSettingLabel.setFont(useFont)
    self.exportSettingLabel.setText('Export options')
    self.vLayout.addWidget(self.exportSettingLabel)    
    
    # export options
    # plot resid?
    self.exportResidGroup = QWidgetMac()
    self.vLayout.addWidget(self.exportResidGroup)
    self.hLayout = QtWidgets.QHBoxLayout(self.exportResidGroup)
    self.hLayout.setContentsMargins(0, 0, 0, 0)
    self.hLayout.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.exportResidLabel = QtWidgets.QLabel('export resid')
    self.exportResidLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.exportResidLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout.addWidget(self.exportResidLabel)
    self.exportResidCheck = QPushButtonCheckable()
    self.exportResidCheck.setCheckMe(True)
    self.exportResidCheck.setChecked(self.parent.graphicsarea.advancedExport['plotResid'])
    self.exportResidCheck.setToolTip('Create residuals plot (in addition to data plot) when exporting graphics')
    self.exportResidCheck.clicked.connect(partial(self.toggleOption, self.exportResidCheck, 'plotResid'))
    self.exportResidCheck.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.exportResidCheck.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.hLayout.addWidget(self.exportResidCheck)

    # plot DPI
    self.exportDPIGroup = QWidgetMac()
    self.vLayout.addWidget(self.exportDPIGroup)
    self.hLayout2 = QtWidgets.QHBoxLayout(self.exportDPIGroup)
    self.hLayout2.setContentsMargins(0, 0, 0, 0)
    self.hLayout2.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.exportDPILabel = QtWidgets.QLabel('export DPI')
    self.exportDPILabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.exportDPILabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout2.addWidget(self.exportDPILabel)

    self.exportDPIEntry = QLineEditClick()
    self.exportDPIEntry.setToolTip('Resolution in pixels per inch of bitmapped graphics')
    self.exportDPIEntry.setText(str(self.parent.graphicsarea.advancedExport['plotDPI']))
    self.exportDPIEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.exportDPIEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.exportDPIEntry.editingFinished.connect(partial(self.changeOption, self.exportDPIEntry, 'plotDPI', 60, 1200))
    self.exportDPIEntry.setValidator(self.validFloat)
    self.hLayout2.addWidget(self.exportDPIEntry)

    # SVG txt2path
    self.svgPathGroup = QWidgetMac()
    self.vLayout.addWidget(self.svgPathGroup)
    self.hLayout3 = QtWidgets.QHBoxLayout(self.svgPathGroup)
    self.hLayout3.setContentsMargins(0, 0, 0, 0)
    self.hLayout3.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.svgPathLabel = QtWidgets.QLabel('Text2Path')
    self.svgPathLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.svgPathLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout3.addWidget(self.svgPathLabel)

    self.svgPathCheck = QPushButtonCheckable(self.svgPathGroup)
    self.svgPathCheck.setToolTip('Convert text to curves in vector graphics')
    self.svgPathCheck.setCheckMe(True)
    self.svgPathCheck.setChecked(self.parent.graphicsarea.advancedExport['SVGtxt2path'])
    self.svgPathCheck.clicked.connect(partial(self.toggleOption, self.svgPathCheck, 'SVGtxt2path'))
    self.svgPathCheck.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.svgPathCheck.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.hLayout3.addWidget(self.svgPathCheck)
    
    # heading
    blah = HLine()
    self.vLayout.addWidget(blah)
    self.graphicsSettingLabel = QtWidgets.QLabel()
    useFont = self.graphicsSettingLabel.font()
    useFont.setBold(True)
    self.graphicsSettingLabel.setFont(useFont)
    self.graphicsSettingLabel.setText('Graphics options')
    self.vLayout.addWidget(self.graphicsSettingLabel)    

    # show advanced options
    self.advancedGraphicsGroup = QWidgetMac()
    self.vLayout.addWidget(self.advancedGraphicsGroup)
    self.hLayout35 = QtWidgets.QHBoxLayout(self.advancedGraphicsGroup)
    self.hLayout35.setContentsMargins(0, 0, 0, 0)
    self.hLayout35.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout35.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.advancedGraphicsLabel = QtWidgets.QLabel('advanced')
    self.advancedGraphicsLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.advancedGraphicsLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout35.addWidget(self.advancedGraphicsLabel)

    self.advancedGraphicsCheck = QPushButtonCheckable(self.advancedGraphicsGroup)
    self.advancedGraphicsCheck.setCheckMe(True)
    self.advancedGraphicsCheck.setChecked(self.parent.graphicsarea.advancedExport['advancedGraphics'])
    self.advancedGraphicsCheck.setToolTip('Display advanced graphics options')
    self.advancedGraphicsCheck.clicked.connect(self.toggleGraphicsOption)
    self.advancedGraphicsCheck.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.advancedGraphicsCheck.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.hLayout35.addWidget(self.advancedGraphicsCheck)

    # show advanced options
    self.unicodeHyphenGroup = QWidgetMac()
    self.vLayout.addWidget(self.unicodeHyphenGroup)
    self.hLayout36 = QtWidgets.QHBoxLayout(self.unicodeHyphenGroup)
    self.hLayout36.setContentsMargins(0, 0, 0, 0)
    self.hLayout36.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout36.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.unicodeHyphenLabel = QtWidgets.QLabel('unicode hyph.')
    self.unicodeHyphenLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.unicodeHyphenLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout36.addWidget(self.unicodeHyphenLabel)

    self.unicodeHyphenCheck = QPushButtonCheckable(self.unicodeHyphenGroup)
    self.unicodeHyphenCheck.setCheckMe(True)
    self.unicodeHyphenCheck.setChecked(self.parent.graphicsarea.advancedExport['unicodeHyphen'])
    self.unicodeHyphenCheck.setToolTip('Use unicode character for minus sign?\n(Uncheck this option if your font does not display minus signs correctly.)')
    self.unicodeHyphenCheck.clicked.connect(self.toggleUnicodeHyphen)
    self.unicodeHyphenCheck.setMaximumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.unicodeHyphenCheck.setMinimumSize(QtCore.QSize(int(BASE_SIZE - 2), int(BASE_SIZE - 2)))
    self.hLayout36.addWidget(self.unicodeHyphenCheck)

    # heading
    blah = HLine()
    self.vLayout.addWidget(blah)
    self.stateFileSettingLabel = QtWidgets.QLabel()
    useFont = self.stateFileSettingLabel.font()
    useFont.setBold(True)
    self.stateFileSettingLabel.setFont(useFont)
    self.stateFileSettingLabel.setText('Save state files')
    self.vLayout.addWidget(self.stateFileSettingLabel)    

    self.stateFileFormatGroup = QWidgetMac()
    self.vLayout.addWidget(self.stateFileFormatGroup)
    self.hLayout31 = QtWidgets.QHBoxLayout(self.stateFileFormatGroup)
    self.hLayout31.setContentsMargins(0, 0, 0, 0)
    self.hLayout31.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout31.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.stateFileFormatLabel = QtWidgets.QLabel('format')
    self.stateFileFormatLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.stateFileFormatLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout31.addWidget(self.stateFileFormatLabel)

    self.stateFileFormat = QComboBoxMac()
    self.stateFileFormat.setToolTip('Output format of state files')
    self.availableStateFileFormats = ['state', 'statez', 'both']
    self.stateFileFormat.addItems(self.availableStateFileFormats)
    self.stateFileFormat.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.stateFileFormat.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout31.addWidget(self.stateFileFormat)
    if(self.parent.graphicsarea.advancedExport['stateFileFormat'] in self.availableStateFileFormats):
      currindex = self.availableStateFileFormats.index(self.parent.graphicsarea.advancedExport['stateFileFormat'])
      self.stateFileFormat.setCurrentIndex(currindex)
    else:
      self.stateFileFormat.setCurrentIndex(0)
    self.stateFileFormat.activated.connect(self.changeStateFileFormat)
    
    # minimization options
    blah = VLine()
    self.hLayout0.addWidget(blah)

    self.minOptionsGroup = QWidgetMac()
    self.hLayout0.addWidget(self.minOptionsGroup)    
    self.vLayout2 = QtWidgets.QVBoxLayout(self.minOptionsGroup)
    self.vLayout2.setContentsMargins(*[0]*4)
    self.vLayout2.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout2.setSpacing(2)
    
    # heading
    self.minSettingLabel = QtWidgets.QLabel()
    useFont = self.minSettingLabel.font()
    useFont.setBold(True)
    self.minSettingLabel.setFont(useFont)
    self.minSettingLabel.setText('Minimization options')
    self.vLayout2.addWidget(self.minSettingLabel)    
    
    # which algorithm?
    self.minAlgorithmGroup = QWidgetMac()
    self.vLayout2.addWidget(self.minAlgorithmGroup)
    self.hLayout4 = QtWidgets.QHBoxLayout(self.minAlgorithmGroup)
    self.hLayout4.setContentsMargins(0, 0, 0, 0)
    self.hLayout4.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout4.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.minAlgorithmLabel = QtWidgets.QLabel('algorithm')
    self.minAlgorithmLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.minAlgorithmLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.minAlgorithmLabel)

    self.minAlgorithm = QComboBoxMac()
    self.minAlgorithm.setToolTip('Algorithm used for numerical minimization')
    self.availableMinAlgorithms = ['Nelder-Mead', 'Powell', 'CG', 'BFGS']
    self.minAlgorithm.addItems(self.availableMinAlgorithms)
    self.minAlgorithm.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.minAlgorithm.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout4.addWidget(self.minAlgorithm)
    if(self.parent.fitarea.advancedMinimization['algorithm'] in self.availableMinAlgorithms):
      currindex = self.availableMinAlgorithms.index(self.parent.fitarea.advancedMinimization['algorithm'])
      self.minAlgorithm.setCurrentIndex(currindex)
    else:
      self.minAlgorithm.setCurrentIndex(0)
    self.minAlgorithm.activated.connect(self.changeMinAlgorithm)
  
    # which tolerance
    self.minToleranceGroup = QWidgetMac()
    self.vLayout2.addWidget(self.minToleranceGroup)
    self.hLayout5 = QtWidgets.QHBoxLayout(self.minToleranceGroup)
    self.hLayout5.setContentsMargins(0, 0, 0, 0)
    self.hLayout5.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout5.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.minToleranceLabel = QtWidgets.QLabel('tolerance')
    self.minToleranceLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.minToleranceLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout5.addWidget(self.minToleranceLabel)

    self.minToleranceEntry = QLineEditClick()
    self.minToleranceEntry.setToolTip('Target tolerance in numerical minimization')
    self.minToleranceEntry.setText(str(self.parent.fitarea.advancedMinimization['xtol']))
    self.minToleranceEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.minToleranceEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.minToleranceEntry.editingFinished.connect(partial(self.changeOptionMin, self.minToleranceEntry, 'xtol', 0, 1000))
    self.minToleranceEntry.setValidator(self.validFloat)
    self.hLayout5.addWidget(self.minToleranceEntry)

    # max fev
    self.minFevGroup = QWidgetMac()
    self.vLayout2.addWidget(self.minFevGroup)
    self.hLayout6 = QtWidgets.QHBoxLayout(self.minFevGroup)
    self.hLayout6.setContentsMargins(0, 0, 0, 0)
    self.hLayout6.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout6.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.minFevLabel = QtWidgets.QLabel('max. fev')
    self.minFevLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.minFevLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout6.addWidget(self.minFevLabel)

    self.minFevEntry = QLineEditClick()
    self.minFevEntry.setToolTip('Maximum allowed function evaluations in numerical minimization')
    self.minFevEntry.setText(str(int(self.parent.fitarea.advancedMinimization['maxfev'])))
    self.minFevEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.minFevEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.minFevEntry.editingFinished.connect(partial(self.changeOptionMinInt, self.minFevEntry, 'maxfev', 1, 1e6))
    self.minFevEntry.setValidator(self.validInt)
    self.hLayout6.addWidget(self.minFevEntry)

    # heading
    blah = HLine()
    self.vLayout2.addWidget(blah)
    self.randomSettingLabel = QtWidgets.QLabel()
    useFont = self.randomSettingLabel.font()
    useFont.setBold(True)
    self.randomSettingLabel.setFont(useFont)
    self.randomSettingLabel.setText('Random search options')
    self.vLayout2.addWidget(self.randomSettingLabel)    
    
    # number cycles
    self.randomCyclesGroup = QWidgetMac()
    self.vLayout2.addWidget(self.randomCyclesGroup)
    self.hLayout7 = QtWidgets.QHBoxLayout(self.randomCyclesGroup)
    self.hLayout7.setContentsMargins(0, 0, 0, 0)
    self.hLayout7.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout7.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.randomCyclesLabel = QtWidgets.QLabel('#cycles')
    self.randomCyclesLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.randomCyclesLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout7.addWidget(self.randomCyclesLabel)

    self.randomCyclesEntry = QLineEditClick()
    self.randomCyclesEntry.setToolTip('Number of search cycles in random parameter optimization')
    self.randomCyclesEntry.setText(str(int(self.parent.fitarea.advancedRandomSearch['cycles'])))
    self.randomCyclesEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.randomCyclesEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.randomCyclesEntry.editingFinished.connect(partial(self.changeOptionRandomInt, self.randomCyclesEntry, 'cycles', 1, 20))
    self.randomCyclesEntry.setValidator(self.validInt)
    self.hLayout7.addWidget(self.randomCyclesEntry)

    # escalate
    self.randomEscalateGroup = QWidgetMac()
    self.vLayout2.addWidget(self.randomEscalateGroup)
    self.hLayout8 = QtWidgets.QHBoxLayout(self.randomEscalateGroup)
    self.hLayout8.setContentsMargins(0, 0, 0, 0)
    self.hLayout8.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout8.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.randomEscalateLabel = QtWidgets.QLabel('escalate')
    self.randomEscalateLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.randomEscalateLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout8.addWidget(self.randomEscalateLabel)

    self.randomEscalateEntry = QLineEditClick()
    self.randomEscalateEntry.setToolTip('Search amplitude increase during random parameter optimization')
    self.randomEscalateEntry.setText(str(self.parent.fitarea.advancedRandomSearch['escalate']))
    self.randomEscalateEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.randomEscalateEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.randomEscalateEntry.editingFinished.connect(partial(self.changeOptionRandom, self.randomEscalateEntry, 'escalate', 0, 10))
    self.randomEscalateEntry.setValidator(self.validFloat)
    self.hLayout8.addWidget(self.randomEscalateEntry)

    # max fev
    self.randomFevGroup = QWidgetMac()
    self.vLayout2.addWidget(self.randomFevGroup)
    self.hLayout9 = QtWidgets.QHBoxLayout(self.randomFevGroup)
    self.hLayout9.setContentsMargins(0, 0, 0, 0)
    self.hLayout9.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout9.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.randomFevLabel = QtWidgets.QLabel('max. fev')
    self.randomFevLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.randomFevLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout9.addWidget(self.randomFevLabel)

    self.randomFevEntry = QLineEditClick()
    self.randomFevEntry.setToolTip('Maximum allowed function evaluations per cylce in random optimization')
    self.randomFevEntry.setText(str(int(self.parent.fitarea.advancedRandomSearch['maxfev'])))
    self.randomFevEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.randomFevEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.randomFevEntry.editingFinished.connect(partial(self.changeOptionRandomInt, self.randomFevEntry, 'maxfev', 1, 1e6))
    self.randomFevEntry.setValidator(self.validInt)
    self.hLayout9.addWidget(self.randomFevEntry)

    # color scheme options
    blah = VLine()
    self.hLayout0.addWidget(blah)

    self.colorOptionsGroup = QWidgetMac()
    self.hLayout0.addWidget(self.colorOptionsGroup)
    self.vLayout3 = QtWidgets.QVBoxLayout(self.colorOptionsGroup)
    self.vLayout3.setContentsMargins(*[0]*4)
    self.vLayout3.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout3.setSpacing(2)
    
    # heading
    self.colorSettingLabel = QtWidgets.QLabel()
    useFont = self.colorSettingLabel.font()
    useFont.setBold(True)
    self.colorSettingLabel.setFont(useFont)
    self.colorSettingLabel.setText('Color scheme')
    self.vLayout3.addWidget(self.colorSettingLabel)
    
    # which scheme?
    self.colorSchemeGroup = QWidgetMac()
    self.vLayout3.addWidget(self.colorSchemeGroup)
    self.hLayout31 = QtWidgets.QHBoxLayout(self.colorSchemeGroup)
    self.hLayout31.setContentsMargins(0, 0, 0, 0)
    self.hLayout31.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout31.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.colorSchemeLabel = QtWidgets.QLabel('scheme')
    self.colorSchemeLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.colorSchemeLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout31.addWidget(self.colorSchemeLabel)

    self.colorScheme = QComboBoxMac()
    self.colorScheme.setToolTip('Color scheme of Fit-o-mat')
    self.availableColorSchemes = ['default', 'dark', 'light', 'sad']###, 'tranquil']
    self.colorScheme.addItems(self.availableColorSchemes)
    self.colorScheme.setMaximumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.colorScheme.setMinimumSize(QtCore.QSize(int(60 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout31.addWidget(self.colorScheme)
    if(self.parent.plotArea.themeUI in self.availableColorSchemes):
      currindex = self.availableColorSchemes.index(self.parent.plotArea.themeUI)
      self.colorScheme.setCurrentIndex(currindex)
    else:
      self.colorScheme.setCurrentIndex(0)
    self.colorScheme.activated.connect(self.changeColorScheme)

    # heading
    blah = HLine()
    self.vLayout3.addWidget(blah)
    self.bootstrapSettingLabel = QtWidgets.QLabel()
    useFont = self.bootstrapSettingLabel.font()
    useFont.setBold(True)
    self.bootstrapSettingLabel.setFont(useFont)
    self.bootstrapSettingLabel.setText('Bootstrap options')
    self.vLayout3.addWidget(self.bootstrapSettingLabel)    

    # bootstrap evals
    self.bootstrapEvalGroup = QWidgetMac()
    self.vLayout3.addWidget(self.bootstrapEvalGroup)
    self.hLayout32 = QtWidgets.QHBoxLayout(self.bootstrapEvalGroup)
    self.hLayout32.setContentsMargins(0, 0, 0, 0)
    self.hLayout32.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout32.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.bootstrapEvalLabel = QtWidgets.QLabel('trials')
    self.bootstrapEvalLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.bootstrapEvalLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout32.addWidget(self.bootstrapEvalLabel)

    self.bootstrapEvalEntry = QLineEditClick()
    self.bootstrapEvalEntry.setToolTip('Number of trials to determine bootstrap confidence band')
    self.bootstrapEvalEntry.setText(str(int(self.parent.fitarea.advancedBootstrap['trials'])))
    self.bootstrapEvalEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.bootstrapEvalEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.bootstrapEvalEntry.editingFinished.connect(partial(self.changeOptionBootstrapInt, self.bootstrapEvalEntry, 'trials', 100, 2e3))
    self.bootstrapEvalEntry.setValidator(self.validInt)
    self.hLayout32.addWidget(self.bootstrapEvalEntry)

    # heading
    blah = HLine()
    self.vLayout3.addWidget(blah)
    self.interpolateSettingLabel = QtWidgets.QLabel()
    useFont = self.interpolateSettingLabel.font()
    useFont.setBold(True)
    self.interpolateSettingLabel.setFont(useFont)
    self.interpolateSettingLabel.setText('Interpolate options')
    self.vLayout3.addWidget(self.interpolateSettingLabel)    

    # bootstrap evals
    self.interpolateGroup = QWidgetMac()
    self.vLayout3.addWidget(self.interpolateGroup)
    self.hLayout33 = QtWidgets.QHBoxLayout(self.interpolateGroup)
    self.hLayout33.setContentsMargins(0, 0, 0, 0)
    self.hLayout33.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout33.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.interpolateLabel = QtWidgets.QLabel('points')
    self.interpolateLabel.setMaximumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.interpolateLabel.setMinimumSize(QtCore.QSize(int(64 * SCALEFONT), int(BASE_SIZE)))
    self.hLayout33.addWidget(self.interpolateLabel)

    self.interpolateEntry = QLineEditClick()
    self.interpolateEntry.setToolTip('Number of points for data interpolation')
    self.interpolateEntry.setText(str(int(self.parent.objectsarea.advancedInterpolate['points'])))
    self.interpolateEntry.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.interpolateEntry.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.interpolateEntry.editingFinished.connect(partial(self.changeOptionInterpolateInt, self.interpolateEntry, 'points', 10, 1e4))
    self.interpolateEntry.setValidator(self.validInt)
    self.hLayout33.addWidget(self.interpolateEntry)

    # heading
    blah = HLine()
    self.vLayout3.addWidget(blah)
    self.resetLabel = QtWidgets.QLabel()
    useFont = self.resetLabel.font()
    useFont.setBold(True)
    self.resetLabel.setFont(useFont)
    self.resetLabel.setText('Reset Fit-o-mat')
    self.vLayout3.addWidget(self.resetLabel)    

    self.resetGroup = QWidgetMac()
    self.vLayout3.addWidget(self.resetGroup)
    self.hLayout34 = QtWidgets.QHBoxLayout(self.resetGroup)
    self.hLayout34.setContentsMargins(0, 0, 0, 0)
    self.hLayout34.setSpacing(int(-2 + 8 * SCALEFONT))
    self.hLayout34.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft)
    self.resetButton = QPushButtonMac()
    self.resetButton.setText('Reset')
    self.resetButton.setToolTip('Reset Fit-o-mat to initial program state\n(Caution -- all current data will be lost.)')
    self.resetButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.resetButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.resetButton.clicked.connect(self.resetAll)
    self.hLayout34.addWidget(self.resetButton)

    self.setFocus()    
    self.focusNextChild()

  def resetAll(self):
    # reinitializes fit-o-mat
    msgBox = QtWidgets.QMessageBox()
    msgBox.setWindowTitle('Fit-o-mat')
    msgBox.setText('Reset Fit-o-mat?\n(All data will be lost!)')
    msgBox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)
    msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Yes)
    msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
    # apply styles and show
    if(QSTYLE != None):
      msgBox.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      msgBox.setStyleSheet(QSTYLESHEET)
    reply = msgBox.exec()
    if (reply == QtWidgets.QMessageBox.StandardButton.Yes):
      # display busy pointer
      QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
      QtCore.QCoreApplication.processEvents()
      
      # disable splits and flips
      if(self.parent.plotArea.flipAxisButtonX.isChecked()):
        self.parent.plotArea.flipAxisButtonX.setChecked(False)
        self.parent.plotArea.flipAxis(axis='x', redraw=False)
      if(self.parent.plotArea.flipAxisButtonX2.isChecked()):
        self.parent.plotArea.flipAxisButtonX2.setChecked(False)
        self.parent.plotArea.flipAxis(axis='x2', redraw=False)
      if(self.parent.plotArea.flipAxisButtonY.isChecked()):
        self.parent.plotArea.flipAxisButtonY.setChecked(False)
        self.parent.plotArea.flipAxis(axis='y', redraw=False)
      if(self.parent.plotArea.flipAxisButtonY2.isChecked()):
        self.parent.plotArea.flipAxisButtonY2.setChecked(False)
        self.parent.plotArea.flipAxis(axis='y2', redraw=False)
  
      if(self.parent.plotArea.splitAxisButton.isChecked()):
        self.parent.plotArea.splitAxisButton.setChecked(False)
        self.parent.plotArea.toggleSplit(redraw=False)
      if(self.parent.plotArea.splitYButton.isChecked()):
        self.parent.plotArea.splitYButton.setChecked(False)
        self.parent.plotArea.setSplitY(value=False, redraw=False)
        
      # restore axis labels to defaults
      self.parent.graphicsarea.configXName.setText('x')
      self.parent.graphicsarea.configYName.setText('y')
      for axis in ['x', 'y']:
        self.parent.plotArea.setAxisLabel(labeltext=axis, axis=axis, redraw=False, target='plot')
        self.parent.plotArea.setAxisLabel(labeltext=axis, axis=axis, redraw=False, target='resid')
      if(hasattr(self.parent, 'graphicsarea2')):
        self.parent.graphicsarea.configYName.setText('y2')
        self.parent.plotArea.setAxisLabel(labeltext='y2', axis='y2', redraw=False, target='plot')
      
      # kill all extras
      numberExtras = len(self.parent.extras)
      for index in range(numberExtras)[::-1]:
        self.parent.objectsarea.deleteExtra(index, redraw=False)
  
      # kill all datasets but one
      # first add empty data set (this is the one that will remain in the end)
      self.parent.objectsarea.dataSetCreate()
      self.parent.data[-1].setName('Data_1')
      self.parent.data[-1].setNameResid('Resid_1')
      numberData = len(self.parent.data) - 1
      for index in range(numberData)[::-1]:
        self.parent.objectsarea.deleteDataSet(index, redraw=False)
  
      # kill all curves but one
      numberFit = len(self.parent.fit) - 1
      for index in range(numberFit)[::-1]:
        self.parent.objectsarea.deleteCurve(index, redraw=False)
      # initalize curve to binding isotherm again
      self.parent.fitarea.declareParamEntry.setText('A0, A1, KD')
      self.parent.fitarea.fitFormula.setText('# a binding isotherm\ny = A0 + A1 * x / (x + KD)')
      # need to make all parameters active lest error is thrown when simulating the function with the default parameters
      self.parent.fitarea.param_active = [1, 1, 1]
      self.parent.fitarea.useFit(redraw=False)
      self.parent.fit[-1].updateParam(param=[1, 1, 1])
      self.parent.fit[-1].setName('Curve_1')
      self.parent.fit[-1].restrictMeLow, self.parent.fit[-1].restrictMeHigh = False, False
      self.parent.fit[-1].restrictLow, self.parent.fit[-1].restrictHigh = 0.0, 1.0
      # need to update param table as well
      for index in range(3):
        self.parent.fitarea.ParamTable.cellWidget(index, 1).setText(self.parent.formatNumber(1))
      
      # make data set and curve visible
      if(not self.parent.fit[-1].visibility):
        self.parent.fit[-1].setVisibility(True, redraw=False)
        self.parent.objectsarea.curvesTable.cellWidget(0, 0).setChecked(True)
      if(not self.parent.data[-1].visibility):
        self.parent.data[-1].setVisibility(True, redraw=False)
        self.parent.objectsarea.dataSetTable.cellWidget(0, 0).setChecked(True)
      
      # set default interval and zoom to contents
      self.parent.plotArea.setAxisLimits(lower=0, upper=1, axis='x', target='plot', redraw=False)
      self.parent.fit[-1].drawMe(redraw=False, rescale=False)
      self.parent.plotArea.doAutoScale(axis='y', redraw=False)
      
      # update togglo container
      self.parent.plotArea.updateToggloContainer()
  
      # revert to default styles
      defaultStyle = 'default.style'
      try:
        with open(WORKINGDIR + PATH_SEPARATOR + 'styles' + PATH_SEPARATOR + defaultStyle, 'r', encoding='utf-8') as loadhandle:
          red = loadhandle.readlines()
        red = ''.join(red)
  
        # apply style sheet
        self.parent.graphicsarea.processStyleSet(operation='load', modus=red, zoffsetData=0, zoffsetCurve=0, redraw=False)
      except:
        pass
      
      # check autoscaling boxes
      self.parent.plotArea.autoScaleCheckX.setChecked(True)
      self.parent.plotArea.autoScaleCheckY.setChecked(True)
      
      # issue plot and ui refreshes
      self.parent.plotArea.dataplotwidget.myRefresh()
      self.parent.plotArea.residplotwidget.myRefresh()
      
      # need to clear data table
      self.parent.dataarea.tableWidget.generateEmptyTable(3, 50)
      
      # turn off advanced graphics settings as required
      if(self.advancedGraphicsCheck.isChecked()):
        self.advancedGraphicsCheck.setChecked(False)
        self.toggleGraphicsOption()
  
      # restore cursor
      QtWidgets.QApplication.restoreOverrideCursor()
      QtCore.QCoreApplication.processEvents()

  def changeColorScheme(self):
    # controls change of Fit-o-mat color scheme
    useScheme = str(self.colorScheme.currentText())
    self.parent.plotArea.themeUI = useScheme
    setColorScheme(value=useScheme, caller=self)

  def toggleUnicodeHyphen(self):
    # toggles usage of unicode hyphen
    value = self.unicodeHyphenCheck.isChecked()
    self.parent.graphicsarea.setAdvancedExport(key='unicodeHyphen', value=value)
    
    # change the actual setting
    matplotlib.rcParams['axes.unicode_minus'] = value
    
    # we should better issue redraws to ensure that hyphens are updated right away
    self.parent.plotArea.dataplotwidget.myRefresh()
    self.parent.plotArea.residplotwidget.myRefresh()
    
  def toggleGraphicsOption(self):
    # toggles display of advanced graphics
    value = self.advancedGraphicsCheck.isChecked()
    self.parent.graphicsarea.configAdvancedSettingsButton.setChecked(value)
    self.parent.graphicsarea.setAdvancedGraphics(value=value)
    self.parent.graphicsarea2.setAdvancedGraphics(value=value)

  def toggleOption(self, entryfield=None, key=None):
    # toggles option of target
    if((entryfield != None) and (key != None)):
      value = entryfield.isChecked()
      self.parent.graphicsarea.setAdvancedExport(key=key, value=value)

  def changeOption(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      self.parent.graphicsarea.setAdvancedExport(key=key, value=value)

  def changeOptionMin(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      self.parent.fitarea.setAdvancedMinimization(key=key, value=value)
      self.parent.globalarea.setAdvancedMinimization(key=key, value=value)

  def changeOptionMinInt(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = int(entryfield.text())
        originalvalue = value
      except:
        value = 0
        originalvalue = 1
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(int(value)))
      self.parent.fitarea.setAdvancedMinimization(key=key, value=value)
      self.parent.globalarea.setAdvancedMinimization(key=key, value=value)
      
  def changeOptionRandom(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = float(entryfield.text())
        originalvalue = value
      except:
        value = 0.0
        originalvalue = 1.0
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(value))
      self.parent.fitarea.setAdvancedRandomSearch(key=key, value=value)
      self.parent.globalarea.setAdvancedRandomSearch(key=key, value=value)

  def changeOptionRandomInt(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = int(entryfield.text())
        originalvalue = value
      except:
        value = 0
        originalvalue = 1
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(int(value)))
      self.parent.fitarea.setAdvancedRandomSearch(key=key, value=value)
      self.parent.globalarea.setAdvancedRandomSearch(key=key, value=value)
      
  def changeOptionBootstrapInt(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = int(entryfield.text())
        originalvalue = value
      except:
        value = 0
        originalvalue = 1
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(int(value)))
      self.parent.fitarea.setAdvancedBootstrap(key=key, value=value)
      
  def changeOptionInterpolateInt(self, entryfield=None, key=None, minval=0, maxval=1):
    if((entryfield != None) and (key != None)):
      # check paramter boundaries
      try:
        value = int(entryfield.text())
        originalvalue = value
      except:
        value = 0
        originalvalue = 1
      value = np.min((value, maxval))
      value = np.max((value, minval))
      # update parameters
      if (value != originalvalue):
        entryfield.setText(str(int(value)))
      self.parent.objectsarea.setAdvancedInterpolate(key=key, value=value)

  def changeMinAlgorithm(self):
    useAlgorithm = str(self.minAlgorithm.currentText())
    self.parent.fitarea.setAdvancedMinimization(key='algorithm', value=useAlgorithm)
    self.parent.globalarea.setAdvancedMinimization(key='algorithm', value=useAlgorithm)

  def changeStateFileFormat(self):
    useStateFile = str(self.stateFileFormat.currentText())
    self.parent.graphicsarea.setAdvancedExport(key='stateFileFormat', value=useStateFile)

class SelectedDataObject(DataObject):
  def __init__(self, parent=None):
    super(SelectedDataObject, self).__init__(parent)
    # undo increase in zcount upon creation of data object
    self.parent.zcount -= 1
    self.indices = np.array([])
    self.isLive = False
    
    # define custom marker
    selectionMarker = matplotlib.path.Path.unit_circle()
    val = np.sin(np.pi / 4.0)
    verts = np.array([[-val, val], [val, -val], [-val, -val], [val, val]])
    codes = [matplotlib.path.Path.MOVETO, matplotlib.path.Path.LINETO, matplotlib.path.Path.MOVETO, matplotlib.path.Path.LINETO]
    verts = np.concatenate([selectionMarker.vertices, verts])
    codes = np.concatenate([selectionMarker.codes, codes])
    self.selectionMarker = matplotlib.path.Path(verts, codes)
    # use crosshairs
    self.selectionMarker = '$\u2316$'
    # use rotated crosshairs -- it is better to hard-code this because we cannot assume the font symbol is present on each and every platform
    self.selectionMarker = matplotlib.path.Path([[  9.93051613e-01,  -8.69218182e-01],
       [  6.77802346e-01,  -5.53968915e-01], [  8.72817009e-01,  -3.13792962e-01], [  8.72817009e-01,   1.74956012e-03], [  8.72817009e-01,   3.16705572e-01],
       [  6.79561877e-01,   5.55121994e-01], [  9.94811144e-01,   8.70371261e-01], [  8.70764223e-01,   9.94418182e-01], [  5.55514956e-01,   6.79168915e-01],
       [  3.15339003e-01,   8.74183578e-01], [ -1.66979472e-03,   8.74476833e-01], [ -3.18971848e-01,   8.74476833e-01], [ -5.57388270e-01,   6.81221701e-01],
       [ -8.72637537e-01,   9.96470968e-01], [ -9.96684457e-01,   8.72424047e-01], [ -6.83194721e-01,   5.58934311e-01], [ -8.78209384e-01,   3.18758358e-01],
       [ -8.77329619e-01,   5.76539589e-04], [ -8.76449853e-01,  -3.17605279e-01], [ -6.79382405e-01,  -5.55728446e-01], [ -9.94631672e-01,  -8.70977713e-01],
       [ -8.70584751e-01,  -9.95024633e-01], [ -5.55335484e-01,  -6.79775367e-01], [ -3.17212317e-01,  -8.76842815e-01], [ -1.08328446e-03,  -8.75669795e-01],
       [  3.15339003e-01,  -8.74790029e-01], [  5.53755425e-01,  -6.78015836e-01], [  8.69004692e-01,  -9.93265103e-01], [ -5.62960117e-01,   1.50497537e+00],
       [  8.97360704e-05,  -1.24350147e-01], [  4.29708504e-01,  -5.53968915e-01], [  2.44078006e-01,  -6.98543695e-01], [  8.97360704e-05,  -6.98543695e-01],
       [ -2.43898534e-01,  -6.98543695e-01], [ -4.31581818e-01,  -5.56021701e-01], [ -5.62960117e-01,   1.50497537e+00], [ -1.23957185e-01,  -3.03225806e-04],
       [ -5.55628739e-01,  -4.31974780e-01], [ -6.99910264e-01,  -2.42531965e-01], [ -7.01083284e-01,   2.62932551e-03], [ -7.01963050e-01,   2.47497361e-01],
       [ -5.57388270e-01,   4.33127859e-01], [ -5.62960117e-01,   1.50497537e+00], [  5.51995894e-01,  -4.28162463e-01], [  1.24136657e-01,  -3.03225806e-04],
       [  5.55808211e-01,   4.31368328e-01], [  6.98036950e-01,   2.47497361e-01], [  6.96277419e-01,   3.50909091e-03], [  6.94517889e-01,  -2.40479179e-01],
       [  5.51995894e-01,  -4.28162463e-01], [ -5.62960117e-01,   1.50497537e+00], [  4.31761290e-01,   5.55415249e-01], [  8.97360704e-05,   1.23743695e-01],
       [ -4.33341349e-01,   5.57174780e-01], [ -2.45658065e-01,   6.99696774e-01], [  8.97360704e-05,   6.99696774e-01], [  2.45837537e-01,   6.99696774e-01],
       [  4.31761290e-01,   5.55415249e-01], [ -5.62960117e-01,   1.50497537e+00]],
       [1, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 3, 3, 3, 3, 2, 79, 1, 2, 3, 3, 3, 3, 79, 1, 2, 3, 3, 3, 3, 79, 1, 2, 2, 3, 3, 3, 3, 79, 1, 2, 2, 3, 3, 3, 3, 79])

    # change some style values
    self.theseStyles = {'markeredgecolor': [1.0, 1.0, 1.0, 1.0], 'markerfacecolor': [0.1, 0.1, 0.1, 1.0], 'markeredgewidth': 0, 'markersize': 8,
                   'marker': self.selectionMarker, 'markevery': 1, 'linestyle': 'None', 'zorder': 2000,
                   'path_effects': [PathEffects.withStroke(linewidth=1.25, foreground=[1.0, 1.0, 1.0, 0.7])]}
                   ###'path_effects': [PathEffects.withStroke(linewidth=1.5, foreground=[0.4, 0.4, 0.8, 0.7])]}
    self.style.update(self.theseStyles)
    self.Residstyle.update(self.theseStyles)

  # override drawMeResid() function
  def drawMeResid(self, axisobject=None, axisobject2=None, redraw=True):
    if((axisobject != None) or (axisobject2 != None)):
      if(self.handleResid != None):
        self.handleResid.remove()
        self.handleResid = None
      if(self.handleResid_div != None):
        self.handleResid_div.remove()
        self.handleResid_div = None
      # plot resid
      if(len(self.x)):
        # first get current resdiual values
        values, roles = self.parent.data[self.parent.activeData].getData_n_Fit()
        if('resid' in roles):
          self.resid = values[:, roles.index('resid')][self.indices]
        if(axisobject != None):
          self.handleResid, = axisobject.plot(self.x, self.resid)
          if(axisobject2 != None):
            self.handleResid_div, = axisobject2.plot(self.x, self.resid)
          # apply styles
          items = ['markerfacecolor', 'markeredgecolor', 'markeredgewidth', 'markersize', 'marker', 'markevery', 'linestyle', 'zorder', 'path_effects']
          for item in items:
            if(axisobject != None):
              if(hasattr(self.handleResid, 'set_' + item)):
                method2call = getattr(self.handleResid, 'set_' + item)
                method2call(self.Residstyle[item])
            if(axisobject2 != None):
              if(hasattr(self.handleResid_div, 'set_' + item)):
                method2call = getattr(self.handleResid_div, 'set_' + item)
                method2call(self.Residstyle[item])
        # adjust marker size
        self.adjustMarkerSize(resid=True)
            
      # redraw?
      if(redraw):
        self.parent.plotArea.residplotwidget.myRefresh()
    
  # override drawMe() function
  def drawMe(self, axisobject=None, axisobject2=None, redraw=True):
    if((axisobject != None) or (axisobject2 != None)):
      if(self.handleData != None):
        self.handleData.remove()
        self.handleData = None
      if(self.handleData_div != None):
        self.handleData_div.remove()
        self.handleData_div = None
      # plot data
      if(len(self.x)):
        self.isLive = True
        self.toggleParentalButtons(True)
        if(axisobject != None):
          self.handleData, = axisobject.plot(self.x, self.y)
        if(axisobject2 != None):
          self.handleData_div, = axisobject2.plot(self.x, self.y)
        # duplicate me
        if(self.parent.plotArea.splitY):
          self.duplicateForSplit()
        # apply styles
        items = ['markerfacecolor', 'markeredgecolor', 'markeredgewidth', 'markersize', 'marker', 'markevery', 'linestyle', 'zorder', 'path_effects']
        for item in items:
          if(axisobject != None):
            if(hasattr(self.handleData, 'set_' + item)):
              method2call = getattr(self.handleData, 'set_' + item)
              method2call(self.style[item])
          if(axisobject2 != None):
            if(hasattr(self.handleData_div, 'set_' + item)):
              method2call = getattr(self.handleData_div, 'set_' + item)
              method2call(self.style[item])
          # split y items
          if(('handleData' in self.handlesBothAxes) and (self.handlesBothAxes['handleData'] != None)):
            method2call = getattr(self.handlesBothAxes['handleData'], 'set_' + item)
            method2call(self.style[item])
          if(('handleData_div' in self.handlesBothAxes) and (self.handlesBothAxes['handleData_div'] != None)):
            method2call = getattr(self.handlesBothAxes['handleData_div'], 'set_' + item)
            method2call(self.style[item])
        # adjust marker size
        self.adjustMarkerSize(resid=False)
      # redraw?
      if(redraw):
        self.parent.plotArea.dataplotwidget.myRefresh()
        
  def adjustMarkerSize(self, resid=False):
    # dynamically adjusts marker size based on zoom level
    relativeZoomLevel = 1.0
    currentZoomText = self.parent.zoomSelector.currentText()
    currentZoom = currentZoomText.split('%')
    if(len(currentZoom) > 1):
      try:
        relativeZoomLevel = 200.0 / float(currentZoom[0])
      except:
        pass
      
    # update plot handles
    if(resid):
      handles = [self.handleResid, self.handleResid_div]
    else:
      handles = [self.handleData, self.handleData_div]
      if(('handleData' in self.handlesBothAxes) and (self.handlesBothAxes['handleData'] != None)):
        handles.append(self.handlesBothAxes['handleData'])
      if(('handleData_div' in self.handlesBothAxes) and (self.handlesBothAxes['handleData_div'] != None)):
        handles.append(self.handlesBothAxes['handleData_div'])
    
    for handle in handles:
      if(hasattr(handle, 'set_markersize')):
        handle.set_markersize(self.style['markersize'] * relativeZoomLevel)
      if(hasattr(handle, 'set_markeredgewidth')):
        handle.set_markeredgewidth(self.style['markeredgewidth'] * relativeZoomLevel)
      if(hasattr(handle, 'set_path_effects')):
        # have to hard-code the style value
        ###handle.set_path_effects([PathEffects.withStroke(linewidth=1.5 * relativeZoomLevel, foreground=[0.4, 0.4, 0.8, 0.7])])
        handle.set_path_effects([PathEffects.withStroke(linewidth=1.25 * relativeZoomLevel, foreground=[1.0, 1.0, 1.0, 0.7])])

  def clearMe(self, redraw=True):
    # resets everything
    deleteResid = (len(self.resid) > 0)
    self.initValues()
    self.indices = np.array([])
    self.isLive = False
    self.toggleParentalButtons(False)
    if(self.handleData != None):
      self.handleData.remove()
      self.handleData = None
    if(self.handleData_div != None):
      self.handleData_div.remove()
      self.handleData_div = None
    if(self.handleResid != None):
      self.handleResid.remove()
      self.handleResid = None
    if(self.handleResid_div != None):
      self.handleResid_div.remove()
      self.handleResid_div = None
    # remove from split y axis
    self.removeFromSplit()
      
    # redraw?
    if(redraw):
      self.parent.plotArea.dataplotwidget.myRefresh()
      if(deleteResid):
        self.parent.plotArea.residplotwidget.myRefresh()

  def duplicateForSplit(self, xval=[], yval=[], xerr=[], yerr=[]):
    # duplicates current data set and draws it on split y axis
    # delete all previous items (if any)
    self.removeFromSplit()
    
    # assign x and y values
    if(not len(xval)):
      xval, yval, xerr, yerr = self.x, self.y, self.xerr, self.yerr
    xval, yval, xerr, yerr = np.array(xval), np.array(yval), np.array(xerr), np.array(yerr)

    if(self.onSecondAxes):
      axisobjectBase = 'ax'
    else:
      axisobjectBase = 'ax2'
      
    # intialize dictionary with None
    self.handlesBothAxes['handleData'] = None
    self.handlesBothAxes['handleData_div'] = None
      
    # copy plot objects
    for extension in ['', '_div']:
      if(extension == ''):
        # first treat main x axes
        handleData = self.handleData
      else:
        # then treat spit x axes
        handleData = self.handleData_div
      axisobject = self.parent.plotArea.__dict__[axisobjectBase + extension]
        
      if(handleData != None):
        handle = 'handleData' + extension
        self.handlesBothAxes[handle], = axisobject.plot(xval, yval, 'ko')
       
    # apply all styles
    items = ['markerfacecolor', 'markeredgecolor', 'markeredgewidth', 'markersize', 'marker', 'markevery', 'linestyle', 'zorder', 'path_effects']
    for item in items:
      # split y items
      if(('handleData' in self.handlesBothAxes) and (self.handlesBothAxes['handleData'] != None)):
        method2call = getattr(self.handlesBothAxes['handleData'], 'set_' + item)
        method2call(self.style[item])
      if(('handleData_div' in self.handlesBothAxes) and (self.handlesBothAxes['handleData_div'] != None)):
        method2call = getattr(self.handlesBothAxes['handleData_div'], 'set_' + item)
        method2call(self.style[item])
   
  def removeFromSplit(self):
    # removes duplicated data set from split y axes
    for handle in self.handlesBothAxes:
      if(hasattr(self.handlesBothAxes[handle], 'remove')):
        self.handlesBothAxes[handle].remove()
    self.handlesBothAxes = {}

  def mouseSelectIndices(self, selind=np.array([]), redraw=True):
    # selects given indices (callback for plot panel)
    if(len(selind)):
      values, roles = self.parent.data[self.parent.activeData].getData_n_Fit()
      if(('x' in roles) and ('y' in roles)):
        if(len(self.indices) == len(selind)):
          selind = np.logical_xor(selind, self.indices)
        filteredValues = values[selind]
        # check for zero size
        if(len(filteredValues)):
          self.setData(data=filteredValues, roles=roles)
          self.setIndices(indices=selind)
          self.resid, self.fval = np.array([]), np.array([])
          # is item on second axes?
          if(self.parent.data[self.parent.activeData].onSecondAxes):
            useAxes, useAxes2 = self.parent.plotArea.ax2, self.parent.plotArea.ax2_div
          else:
            useAxes, useAxes2 = self.parent.plotArea.ax, self.parent.plotArea.ax_div
          self.drawMe(useAxes, useAxes2, redraw=redraw)
        else:
          self.clearMe(redraw=redraw)
        # update the results table
        if(hasattr(self.parent, 'resultsarea')):
          selectIndices = [i for i, j in enumerate(selind) if j]
          self.parent.resultsarea.selectTheseRows(selectIndices)

  def tableSelectIndices(self, selind=[], redraw=True):
    # selects given indices (callback for resultstable)
    if(len(selind)):
      selind = sorted([i.row() for i in selind])
      values, roles = self.parent.data[self.parent.activeData].getData_n_Fit()
      if(('x' in roles) and ('y' in roles) and len(values[:, roles.index('x')])):
        selind = np.array([True if i in selind else False for i in range(len(values[:, roles.index('x')]))])
        filteredValues = values[selind]
        self.setData(data=filteredValues, roles=roles)
        self.setIndices(indices=selind)
        # is item on second axes?
        if(self.parent.data[self.parent.activeData].onSecondAxes):
          useAxes, useAxes2 = self.parent.plotArea.ax2, self.parent.plotArea.ax2_div
        else:
          useAxes, useAxes2 = self.parent.plotArea.ax, self.parent.plotArea.ax_div
        self.drawMe(useAxes, useAxes2, redraw=redraw)
      else:
        self.clearMe(redraw=redraw)
    else:
      self.clearMe(redraw=redraw)

  def setIndices(self, indices):
    # updates indices
    self.indices = indices

  def getIndices(self):
    return self.indices
  
  def toggleParentalButtons(self, state=True):
    # toggles activity of buttons in plotArea and resultstable
    for item in ['plotArea', 'resultsarea']:
      if(hasattr(self.parent, item)):
        self.parent.__dict__[item].clearButton.setEnabled(state)
    if(hasattr(self.parent, 'resultsarea')):
      self.parent.resultsarea.spawnButton.setEnabled(state)

class Ui_MainWindow(object):
  def setupUi(self, MainWindow):
    # save MainWindow
    self.MainWindow = MainWindow
    # set up z-order counter
    self.zcount = 0
    # upshift all draw items in zo to ensure display in front of axes and gridlines
    self.zOffset = 3
    # default duration for status messages
    self.STATUS_TIME = 10000
    # store styles for data, fit and residuals objects
    self.dataStyles, self.residStyles, self.fitStyles = {}, {}, {}
    # set up data and fit objects
    self.fit = []
    self.fit.append(FitObject(self))
    self.data = []
    self.data.append(DataObject(self))
    self.selectedData = SelectedDataObject(self)
    self.extras = []
    self.activeData = 0
    self.activeFit = 0
    self.discardCheck = True
    self.discard = False
    self.zoomLevels = [25, 33, 50, 75, 100, 150, 200, 300, 400, 600]
    self.currStateFile = None
    self.lastFitType = 'none'
    self.lastFitGlobalDataSets, self.lastFitGlobalCurves = [], []
    
    # get font list
    self.fontList = matplotlib.font_manager.fontManager.ttflist
    self.fontNames = [i.name for i in self.fontList]
    self.fontNames= sorted(list(set(self.fontNames)))
    
    # set up GUI
    self.buildRessource()
    
    # set central widget and splitter sizes
    self.MainWindow.setCentralWidget(self.centralwidget)
    self.masterwidget.setSizes([int(424 * SCALEFONT), self.masterwidget.size().width() - int(424 * SCALEFONT)])

  def buildRessource(self):
    # build the gui
    self.MainWindow.setObjectName("MainWindow")
    # try to determine screen size to prevent initial window size larger than screen on small displays
    try:
      screen = app.primaryScreen()
      height = screen.size().height()
    except:
      height = 768
    useHeight = min(int(768 * (1 + SCALEFONT) / 2.), height)
    self.MainWindow.resize(int(1024 * SCALEFONT), useHeight)
    self.MainWindow.setMinimumWidth(int(864 * SCALEFONT))
    self.MainWindow.setMinimumHeight(int(500 * (1 + SCALEFONT) / 2.))
    self.MainWindow.setWindowTitle('Fit-o-mat v' + VERSION)
    self.centralwidget = QWidgetMac()
    self.centralwidget.setObjectName("centralwidget")
    self.vLayout0 = QtWidgets.QVBoxLayout(self.centralwidget)
    self.vLayout0.setAlignment(QtCore.Qt.AlignmentFlag.AlignLeft|QtCore.Qt.AlignmentFlag.AlignTop)
    self.vLayout0.setContentsMargins(0, 0, 0, 0)
    self.masterwidget = QtWidgets.QSplitter(QtCore.Qt.Orientation.Horizontal)
    self.vLayout0.addWidget(self.masterwidget)
    self.masterwidget.setSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Expanding)
    self.masterwidget.setChildrenCollapsible(False)
    self.masterwidget.setOpaqueResize(False)

    self.tabWidget = myQTabWidget()
    self.masterwidget.addWidget(self.tabWidget)
    self.tabWidget.setEnabled(True)
    self.tabWidget.setGeometry(QtCore.QRect(0, 0, int(422 * SCALEFONT), int(500 * (1 + SCALEFONT) / 2.)))
    #self.tabWidget.setMaximumSize(QtCore.QSize(422, 16777215))
    self.tabWidget.setMinimumSize(QtCore.QSize(int(422 * SCALEFONT), int(500 * (1 + SCALEFONT) / 2.)))
    self.tabWidget.setObjectName("tabWidget")

    # the matplotlib canvas
    self.plotArea = MatplotlibCanvas(self)
    self.plotArea.setMinimumSize(QtCore.QSize(int(440 * SCALEFONT), int(500 * (1 + SCALEFONT) / 2.)))
    self.masterwidget.addWidget(self.plotArea)
    self.masterwidget.setSizes([int(422 * SCALEFONT), self.masterwidget.size().height() - int(422 * SCALEFONT)])
    
    # on Windows and Linux add markers for splitter
    if(not (platform == 'darwin')):
      self.addSplitterHandleSymbols(parentHandle=self.masterwidget, direction='vertical', number=1, repeat=5)

    # the data tab
    self.tab = QWidgetMac()
    self.tab.setObjectName("tab")
    self.vLayout = QtWidgets.QVBoxLayout(self.tab)
    self.vLayout.setContentsMargins(2, 2, 2, 2)
    self.dataarea = DataArea(self)
    self.vLayout.addWidget(self.dataarea)
    self.tabWidget.addTab(self.tab, "")
    self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), "Data")
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab), 'This tab serves to import data, optionally allowing to specify data errors\nand perform data reduction and transformation operations.\n(Ctrl-1)')

    # the fit tab
    self.tab_2 = QWidgetMac()
    self.tab_2.setObjectName("tab_2")
    self.hLayout2 = QtWidgets.QHBoxLayout(self.tab_2)
    self.hLayout2.setContentsMargins(2, 2, 2, 2)
    self.fitarea = FitArea(self)
    self.hLayout2.addWidget(self.fitarea)
    self.tabWidget.addTab(self.tab_2, "")
    self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), "Fit")
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab_2), 'This tab serves to select, modify and fit functions,\nand to optimize parameters by nonlinear least-\nsquares fitting or numerical minimization.\n(Ctrl-2)')

    # the global fit tab
    self.tab_3 = QWidgetMac()
    self.tab_3.setObjectName("tab_3")
    self.hLayout25 = QtWidgets.QHBoxLayout(self.tab_3)
    self.hLayout25.setContentsMargins(2, 2, 2, 2)
    self.globalarea = GlobalArea(self)
    self.hLayout25.addWidget(self.globalarea)
    self.tabWidget.addTab(self.tab_3, "")
    self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), "Global")
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab_3), 'This tab allows to conduct global fits, i.e. fit\nmore than one data set simultaneously to one or more functions.\n(Ctrl-3)')

    # the results tab
    self.tab_4 = QWidgetMac()
    self.tab_4.setObjectName("tab_4")
    self.hLayout3 = QtWidgets.QHBoxLayout(self.tab_4)
    self.hLayout3.setContentsMargins(2, 2, 2, 2)
    self.resultsarea = ResultsArea(self)
    self.hLayout3.addWidget(self.resultsarea)
    self.tabWidget.addTab(self.tab_4, "")
    self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), "Results")
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab_4), 'This tab shows the imported data and fit results if present.\n(Ctrl-4)')

    # the objects tab
    self.tab_5 = QWidgetMac()
    self.tab_5.setObjectName("tab_5")
    self.hLayout4 = QtWidgets.QHBoxLayout(self.tab_5)
    self.hLayout4.setContentsMargins(2, 2, 2, 2)
    self.objectsarea = ObjectsArea(self)
    self.hLayout4.addWidget(self.objectsarea)
    self.tabWidget.addTab(self.tab_5, "")
    self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_5), "Objects")
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab_5), 'This tab grants control of data, curve and graphics objects.\nObjects can be configured, copied or deleted.\n(Ctrl-5)')
    
    # the graphics export tab
    self.tab_6 = QWidgetMac()
    self.hLayout5 = QtWidgets.QHBoxLayout(self.tab_6)
    self.hLayout5.setContentsMargins(2, 2, 2, 2)
    self.graphicsarea = GraphicsArea(self)
    self.hLayout5.addWidget(self.graphicsarea)
    self.tabWidget.addTab(self.tab_6, "")
    self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_6), "Graphics")
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab_6), 'This tab allows to configure the plot graphics,\nexport them in vector and bitmap formats,\nand compile a report of current fit results.\n(Ctrl-6)')
    
    # the 2nd axes tab
    self.tab_7 = QWidgetMac()
    self.hLayout6 = QtWidgets.QHBoxLayout(self.tab_7)
    self.hLayout6.setContentsMargins(2, 2, 2, 2)
    self.graphicsarea2 = GraphicsArea(parent=self, secondAxes=True)
    self.hLayout6.addWidget(self.graphicsarea2)
    self.tabWidget.setTabToolTip(self.tabWidget.indexOf(self.tab_7), 'This tab contains options for configuring the second axes graphics.\n(Ctrl-7)')

    # initially turn off and on to store tab tooltips
    self.tabWidget.toggleToolTips(False)
    self.tabWidget.toggleToolTips(True)

    # check whether default style exists
    defaultStyle = 'default.style'
    try:
      with open(WORKINGDIR + PATH_SEPARATOR + 'styles' + PATH_SEPARATOR + defaultStyle, 'r', encoding='utf-8') as loadhandle:
        red = loadhandle.readlines()
      red = ''.join(red)

      # apply style sheet
      self.graphicsarea.processStyleSet(operation='load', modus=red, zoffsetData=0, zoffsetCurve=0, redraw=False)
    except:
      pass
    
    self.plotArea.initLegend()
    
    # status bar and buttons
    self.statusbar = KuhStatusBar(self.MainWindow)
    self.MainWindow.setStatusBar(self.statusbar)
    
    blah = QtWidgets.QLabel()
    blah.setText('Status OK')
    self.statusbar.addWidget(blah)

    self.zoomLabel = QtWidgets.QLabel()
    self.zoomLabel.setText('Zoom')
    self.zoomLabel.setMaximumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
    self.zoomLabel.setMinimumSize(QtCore.QSize(int(35 * SCALEFONT), int(BASE_SIZE)))
    self.statusbar.addPermanentWidget(self.zoomLabel)

    self.zoomSelector = QComboBoxMac()
    self.zoomSelector.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.zoomSelector.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    for item in self.zoomLevels[::-1]:
      self.zoomSelector.addItem(str(item) + '%')
    currZoomLevel = self.plotArea.matplot.get_dpi()
    # make sure there are no decimal points
    currZoomLevel = int(currZoomLevel)
    if(currZoomLevel in self.zoomLevels):
      self.zoomSelector.setCurrentIndex(self.zoomLevels[::-1].index(currZoomLevel))
    else:
      self.zoomSelector.setCurrentText('100%')
    self.zoomSelector.currentIndexChanged.connect(self.setZoomLevel)
    self.zoomSelector.setToolTip('Set plot magnification\n(Ctrl-+ and Ctrl--)')
    self.statusbar.addPermanentWidget(self.zoomSelector)

    self.loadStateButton = QToolButtonMac()
    self.loadStateButton.setText(' Open State')
    self.loadStateButton.setToolTip('Load program state file\n(Ctrl-O)')
    self.loadStateButton.setMaximumSize(QtCore.QSize(int(85 * SCALEFONT), int(BASE_SIZE)))
    self.loadStateButton.setMinimumSize(QtCore.QSize(int(85 * SCALEFONT), int(BASE_SIZE)))
    self.loadStateButton.clicked.connect(partial(self.loadState, None, False, True))
    self.loadStateButton.setIcon(FOM_ICON_DIALOG_OPEN_BUTTON)
    self.loadStateButton.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
    self.statusbar.addPermanentWidget(self.loadStateButton)

    # set up recent file list menu
    self.recentStateMenu = RecentStateMenu(self)
    self.loadStateButton.setPopupMode(QtWidgets.QToolButton.ToolButtonPopupMode.MenuButtonPopup)
    self.loadStateButton.setMenu(self.recentStateMenu)

    self.saveStateButton = QPushButtonMac()
    self.saveStateButton.setText(' Save State')
    self.saveStateButton.setToolTip('Save program state to file\n(Ctrl-S)')
    self.saveStateButton.setMaximumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.saveStateButton.setMinimumSize(QtCore.QSize(int(80 * SCALEFONT), int(BASE_SIZE)))
    self.saveStateButton.clicked.connect(partial(self.saveState, ''))
    self.saveStateButton.setIcon(FOM_ICON_DIALOG_SAVE_BUTTON)
    self.statusbar.addPermanentWidget(self.saveStateButton)

    self.advancedButton = QPushButtonMac()
    self.advancedButton.setText(' Advanced')
    self.advancedButton.setToolTip('Configure advanced program settings')
    self.advancedButton.setMaximumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.advancedButton.setMinimumSize(QtCore.QSize(int(75 * SCALEFONT), int(BASE_SIZE)))
    self.advancedButton.clicked.connect(partial(self.advancedOption, self.advancedButton))
    self.advancedButton.setIcon(FOM_ICON_TOOL)
    self.statusbar.addPermanentWidget(self.advancedButton)

    self.aboutButton = QPushButtonMac()
    self.aboutButton.setText(' About')
    self.aboutButton.setToolTip('Who dunnit?')
    self.aboutButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.aboutButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.aboutButton.clicked.connect(self.aboutInfo)
    self.aboutButton.setIcon(FOM_ICON_MESSAGE_BOX_QUESTION)
    self.statusbar.addPermanentWidget(self.aboutButton)
  
    self.helpButton = QPushButtonMac()
    self.helpButton.setText(' Help')
    self.helpButton.setToolTip('Display manual page\n(F1)')
    self.helpButton.setMaximumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.helpButton.setMinimumSize(QtCore.QSize(int(50 * SCALEFONT), int(BASE_SIZE)))
    self.helpButton.clicked.connect(self.showHelp)
    self.helpButton.setIcon(FOM_ICON_DIALOG_HELP_BUTTON)
    self.statusbar.addPermanentWidget(self.helpButton)
    
    # add controls for tooltips
    self.toolTipButton = QPushButtonCheckable()
    self.toolTipButton.setText('Tooltips')
    self.toolTipButton.setToolTip('Display tool tips')
    self.toolTipButton.setChecked(True)
    self.toolTipButton.setMaximumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.toolTipButton.setMinimumSize(int(50 * SCALEFONT), int(BASE_SIZE))
    self.toolTipButton.clicked.connect(self.toggleToolTips)
    self.statusbar.addPermanentWidget(self.toolTipButton)
    
    # give focus to empty data table
    self.dataarea.tableWidget.setFocus()

  def addSplitterHandleSymbols(self, parentHandle=None, direction='horizontal', number=1, repeat=5):
    # adds spacer symbols to splitter handle
    if(parentHandle != None):
      if(direction in ['horizontal', 'vertical']):
        parentHandle.setHandleWidth(5)
        handli = parentHandle.handle(number)
        if(handli != None):
          if(direction == 'horizontal'):
            layoutli = QtWidgets.QHBoxLayout(handli)
          else:
            layoutli = QtWidgets.QVBoxLayout(handli)
          layoutli.addStretch()
          for repeat in range(repeat):
            bogus = QtWidgets.QWidget()
            bogus.setStyleSheet('QWidget {background-color: #888888;border-radius: 2px;}')
            bogus.setMinimumSize(4, 4)
            bogus.setMaximumSize(4, 4)
            layoutli.addWidget(bogus)
          layoutli.addStretch()
          layoutli.setSpacing(3)
          layoutli.setContentsMargins(1, 0, 0, 0)

  def toggleToolTips(self):
    # toggles display of tool tips
    state = self.toolTipButton.isChecked()
    myapp.toolTipsShow = state
    # toggle tool tips for tab widget (need to do this separately from other widgets as event catching fails)
    self.tabWidget.toggleToolTips(state)
    # toggle tool tip for data table
    self.dataarea.tableWidget.toggleToolTips(state)
    # toggle tool tip for results table
    self.resultsarea.toggleToolTips(state)

  def setZoomLevel(self):
    # sets DPI level in data and resid plots
    zoomLevel = str(self.zoomSelector.currentText())
    zoomLevel = zoomLevel.split('%')[0]
    zoomLevel = int(zoomLevel)
    self.plotArea.setZoomLevel(zoomLevel)

  def handleDropEvent(self, mimeData):
    # callback function that handles drag and drop events
    success = False
    if(mimeData.hasUrls()):
      urls = mimeData.urls()
      if(len(urls)):
        filename = urls[0].path()
        if((platform == 'win32') and (filename.startswith('/'))):
          # delete leading slash as this causes problems with the subsequent opening of file
          filename = filename[1:]
        if((filename.endswith('.state')) or (filename.endswith('.statez'))):
          self.loadState(filename, dropEvent=True)
          success = True
        elif((filename.endswith('.xls')) or (filename.endswith('.xlsx'))):
          self.dataarea.tableWidget.loadXLS(filename=filename, transpose=self.dataarea.transposeData)
          self.tabWidget.setCurrentIndex(0)
          success = True
        elif(filename.endswith('.txt')):
          self.dataarea.tableWidget.loadTextFile(filename=filename, delimiter=None, transpose=self.dataarea.transposeData)
          self.tabWidget.setCurrentIndex(0)
          success = True
        elif(filename.endswith('.csv')):
          self.dataarea.tableWidget.loadTextFile(filename=filename, delimiter=',', transpose=self.dataarea.transposeData)
          self.tabWidget.setCurrentIndex(0)
          success = True
        elif(filename.endswith('.res')):
          self.dataarea.tableWidget.loadUnicornFile(filename=filename, transpose=self.dataarea.transposeData)
          self.tabWidget.setCurrentIndex(0)
          success = True
      
    if(not success):
      self.statusbar.showMessage('Cannot deal with this input/file type.', self.STATUS_TIME)
  
  def loadState(self, stateFile=None, dropEvent=False, appendState=True):
    global RECENTSTATES
    # store how many objects we have before the state load
    numberData, numberFit, numberExtras = len(self.data), len(self.fit), len(self.extras)
    # open specific file or select one via file dialog
    if(stateFile != None):
      filename = stateFile
      if(not dropEvent):
        self.discardCheck = False
        self.discard = True
    else:
      global REMEMBERDIR
      # parse state file
      filename, filter_ = QtWidgets.QFileDialog.getOpenFileName(self.centralwidget, filter='State file (*.state *.statez)', directory = REMEMBERDIR, caption='Open State')
      filename = str(filename)
      if(PATH_SEPARATOR in filename):
        REMEMBERDIR = filename.split(PATH_SEPARATOR)[:-1]
        REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
      elif('/' in filename):
        REMEMBERDIR = filename.split('/')[:-1]
        REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
        
    # read file contents
    settings = {}; activeCategory = ''
    # check whether this state file is a zip archive
    if(zipfile.is_zipfile(filename)):
      try:
        ziphandle = zipfile.ZipFile(filename)
        fileList = ziphandle.filelist
        if(len(fileList)):
          loadhandle = ziphandle.open(fileList[0].filename, 'r')
          # use wrapper to convert bytes into str
          loadhandle = io.TextIOWrapper(loadhandle, encoding='utf-8')
          success = True
        else:
          success = False
      except:
        success = False
    else:
      try:
        loadhandle = open(filename,'r', encoding='utf-8')
        success = True
      except:
        success = False
    
    if(not success):
      if(filename):
        self.statusbar.showMessage('Cannot load state file ' + filename, self.STATUS_TIME)
    else:
      # save filename for future reference
      if(filename.endswith('.statez')):
        self.currStateFile = ''.join(filename.split('.')[:-1]) + '.statez'
      else:
        self.currStateFile = filename
      # display filename in windows title
      self.MainWindow.setWindowTitle('Fit-o-mat v' + VERSION + ' -- ' + self.currStateFile)
      # check whether to discard old items
      if(self.discardCheck):
        msgBox = myQMessageBox()
        msgBox.setWindowTitle('Fit-o-mat')
        msgBox.setText('Discard current objects?')
        msgBox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)
        msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.No)
        msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
        # apply styles and show
        if(QSTYLE != None):
          msgBox.setStyle(QSTYLE)
        if(QSTYLESHEET != None):
          msgBox.setStyleSheet(QSTYLESHEET)
        reply, self.discardCheck = msgBox.exec()
        if (reply == QtWidgets.QMessageBox.StandardButton.Yes):
          self.discard = True
        else:
          self.discard = False
      # display busy pointer
      QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
      QtCore.QCoreApplication.processEvents()
      # check whether message window exists and close it
      if(hasattr(self, 'daughterWindow')):
        self.daughterWindow.close()
      # check whether preview window exists and close it
      if(hasattr(self, 'previewWindow')):
        self.previewWindow.close()
      # check whether covar window exists and close it
      if(hasattr(self, 'covarWindow')):
        self.covarWindow.close()
      # parse file contents
      red = loadhandle.readline()
      while (red):
        if(activeCategory == 'MESSAGE'):
          if(red.find('</MESSAGE>') == 0):
            activeCategory = ''
          else:
            red = red.strip()
            if(activeCategory in settings):
              settings[activeCategory].append(red)
        elif(activeCategory == 'GLOBAL'):
          if(red.find('</GLOBAL>') == 0):
            activeCategory = ''
          else:
            red = red.strip()
            if(activeCategory in settings):
              settings[activeCategory].append(red)
        else:
          if(red.find('<') == 0):
            if(red.find('/') == 1):
              activeCategory = ''
            else:
              activeCategory = red[1:].split('>')[0]
              if(activeCategory in ['GRAPHICS', 'CANVAS', 'OBJECTS', 'ADVANCED', 'ADVANCED_MIN', 'ADVANCED_RANDOM']):
                settings[activeCategory] = {}
              else:
                settings[activeCategory] = []
          elif(red.find('>>>') == 0):
            if(activeCategory != ''):
              entry = red[3:].strip()
              red = loadhandle.readline()
              red = red.strip()
              # convert string input to original data
              try:
                red = literal_eval(red)
                settings[activeCategory][entry] = red
              except:
                self.statusbar.showMessage('Failed to restore setting ' + repr(entry) + repr(red), self.STATUS_TIME)
                print('Failed to restore setting', entry, red)
          else:
            red = red.strip()
            if(activeCategory in settings):
              settings[activeCategory].append(red)
        red = loadhandle.readline()
        
      loadhandle.close()

      # close zip file if handle had been opened
      if(zipfile.is_zipfile(filename)):
        ziphandle.close()

      # apply settings
      # apply axes mode to avoid problems with log axes
      temp_settings = {}
      # initially set splitY to False (this is to streamline loading older state files that are ignorant of this setting yet)
      temp_settings['splitY'] = False
      for entry in ['modeX', 'modeY', 'modeX_div']:
        if(('CANVAS' in settings) and (entry in settings['CANVAS'])):
          temp_settings[entry] = settings['CANVAS'][entry]
        else:
          temp_settings[entry] = 'linear'
      # also update x limits to have curves drawn over entire interval
      for entry in ['minX', 'maxX', 'minX_div', 'maxX_div']:
        if(('CANVAS' in settings) and (entry in settings['CANVAS'])):
          temp_settings[entry] = settings['CANVAS'][entry]
      # disable autoscaling to speed up things
      temp_settings.update({'autoScaleX': False, 'autoScaleY': False, 'autoScaleX_div': False, 'autoScaleY2': False})
      self.plotArea.restoreState(temp_settings, updateTicks=False)

      # count data sets and curves already present
      zoffsetData = len(self.data)
      zoffsetCurve = len(self.fit)
      zoffsetResid = zoffsetData
      zoffsetExtras = len(self.extras)
      zoffset = zoffsetData + zoffsetCurve
  
      # data sets
      # helper function for sort routine
      def itemSort(a, b):
        # first try converting to integer
        try:
          ia, ib = a.split('_')[-1], b.split('_')[-1]
          ia = int(ia)
          ib = int(ib)
          return (ia > ib) - (ia < ib)
        except:
          return (a > b) - (a < b)

      duplicateItems = []
      datasets = [i for i in settings if i.startswith('DATA')]
      datasets = sorted(datasets, key=cmp_to_key(itemSort))
      if(len(datasets)):
        for index, entry in enumerate(datasets):
          data = ''.join(settings[entry])
          try:
            data = literal_eval(data)
            # need to restore 'inf' as they did not correctly propagate through ast.literal_eval
            # as of recent python/numpy versions, also need to check for np.nan
            for key in data:
              if(type(data[key]) == list):
                data[key] = [np.inf if ((i == 'np.inf') or (i == 'inf')) else i for i in data[key]]
                data[key] = [-np.inf if ((i == '-np.inf') or (i == '-inf')) else i for i in data[key]]
                data[key] = [np.nan if ((i == 'np.nan') or (i == 'nan')) else i for i in data[key]]
            # generate new data set
            self.data.append(DataObject(self))
            # and restore values
            if(self.discard):
              useZoffsetCurve = 0
            else:
              useZoffsetCurve = zoffsetCurve
            self.data[-1].restoreState(data, zoffset, zoffsetResid, useZoffsetCurve)
            # phew - we shall need to activate bar, scatter, and stack display before duplicating for split
            if('GRAPHICS' in settings):
              grfx = settings['GRAPHICS']
              if((('dataBar_' + str(index)) in grfx) and ('showBar' in grfx['dataBar_' + str(index)])):
                self.data[-1].Barstyle['showBar'] = grfx['dataBar_' + str(index)]['showBar']
              if((('dataStack_' + str(index)) in grfx) and ('showStack' in grfx['dataStack_' + str(index)])):
                self.data[-1].Stackstyle['showStack'] = grfx['dataStack_' + str(index)]['showStack']
              if((('dataScatter_' + str(index)) in grfx) and ('showScatter' in grfx['dataScatter_' + str(index)])):
                self.data[-1].Scatterstyle['showScatter'] = grfx['dataScatter_' + str(index)]['showScatter']
            # cause data to be drawn
            self.data[-1].drawMe(redraw=False, rescale=False)
            # also create a new resid object
            self.data[-1].drawMeResid(redraw=False, rescale=False)
            # mark for split
            duplicateItems.append(self.data[-1])
          except:
            self.statusbar.showMessage('Failed to restore data set!', self.STATUS_TIME)
            print('Failed to restore data set', data)
          
      # curve sets
      curvesets = [i for i in settings if i.startswith('CURVE')]
      curvesets = sorted(curvesets, key=cmp_to_key(itemSort))
      if(len(curvesets)):
        for entry in curvesets:
          data = ''.join(settings[entry])
          try:
            # evaluate data
            data = literal_eval(data)
            # need to restore 'inf' as they did not correctly propagate through ast.literal_eval
            # as of recent python/numpy versions, also need to check for np.nan
            for key in data:
              if(type(data[key]) == list):
                data[key] = [np.inf if ((i == 'np.inf') or (i == 'inf')) else i for i in data[key]]
                data[key] = [-np.inf if ((i == '-np.inf') or (i == '-inf')) else i for i in data[key]]
                data[key] = [np.nan if ((i == 'np.nan') or (i == 'nan')) else i for i in data[key]]
            # generate new data set
            self.fit.append(FitObject(self))
            # and restore values
            self.fit[-1].restoreState(data, zoffset)
            # cause data to be drawn
            self.fit[-1].drawMe(redraw=False, rescale=False)
            # mark for split
            duplicateItems.append(self.fit[-1])
          except:
            self.statusbar.showMessage('Failed to restore curve!', self.STATUS_TIME)
            print('Failed to restore curve', data)
      
      # extras!
      failedImportExtras = []
      extras = [i for i in settings if i.startswith('EXTRAS')]
      extras = sorted(extras, key=lambda k: k.split('_')[-1])
      if(len(extras)):
        for entry in extras:
          data = ''.join(settings[entry])
          try:
            # generate new data set -- need to do this first in case it fails (keep track on correct extras count)
            self.extras.append(ExtrasObject(self))
            # evaluate data
            data = literal_eval(data)
            # and restore values
            self.extras[-1].restoreState(data, zoffset)
            # cause data to be drawn
            self.extras[-1].drawMe(redraw=False)
            # mark for split
            duplicateItems.append(self.extras[-1])
          except:
            self.statusbar.showMessage('Failed to restore extra object!', self.STATUS_TIME)
            print('Failed to restore extra object', data)
            failedImportExtras.append(len(self.extras) - 1)
      
      if((len(datasets)) or (len(curvesets))):
        # update legend
        self.objectsarea.updateLegend(redraw=False)
        
      # objects tab
      if('OBJECTS' in settings):
        self.objectsarea.restoreState(settings['OBJECTS'], zoffsetData, zoffsetCurve, zoffsetExtras, zoffsetResid)
      
      # data area tab
      if('IMPORTTABLE' in settings):
        self.dataarea.restoreState(settings['IMPORTTABLE'])
      
      # global area tab
      if('GLOBAL' in settings):
        self.globalarea.restoreState(settings['GLOBAL'])
      
      # call canvas comes last to update certain settings
      if('CANVAS' in settings):
        self.plotArea.restoreState(settings['CANVAS'])
        
      # check at this point whether we have splitY axis
      if(self.plotArea.splitY):
        for item in duplicateItems:
          if(item.visibility):
            if(not hasattr(item, 'onBothAxes')):
              # for older state files that don't know this setting
              item.onBothAxes = True
            item.duplicateForSplit()

      # also need to enable zoom on splitY axis and deal w/ cursors
      self.plotArea.toggleSplitYHelper(state=self.plotArea.splitY)
        
      # apply canvas and graphics last to counteract autozoom when generating data and fits
      if('GRAPHICS' in settings):
        red = ''
        for key in settings['GRAPHICS']:
          red += '>>>' + key + '\n'
          red += repr(settings['GRAPHICS'][key]) + '\n'
        self.graphicsarea.processStyleSet(operation='load', modus=red, zoffsetData=zoffsetData,\
                                          zoffsetCurve=zoffsetCurve, redraw=False, rescale=False)

      # very finally check whether state file contained a message to display
      if('MESSAGE' in settings):
        self.displayMessage(settings['MESSAGE'])
        
      # delete extras that failed to import
      for entry in failedImportExtras[::-1]:
        self.objectsarea.deleteExtra(entry, redraw=False)
      
      # delete initial data and curve objects
      if(self.discard):
        # delete surplus fit objects
        numberFit = min(numberFit, len(self.fit) - 1)
        for entry in range(numberFit)[::-1]:
          self.objectsarea.deleteCurve(entry, redraw=False)
        # delete surplus data objects
        numberData = min(numberData, len(self.data) - 1)
        for entry in range(numberData)[::-1]:
          self.objectsarea.deleteDataSet(entry, redraw=False)
        # delete surplus extra objects
        for entry in range(numberExtras)[::-1]:
          self.objectsarea.deleteExtra(entry, redraw=False)

      # and again call canvas comes last to preserve axis settings
      if('CANVAS' in settings):
        self.plotArea.restoreState(settings['CANVAS'])
        
      # set advanced settings
      if('ADVANCED' in settings):
        self.graphicsarea.advancedExport.update(settings['ADVANCED'])
        if('advancedGraphics' in settings['ADVANCED']):
          value = settings['ADVANCED']['advancedGraphics']
          self.graphicsarea.setAdvancedGraphics(value)
          self.graphicsarea2.setAdvancedGraphics(value)
          self.graphicsarea.configAdvancedSettingsButton.setChecked(value)
        
      # set advanced settings
      if('ADVANCED_MIN' in settings):
        self.fitarea.advancedMinimization.update(settings['ADVANCED_MIN'])
        self.globalarea.advancedMinimization.update(settings['ADVANCED_MIN'])
        
      # set advanced settings
      if('ADVANCED_RANDOM' in settings):
        self.fitarea.advancedRandomSearch.update(settings['ADVANCED_RANDOM'])
        self.globalarea.advancedRandomSearch.update(settings['ADVANCED_RANDOM'])
        
      # control visibility of transformed axis controls
      if(('GRAPHICS' in settings) and ('slavedX' in settings['GRAPHICS'])):
        if(settings['GRAPHICS']['slavedX']):
          for entry in self.graphicsarea.toggleItemsTransformed:
            if((not (entry in self.graphicsarea.advancedGraphicsSettings)) or self.graphicsarea.advancedExport['advancedGraphics']):
              entry.show()
        else:
          for entry in self.graphicsarea.toggleItemsTransformed:
            entry.hide()
        
      # check whether we have items on the second axes
      self.objectsarea.checkSecondAxes()
      
      # ensure that visibility of items is correctly set (can somehow be lost en route)
      for entry in self.fit:
        entry.setVisibility(state=entry.visibility, redraw=False)
      for entry in self.data:
        entry.setVisibility(state=entry.visibility, redraw=False)
        entry.setVisibilityResid(state=entry.visibilityResid, redraw=False)
      for entry in self.extras:
        entry.setVisibility(state=entry.visibility, redraw=False)
        
      # need to put extra check for category labels on x axis (kind of ugly but somehow this info is lost en route)
      if(len(self.plotArea.ticksXLabel) and (len(self.plotArea.ticksXLabel) == len(self.plotArea.ticksX))):
        for axisobject in [self.plotArea.ax2, self.plotArea.ax_resid]:
          axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.plotArea.ticksX))
          axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.plotArea.ticksXLabel))
      if(len(self.plotArea.ticksXLabel_div) and (len(self.plotArea.ticksXLabel_div) == len(self.plotArea.ticksX_div))):
        for axisobject in [self.plotArea.ax2_div, self.plotArea.ax_resid_div]:
          axisobject.xaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.plotArea.ticksX_div))
          axisobject.xaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.plotArea.ticksXLabel_div))
      
      # and for the y axes as well for good measure
      if(len(self.plotArea.ticksYLabel) and (len(self.plotArea.ticksYLabel) == len(self.plotArea.ticksY))):
        for axisobject in [self.plotArea.ax]:
          axisobject.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.plotArea.ticksY))
          axisobject.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.plotArea.ticksYLabel))
      if(len(self.plotArea.ticksY2Label) and (len(self.plotArea.ticksY2Label) == len(self.plotArea.ticksY2))):
        for axisobject in [self.plotArea.ax2]:
          axisobject.yaxis.set_major_locator(matplotlib.ticker.FixedLocator(self.plotArea.ticksY2))
          axisobject.yaxis.set_major_formatter(matplotlib.ticker.FixedFormatter(self.plotArea.ticksY2Label))
      
      # restore discardCheck when loading state on startup
      if((stateFile != None) and (not dropEvent)):
        self.discardCheck = True
        self.discard = False
        
      # finally refresh plots
      try:
        self.plotArea.dataplotwidget.myRefresh()
      except:
        # implement extra check for bad fonts in state file
        ###safeFont = 'DejaVu Sans'
        # x and y axes
        _, _, descent = self.plotArea.ax.xaxis.label._get_layout(self.plotArea.matplot.canvas.renderer)
        if(not (descent > 0)):
          self.plotArea.setAxisFont(value=SAFE_FONT, axis='x', redraw=True, target='plot')
        _, _, descent = self.plotArea.ax.yaxis.label._get_layout(self.plotArea.matplot.canvas.renderer)
        if(not (descent > 0)):
          self.plotArea.setAxisFont(value=SAFE_FONT, axis='y', redraw=True, target='plot')
          
        # x ticks
        flag = False
        tickLabels = self.plotArea.ax.get_xticklabels()
        for entry in tickLabels:
          _, _, descent = entry._get_layout(self.plotArea.matplot.canvas.renderer)
          if(not (descent > 0)):
            flag = True
        if(flag):
          self.plotArea.setTickFont(value=SAFE_FONT, axis='x', redraw=True, target='plot')
          
        # y ticks
        flag = False
        tickLabels = self.plotArea.ax.get_yticklabels()
        for entry in tickLabels:
          _, _, descent = entry._get_layout(self.plotArea.matplot.canvas.renderer)
          if(not (descent > 0)):
            flag = True
        if(flag):
          self.plotArea.setTickFont(value=SAFE_FONT, axis='y', redraw=True, target='plot')
          
        # legend (get_layout trick won't work here ... would need to draw extra text ... too complicated)
        # this is easier if clumsy
        probeFont = self.plotArea.legendLabelFont; self.plotArea.legendLabelFont = SAFE_FONT
        self.plotArea.setLegendLabelFont(value=probeFont, redraw=True, target='plot')
        
        # now also need to check extras
        for entry in self.extras:
          if((entry.handle != None) and hasattr(entry.handle, '_get_layout')):
            _, _, descent = entry.handle._get_layout(self.plotArea.matplot.canvas.renderer)
            if(not (descent > 0)):
              entry.handle.set_fontname(SAFE_FONT)
              try:
                self.plotArea.dataplotwidget.myRefresh()
              except:
                pass
        
      try:        
        self.plotArea.residplotwidget.myRefresh()
      except:
        # implement extra check for bad fonts in state file
        ###safeFont = 'DejaVu Sans'
        # x and y axes
        _, _, descent = self.plotArea.ax_resid.xaxis.label._get_layout(self.plotArea.residplot.canvas.renderer)
        if(not (descent > 0)):
          self.plotArea.setAxisFont(value=SAFE_FONT, axis='x', redraw=True, target='resid')
        _, _, descent = self.plotArea.ax_resid.yaxis.label._get_layout(self.plotArea.residplot.canvas.renderer)
        if(not (descent > 0)):
          self.plotArea.setAxisFont(value=SAFE_FONT, axis='y', redraw=True, target='resid')
          
        # x ticks
        flag = False
        tickLabels = self.plotArea.ax_resid.get_xticklabels()
        for entry in tickLabels:
          _, _, descent = entry._get_layout(self.plotArea.residplot.canvas.renderer)
          if(not (descent > 0)):
            flag = True
        if(flag):
          self.plotArea.setTickFont(value=SAFE_FONT, axis='x', redraw=True, target='resid')
          
        # y ticks
        flag = False
        tickLabels = self.plotArea.ax_resid.get_yticklabels()
        for entry in tickLabels:
          _, _, descent = entry._get_layout(self.plotArea.residplot.canvas.renderer)
          if(not (descent > 0)):
            flag = True
        if(flag):
          self.plotArea.setTickFont(value=SAFE_FONT, axis='y', redraw=True, target='resid')

      # check version of save file
      if('VERSION' in settings):
        if(len(settings['VERSION'])):
          savedVersion = settings['VERSION'][0].split('.')
          try:
            savedVersion = int(savedVersion[-1])
            currVersion = int(VERSION.split('.')[-1])
            if(savedVersion > currVersion):
              self.statusbar.showMessage('Fyi, state file was generated in a later program version (0.' + str(savedVersion) + ') than the current one (' + VERSION + ').', self.STATUS_TIME, color='blue')
          except:
            pass
          
      # store name of state file for recent state list
      if(appendState):
        # loop over list and remove previous occurrence of this entry
        index = len(RECENTSTATES) - 1
        for entry in RECENTSTATES[::-1]:
          if(entry == filename):
            RECENTSTATES.pop(index)
          index -= 1
        RECENTSTATES.insert(0, filename)
        # restrict to 20 entries
        RECENTSTATES = RECENTSTATES[:20]

      # restore original pointer
      QtWidgets.QApplication.restoreOverrideCursor()

  def saveState(self, errorstring=''):
    global REMEMBERDIR
    global RECENTSTATES
    # exports current settings and data
    filter_options = ['State file (*.statez *.state)', 'All files (*.*)']
    filterstring = ';;'.join(filter_options)
    # get save file name
    usedir = REMEMBERDIR
    if(self.currStateFile != None):
      usedir = self.currStateFile
    filename, filter_ = QtWidgets.QFileDialog.getSaveFileName(self.centralwidget, filter=filterstring, directory=usedir, caption='Save State')
    filename = str(filename)
    if(PATH_SEPARATOR in filename):
      REMEMBERDIR = filename.split(PATH_SEPARATOR)[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)
    elif('/' in filename):
      REMEMBERDIR = filename.split('/')[:-1]
      REMEMBERDIR = PATH_SEPARATOR.join(REMEMBERDIR)

    if(filename != ''):
      # use StringIO to write memory file
      memoryFile = io.StringIO()
      # save version info
      memoryFile.write('<VERSION>\n' + VERSION + '\n</VERSION>\n\n')
      # do we have an error string to report
      if(len(errorstring)):
        memoryFile.write('<ERROR>\n' + errorstring + '\n</ERROR>\n\n')
      # process canvas
      memoryFile.write('<CANVAS>\n')
      settings = self.plotArea.reportState()
      for key in settings:
        # switch to __str__ as ast.literal_eval does not handle numpy __repr__ serialization
        # correctly as of recent (Python 3.13)
        # but mind you, this will fail for numpy items within lists etc.
        if(type(settings[key]) is str):
          red = '>>>' + key + '\n' + repr(settings[key]) + '\n'
        else:
          red = '>>>' + key + '\n' + str(settings[key]) + '\n'
        memoryFile.write(red)
      memoryFile.write('</CANVAS>\n')
      
      # process objects tab
      memoryFile.write('\n<OBJECTS>\n')
      objects = self.objectsarea.reportState()
      memoryFile.write(objects)
      memoryFile.write('</OBJECTS>\n')
      
      # process graphics tab
      memoryFile.write('\n<GRAPHICS>\n')
      settings = self.graphicsarea.processStyleSet(operation='save', modus='string')
      memoryFile.write(settings)
      memoryFile.write('</GRAPHICS>\n')
      
      # process data area
      memoryFile.write('\n<IMPORTTABLE>\n')
      dataTable = self.dataarea.reportState()
      memoryFile.write(repr(dataTable) + '\n')
      #memoryFile.write(str(dataTable) + '\n')
      memoryFile.write('</IMPORTTABLE>\n')
      
      # process global tab
      memoryFile.write('\n<GLOBAL>\n')
      globalstate = self.globalarea.reportState()
      memoryFile.write(globalstate)
      memoryFile.write('</GLOBAL>\n')
      
      # write all data and fit objects
      for index, entry in enumerate(self.data):
        memoryFile.write('\n<DATA_' + str(index) + '>\n')
        data = entry.reportState()
        data = self.replaceInf(repr(data))
        #data = self.replaceInf(str(data))
        memoryFile.write(data + '\n')
        memoryFile.write('</DATA_' + str(index) + '>\n')
  
      for index, entry in enumerate(self.fit):
        memoryFile.write('\n<CURVE_' + str(index) + '>\n')
        curve = entry.reportState()
        curve = self.replaceInf(repr(curve))
        #curve = self.replaceInf(str(curve))
        memoryFile.write(curve + '\n')
        memoryFile.write('</CURVE_' + str(index) + '>\n')
  
      # write all extra objects
      for index, entry in enumerate(self.extras):
        memoryFile.write('\n<EXTRAS_' + str(index) + '>\n')
        extras = repr(entry.reportState())
        memoryFile.write(extras + '\n')
        memoryFile.write('</EXTRAS_' + str(index) + '>\n')
    
      # write advanced settings
      memoryFile.write('\n<ADVANCED>\n')
      for entry in self.graphicsarea.advancedExport:
        memoryFile.write('>>>' + entry + '\n')
        if(type(self.graphicsarea.advancedExport[entry]) is str):
          memoryFile.write(repr(self.graphicsarea.advancedExport[entry]) + '\n')
        else:
          memoryFile.write(str(self.graphicsarea.advancedExport[entry]) + '\n')
      memoryFile.write('</ADVANCED>\n')
    
      # write advanced settings
      memoryFile.write('\n<ADVANCED_MIN>\n')
      for entry in self.fitarea.advancedMinimization:
        memoryFile.write('>>>' + entry + '\n')
        if(type(self.fitarea.advancedMinimization[entry]) is str):
          memoryFile.write(repr(self.fitarea.advancedMinimization[entry]) + '\n')
        else:
          memoryFile.write(str(self.fitarea.advancedMinimization[entry]) + '\n')
      memoryFile.write('</ADVANCED_MIN>\n')
    
      # write advanced settings
      memoryFile.write('\n<ADVANCED_RANDOM>\n')
      for entry in self.fitarea.advancedRandomSearch:
        memoryFile.write('>>>' + entry + '\n')
        if(type(self.fitarea.advancedRandomSearch[entry]) is str):
          memoryFile.write(repr(self.fitarea.advancedRandomSearch[entry]) + '\n')
        else:
          memoryFile.write(str(self.fitarea.advancedRandomSearch[entry]) + '\n')
      memoryFile.write('</ADVANCED_RANDOM>\n')
  
      # prepare file output
      success = False
      if(self.graphicsarea.advancedExport['stateFileFormat'] in ['both', 'state']):
        # save regular state file
        try:
          if(filename.endswith('.state')):
            writehandle = open(filename, 'w', encoding='utf-8')
          elif(filename.endswith('.statez')):
            writehandle = open(filename[:-1], 'w', encoding='utf-8')
          else:
            writehandle = open(filename + '.state', 'w', encoding='utf-8')
          writehandle.write(memoryFile.getvalue())
        except:
          self.statusbar.showMessage('Cannot save state file ' + filename, self.STATUS_TIME)
        else:
          writehandle.close()
          success = True
        
      if(self.graphicsarea.advancedExport['stateFileFormat'] in ['both', 'statez']):
        # now zip this file
        try:
          if(filename.endswith('.state')):
            ziphandle = zipfile.ZipFile(filename + 'z', 'w', zipfile.ZIP_DEFLATED)
          elif(filename.endswith('.statez')):
            ziphandle = zipfile.ZipFile(filename, 'w', zipfile.ZIP_DEFLATED)
          else:
            ziphandle = zipfile.ZipFile(filename + '.statez', 'w', zipfile.ZIP_DEFLATED)
          if(PATH_SEPARATOR in filename):
            storeName = filename.split(PATH_SEPARATOR)[-1]
          else:
            storeName = filename.split('/')[-1]
          storeName = storeName.replace('.statez', '.state')
          ziphandle.writestr(storeName, memoryFile.getvalue())
        except:
          self.statusbar.showMessage('Cannot save state file ' + filename, self.STATUS_TIME)
        else:
          ziphandle.close()
          success = True
        
      if(success):
        # store name of state file for recent state list
        # loop over list and remove previous occurrence of this entry
        index = len(RECENTSTATES) - 1
        for entry in RECENTSTATES[::-1]:
          if(entry == filename):
            RECENTSTATES.pop(index)
          index -= 1
        RECENTSTATES.insert(0, filename)
        # restrict to 20 entries
        RECENTSTATES = RECENTSTATES[:20]

        # save filename for future reference
        self.currStateFile = filename
        # update window title if successful
        self.MainWindow.setWindowTitle('Fit-o-mat v' + VERSION + ' -- ' + filename)

  def replaceInf(self, literal):
    # function to replace any 'inf' by 'np.inf' (otherwise data cannot be loaded again)
    literal = literal.replace('-inf]', '\'-np.inf\']')
    literal = literal.replace('-inf,', '\'-np.inf\',')
    literal = literal.replace('inf]', '\'np.inf\']')
    literal = literal.replace('inf,', '\'np.inf\',')
    # and replace nan
    literal = literal.replace('nan]', '\'np.nan\']')
    literal = literal.replace('nan,', '\'np.nan\',')
    return literal
    
  def showHelp(self):
    # display help file in browser
    helpFile = WORKINGDIR + PATH_SEPARATOR + 'manual' + PATH_SEPARATOR + 'Fit-o-mat.html'
    try:
      readhandle = open(helpFile, 'r', encoding='utf-8')
      readhandle.close()
    except:
      self.statusbar.showMessage('Cannot locate local copy of help files, redirecting to homepage!', self.STATUS_TIME)
      helpFile = 'http://www.moeglich.uni-bayreuth.de/en/fit-o-mat'
    webbrowser.open(helpFile)

  def aboutInfo(self):
    # display information on canvas
    self.plotArea.drawAboutLogo()
    
  def advancedOption(self, callButton=None):
    # opens menu to access advanced configuration options
    self.menu = OptionMenu(self)
    # apply styles to popup window
    if(QSTYLE != None):
      self.menu.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      self.menu.setStyleSheet(QSTYLESHEET)

    # bottom align position of QMenu
    # first need to display QMenu to get reliable size (even sizeHint fails)
    self.menu.popup(QtGui.QCursor.pos())
    # now move window to new position
    if(callButton != None):
      qPoint = callButton.mapToGlobal(QtCore.QPoint(0, 0))
      menuX, menuY = qPoint.x(), qPoint.y()
      menuX += callButton.geometry().width()
    else:
      menuX, menuY = QtGui.QCursor.pos().x(), QtGui.QCursor.pos().y()

    ###menuX -= self.menu.width() / 2
    menuX -= self.menu.width() / 1.5
    menuY -= self.menu.height()
    menuX, menuY = max(menuX, 0), max(menuY, 0)
    menuPos = QtCore.QPoint(int(menuX), int(menuY))
    self.menu.move(menuPos)

  def displayMessage(self, message=''):
    # displays message contained in state file
    if(len(message)):
      messageText = '\n'.join(message)
      #messageText += '\n<style type="text/css">\nbody {color:blue;}\n</style>\n'
      # open window that displays message
      self.daughterWindow = QtWidgets.QMainWindow()
      self.daughterWindow.setWindowTitle('Message Window')
      
      self.centralwidget = QWidgetMac(self.daughterWindow)
      self.centralwidget.setMinimumSize(QtCore.QSize(int(320 * SCALEFONT), int(240 * SCALEFONT)))
      self.daughterWindow.setCentralWidget(self.centralwidget)
      
      self.vLayout = QtWidgets.QVBoxLayout(self.centralwidget)
      self.vLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter|QtCore.Qt.AlignmentFlag.AlignTop)
      self.vLayout.setContentsMargins(0, 0, 0, 0)

      self.messageField = QtWidgets.QTextBrowser()
      self.messageField.setOpenExternalLinks(True)
      self.messageField.setGeometry(QtCore.QRect(0, 0, int(500 * SCALEFONT), int(600 * SCALEFONT)))
      self.vLayout.addWidget(self.messageField)
      self.messageField.document().setDefaultStyleSheet('body {font-size:' + WIDGET_FONTSIZE + 'pt;}\nh2 {color: rgb(0, 146, 96);}')
      self.messageField.setReadOnly(True)
      self.messageField.setHtml(messageText)
      
      # apply styles and show
      if(QSTYLE != None):
        self.daughterWindow.setStyle(QSTYLE)
      if(QSTYLESHEET != None):
        self.daughterWindow.setStyleSheet(QSTYLESHEET)
      self.daughterWindow.show()
      
      # adjust window position and size
      winWidth, winHeight = self.daughterWindow.geometry().width(), self.MainWindow.geometry().height()
      self.daughterWindow.resize(int(winWidth), int(winHeight))
      winX, winY = self.daughterWindow.geometry().x(), self.MainWindow.geometry().y()
      self.daughterWindow.move(int(winX), int(winY))

  def formatPairedNumbers(self, n1, n2):
    # formats numbers for output while successively increasing precision to make resultant strings different
    NUMBER_SWITCH = 1e3
    DECIMAL_PRECISION = 4
    SCIENTIFIC_PRECISION = 3
    # check whether numbers are real
    real1, real2 = False, False
    if((self.isNumber(n1)) and (np.isfinite(float(n1)))):
      real1 = True
    if((self.isNumber(n2)) and (np.isfinite(float(n2)))):
      real2 = True
    # determine paired numbers
    if(real1 and real2 and (n1 != n2)):
      continueFlag = True
      while(continueFlag):
        # generate current formats
        FORMAT_DECIMAL = '{:.' + str(DECIMAL_PRECISION) + 'f}'
        FORMAT_SCIENTIFIC = '{:.' + str(SCIENTIFIC_PRECISION) + 'e}'
        numberstr = []
        for number in [n1, n2]:
          if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0 / NUMBER_SWITCH)):
            numberstr.append(FORMAT_SCIENTIFIC.format(number))
            zerostr = FORMAT_SCIENTIFIC.format(0.0)
          else:
            numberstr.append(FORMAT_DECIMAL.format(number))
            zerostr = FORMAT_DECIMAL.format(0.0)
          if(numberstr[-1] == zerostr):
            numberstr[-1] = '0'
        # check whether strings are  still equal
        if(numberstr[0] == numberstr[1]):
          DECIMAL_PRECISION += 1
          SCIENTIFIC_PRECISION += 1
        else:
          continueFlag = False
          n1str, n2str = numberstr[0], numberstr[1]
    else:
      n1str, n2str = self.formatNumber(n1), self.formatNumber(n2)

    return n1str, n2str
  
  def getPrecisionFormatPairedNumbers(self, n1, n2):
    # formats numbers for output while successively increasing precision to make resultant strings different
    # returns the required format string rather than the actual formatted numbers
    NUMBER_SWITCH = 1e3
    DECIMAL_PRECISION = 2
    SCIENTIFIC_PRECISION = 1
    # set up some default values in case we have unreal or equal numbers
    FORMAT_DECIMAL = '{:.' + str(DECIMAL_PRECISION) + 'f}'
    FORMAT_SCIENTIFIC = '{:.' + str(SCIENTIFIC_PRECISION) + 'e}'
    # check whether numbers are real
    real1, real2 = False, False
    if((self.isNumber(n1)) and (np.isfinite(float(n1)))):
      real1 = True
    if((self.isNumber(n2)) and (np.isfinite(float(n2)))):
      real2 = True
    # determine paired numbers
    if(real1 and real2 and (n1 != n2)):
      continueFlag = True
      while(continueFlag):
        # generate current formats
        FORMAT_DECIMAL = '{:.' + str(DECIMAL_PRECISION) + 'f}'
        FORMAT_SCIENTIFIC = '{:.' + str(SCIENTIFIC_PRECISION) + 'e}'
        numberstr = []
        for number in [n1, n2]:
          if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0 / NUMBER_SWITCH)):
            numberstr.append(FORMAT_SCIENTIFIC.format(number))
            zerostr = FORMAT_SCIENTIFIC.format(0.0)
          else:
            numberstr.append(FORMAT_DECIMAL.format(number))
            zerostr = FORMAT_DECIMAL.format(0.0)
          if(numberstr[-1] == zerostr):
            numberstr[-1] = '0'
        # check whether strings are  still equal
        if(numberstr[0] == numberstr[1]):
          DECIMAL_PRECISION += 1
          SCIENTIFIC_PRECISION += 1
        else:
          continueFlag = False

      # for small numbers use scientific notation
      if((abs(n1) > NUMBER_SWITCH) or (abs(n1) < 1. / NUMBER_SWITCH) or (abs(n2) > NUMBER_SWITCH) or (abs(n2) < 1. / NUMBER_SWITCH)):### or (abs(n1 - n2) < 1. / NUMBER_SWITCH)):
        return FORMAT_SCIENTIFIC

    return FORMAT_DECIMAL
    
  def formatNumber(self, number):
    # formats number for output
    NUMBER_SWITCH = 1e3
    FORMAT_DECIMAL = '{:.4f}'
    FORMAT_SCIENTIFIC = '{:.3e}'
    # determine return string
    if((self.isNumber(number)) and (np.isfinite(float(number)))):
      if((np.abs(number) > NUMBER_SWITCH) or (np.abs(number) < 1.0 / NUMBER_SWITCH)):
        numberstr = FORMAT_SCIENTIFIC.format(number)
        zerostr = FORMAT_SCIENTIFIC.format(0.0)
      else:
        numberstr = FORMAT_DECIMAL.format(number)
        zerostr = FORMAT_DECIMAL.format(0.0)
      if(numberstr == zerostr):
        numberstr = '0'
    else:
      numberstr = str(number)
    
    return numberstr

  def isNumber(self, s):
    # checks whether string is a number
    try:
      float(s)
      return True
    except ValueError:
      pass
   
    try:
      import unicodedata
      unicodedata.numeric(s)
      return True
    except (TypeError, ValueError):
      pass
    
    return False

class MyForm(QtWidgets.QMainWindow):
  def __init__(self, parent=None):
    QtWidgets.QWidget.__init__(self, parent)
    self.ui = Ui_MainWindow()
    self.ui.setupUi(MainWindow=self)

    # enable drop event
    self.setAcceptDrops(True)
    
    # initialize tooltip toggle
    self.toolTipsShow = True
    
  def dropEvent(self, event):
    self.ui.handleDropEvent(event.mimeData())
    event.accept()

  # we must handle dragEnter and dragMove lest we cannot detect the dropEvent
  def dragEnterEvent(self, event):
    event.accept()

  def dragMoveEvent(self, event):
    event.accept()

  def closeEvent(self, event):
    msgBox = QtWidgets.QMessageBox()
    msgBox.setWindowTitle('Fit-o-mat')
    msgBox.setText('Close program?')
    msgBox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No)
    msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Yes)
    msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
    # apply styles and show
    if(QSTYLE != None):
      msgBox.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      msgBox.setStyleSheet(QSTYLESHEET)
    reply = msgBox.exec()
    if (reply == QtWidgets.QMessageBox.StandardButton.Yes):
      # save information for next fit-o-mat run
      SETTINGSDIR = APPDIR + PATH_SEPARATOR + 'fit-o-mat'
      SETTINGSFILE = SETTINGSDIR + PATH_SEPARATOR + '.config'
      if(not exists(SETTINGSDIR)):
        # don't use mkdir as it potentially causes problems
        # makedirs ensures that the intermediate paths are also made
        # (yes, we did test for this but maybe not in the right manner)
        try:
          makedirs(SETTINGSDIR)
        except:
          pass
      if(exists(SETTINGSDIR)):
        try:
          with open(SETTINGSFILE, 'w') as writehandle:
            # preferred graphics format
            writehandle.write('<EXPORT>\n')
            writehandle.write(self.ui.graphicsarea.exportFilter + '\n')
            # current directories
            writehandle.write('<WORKDIR>\n')
            writehandle.write(REMEMBERDIR + '\n')
            # recent data files
            writehandle.write('<RECENTFILES>\n')
            MAX_ENTRIES = 20
            for entry in RECENTFILES[-1:-MAX_ENTRIES - 1:-1]:
              writehandle.write(entry[0] + ', ' + str(entry[1]) + '\n')
            # recent state files
            writehandle.write('<RECENTSTATES>\n')
            MAX_ENTRIES = 20
            for entry in RECENTSTATES[-1:-MAX_ENTRIES - 1:-1]:
              writehandle.write(entry + '\n')
        except:
          # silent
          pass
      
      # check whether message window exists and close it
      if(hasattr(self.ui, 'daughterWindow')):
        self.ui.daughterWindow.close()
      # check whether preview window exists and close it
      if(hasattr(self.ui, 'previewWindow')):
        self.ui.previewWindow.close()
      # check whether covar window exists and close it
      if(hasattr(self.ui, 'covarWindow')):
        self.ui.covarWindow.close()
      event.accept()
    else:
      event.ignore()

  def keyPressEvent(self, event):
    if event.matches(QtGui.QKeySequence.StandardKey.Save):
      # trigger save state on CTRL-S
      self.ui.saveState(errorstring='')
    elif event.matches(QtGui.QKeySequence.StandardKey.Open):
      # trigger load state on CTRL-O
      self.ui.loadState()
    elif event.matches(QtGui.QKeySequence.StandardKey.HelpContents):
      # trigger help
      self.ui.showHelp()
    elif event.matches(QtGui.QKeySequence.StandardKey.Print):
      # export graphics on CTRL-P
      self.ui.graphicsarea.exportThis()
    elif event.matches(QtGui.QKeySequence.StandardKey.Quit):
      # close program on CTRL-Q
      self.close()
    elif event.matches(QtGui.QKeySequence.StandardKey.Italic):
      # hijack control-I to load new data file and switch to data tab
      retv = self.ui.dataarea.loadData()
      if(retv):
        self.ui.tabWidget.setCurrentIndex(0)
    elif event.matches(QtGui.QKeySequence.StandardKey.ZoomIn):
      # zoom canvas
      self.ui.plotArea.incZoomLevel(increment=-1)
    elif event.matches(QtGui.QKeySequence.StandardKey.ZoomOut):
      # zoom canvas
      self.ui.plotArea.incZoomLevel(increment=1)
    elif event.matches(QtGui.QKeySequence.StandardKey.Find):
      # fit data and switch to fit tab
      retv = self.ui.fitarea.doFit()
      if(retv):
        self.ui.tabWidget.setCurrentIndex(1)
    elif event.matches(QtGui.QKeySequence.StandardKey.New):
      # fit data and switch to fit tab
      retv = self.ui.fitarea.doMin()
      if(retv):
        self.ui.tabWidget.setCurrentIndex(1)
    elif event.matches(QtGui.QKeySequence.StandardKey.Refresh):
      # display preview window
      self.ui.graphicsarea.previewThis()
    elif event.matches(QtGui.QKeySequence.StandardKey.FindNext):
      # generate report
      self.ui.graphicsarea.reportThis()

def switchTab(index=0):
  # switches to target tab (handler for keyboard shortcut)
  if(0 <= index < myapp.ui.tabWidget.count()):
    myapp.ui.tabWidget.setCurrentIndex(index)

def criticalFailure(exctype, value, traceback):
  # global except hook for uncaught exceptions as a last ressort
  # => allow dumping of state file to salvage bits
  # determine error information to print and save in file
  errorstring = repr(value)
  if(errorstring.startswith('ValueError(\'zero-size array')):
    # catch and ignore sporadic recursive repaint error
    # this is but a fudge, we should at some point pinpoint and weed out this error
    pass
  else:
    try:
      errorstring = repr(traceback.tb_next.tb_lineno) + '\t' + errorstring
    except:
      pass
  
    # display final dialogue
    msgBox = QtWidgets.QMessageBox()
    msgBox.setWindowTitle('Critical failure Fit-o-mat')
    text = '<span style="white-space:nowrap;text-align:left;"><p>Oooops, a fatal error occurred.'
    text += '\n<br/>It is advised to terminate the program.'
    text += '\n<br/>(You can also ignore the failure but it\'s not recommended.)</p>'
    ###text += '\n<br/>\n<br/>'
    text += '<p><b>\u2192 You should save state file NOW! \u2190</b>'
    usestring = 'version ' + VERSION + '\n' + errorstring.replace('"', '&quot;')
    text += '\n<br/>(Ideally, then send state file to <a href="mailto:andreas.moeglich@uni-bayreuth.de?subject=[Fit-o-mat -- Bug Report]&body=' + usestring + '">A.M.</a> for review.)</p>'
    # add dummy line to prevent layout desaster in QMessageBox :(
    ###text += '\n<br/></span>'
    text += '\n</span>'
    msgBox.setText(text)
    msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText)
    msgBox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Save | QtWidgets.QMessageBox.StandardButton.Abort | QtWidgets.QMessageBox.StandardButton.Ignore)
    msgBox.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Save)
    msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
    # apply styles and show
    if(QSTYLE != None):
      msgBox.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      msgBox.setStyleSheet(QSTYLESHEET)
      
    # get user click
    reply = ''
    while(reply not in [QtWidgets.QMessageBox.StandardButton.Abort, QtWidgets.QMessageBox.StandardButton.Ignore]):
      reply = msgBox.exec()
      if (reply == QtWidgets.QMessageBox.StandardButton.Save):
        myapp.ui.saveState(errorstring=errorstring)
  
    if(reply == QtWidgets.QMessageBox.StandardButton.Ignore):
      myapp.ui.statusbar.showMessage('Warning -- program may run unstably after ignoring critical failure!', myapp.ui.STATUS_TIME)
    else:
      # kill the program
      print('This is what killed me in the end ...\n', errorstring)
      
      # execute original except hook
      originalhook(exctype, value, traceback)
      # exit if still running
      sys.exit(1)

def setColorScheme(value='default', caller=None, initOnly=False):
  # allows change of Fit-o-mat color scheme
  # make the variables global
  global QSTYLE, DARK_MODE, PALIM, QSTYLESHEET
  global UI_BASE_COLOR, UI_ALTERNATE_BASE_COLOR, UI_TEXT_COLOR, UI_BRIGHT_COLOR, UI_NOTIFICATION_COLOR, UI_ALERT_COLOR
  global UI_BASE_STRING, UI_ALTERNATE_BASE_STRING, UI_TEXT_STRING, UI_BRIGHT_STRING
  global UI_STYLE
  
  UI_STYLE = value
  QSTYLE, DARK_MODE, PALIM = setQStyle(value=value)
  try:
    UI_BASE_COLOR = PALIM.color(QtGui.QPalette.ColorRole.Base).getRgb()
    UI_ALTERNATE_BASE_COLOR = PALIM.color(QtGui.QPalette.ColorRole.AlternateBase).getRgb()
    UI_TEXT_COLOR = PALIM.color(QtGui.QPalette.ColorRole.WindowText).getRgb()
    UI_BRIGHT_COLOR = PALIM.color(QtGui.QPalette.ColorRole.BrightText).getRgb()
  except:
    UI_BASE_COLOR, UI_ALTERNATE_BASE_COLOR, UI_TEXT_COLOR, UI_BRIGHT_COLOR = (255, 255, 255, 255), (247, 247, 247, 255), (0, 0, 0, 255), (255, 255, 255, 255)
  UI_BASE_STRING, UI_ALTERNATE_BASE_STRING, UI_TEXT_STRING, UI_BRIGHT_STRING = str(UI_BASE_COLOR), str(UI_ALTERNATE_BASE_COLOR), str(UI_TEXT_COLOR), str(UI_BRIGHT_COLOR)
  if(DARK_MODE):
    UI_NOTIFICATION_COLOR, UI_ALERT_COLOR = (180, 180, 255, 255), (221, 85, 102, 255)
  else:
    UI_NOTIFICATION_COLOR, UI_ALERT_COLOR = (0, 0, 255, 255), (255, 0, 0, 255)

  if(value == 'default'):
    UI_BRIGHT_COLOR, UI_BRIGHT_STRING = (160, 160, 160, 255), '(160, 160, 160, 255)'
  
  QSTYLESHEET = setQStyleSheet(value=value, PALIM=PALIM)

  # and now apply all this
  if(not initOnly):
    subWindows = ['daughterWindow', 'previewWindow', 'covarWindow']
    if(QSTYLE != None):
      myapp.setStyle(QSTYLE)
      myapp.ui.fitarea.fitResults.updateStyle()
      myapp.ui.globalarea.globalFitResults.updateStyle()
      myapp.ui.fitarea.fitFormula.updateStyle()
      if(caller != None):
        caller.setStyle(QSTYLE)
      for window in subWindows:
        if(hasattr(myapp.ui, window) and hasattr(myapp.ui.__dict__[window], 'setStyle')):
          myapp.ui.__dict__[window].setStyle(QSTYLE)
  
    if(QSTYLESHEET != None):
      myapp.setStyleSheet(QSTYLESHEET)
      if(caller != None):
        caller.setStyleSheet(QSTYLESHEET)
      for window in subWindows:
        if(hasattr(myapp.ui, window) and hasattr(myapp.ui.__dict__[window], 'setStyleSheet')):
          myapp.ui.__dict__[window].setStyleSheet(QSTYLESHEET)

def setQStyle(value='default'):
  # function to generate QSTYLE on the fly
  QSTYLE, DARK_MODE, PALIM = None, False, None
  
  # generate Fusion style
  QSTYLE = QtWidgets.QStyleFactory.create('Fusion')

  # modify tyle as needed and set DARK_MODE
  if(hasattr(QSTYLE, 'standardPalette')):
    PALIM = QSTYLE.standardPalette()
    if(value == 'dark'):
      PALIM.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor(53, 53, 53))
      PALIM.setColor(QtGui.QPalette.ColorRole.WindowText, QtCore.Qt.GlobalColor.white)
      PALIM.setColor(QtGui.QPalette.ColorRole.Base, QtGui.QColor(25, 25, 25))
      PALIM.setColor(QtGui.QPalette.ColorRole.AlternateBase, QtGui.QColor(53, 53, 53))
      PALIM.setColor(QtGui.QPalette.ColorRole.BrightText, QtGui.QColor(200, 200, 200))
    elif(value == 'light'):
      PALIM.setColor(QtGui.QPalette.ColorRole.Window, QtCore.Qt.GlobalColor.white)
      PALIM.setColor(QtGui.QPalette.ColorRole.WindowText, QtCore.Qt.GlobalColor.black)
      PALIM.setColor(QtGui.QPalette.ColorRole.Base, QtCore.Qt.GlobalColor.white)
      PALIM.setColor(QtGui.QPalette.ColorRole.AlternateBase, QtGui.QColor(245, 245, 245))
      PALIM.setColor(QtGui.QPalette.ColorRole.BrightText, QtGui.QColor(190, 190, 190))
    elif(value == 'sad'):
      PALIM.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor(53, 53, 153))
      PALIM.setColor(QtGui.QPalette.ColorRole.WindowText, QtGui.QColor(193, 193, 255))
      PALIM.setColor(QtGui.QPalette.ColorRole.Base, QtGui.QColor(25, 25, 125))
      PALIM.setColor(QtGui.QPalette.ColorRole.AlternateBase, QtGui.QColor(43, 43, 163))
      PALIM.setColor(QtGui.QPalette.ColorRole.BrightText, QtGui.QColor(143, 143, 215))
    elif(value == 'tranquil'):
      # puh, this looks superugly, disable for the time being
      PALIM.setColor(QtGui.QPalette.ColorRole.Window, QtGui.QColor(23, 123, 23))
      PALIM.setColor(QtGui.QPalette.ColorRole.WindowText, QtGui.QColor(193, 255, 193))
      PALIM.setColor(QtGui.QPalette.ColorRole.Base, QtGui.QColor(5, 105, 5))
      PALIM.setColor(QtGui.QPalette.ColorRole.AlternateBase, QtGui.QColor(23, 143, 23))
      PALIM.setColor(QtGui.QPalette.ColorRole.BrightText, QtGui.QColor(143, 215, 143))
    
    if(sum(PALIM.color(QtGui.QPalette.ColorRole.Base).getRgb()[0:3]) < 1.5 * 255):
      DARK_MODE = True

  return QSTYLE, DARK_MODE, PALIM

def setQStyleSheet(value='default', PALIM=None):
  # function to generate QSTYLESHEET on the fly
  # parse palette
  try:
    UI_BASE_COLOR = PALIM.color(QtGui.QPalette.ColorRole.Base).getRgb()
    UI_ALTERNATE_BASE_COLOR = PALIM.color(QtGui.QPalette.ColorRole.AlternateBase).getRgb()
    UI_TEXT_COLOR = PALIM.color(QtGui.QPalette.ColorRole.WindowText).getRgb()
    UI_BRIGHT_COLOR = PALIM.color(QtGui.QPalette.ColorRole.BrightText).getRgb()
  except:
    UI_BASE_COLOR, UI_ALTERNATE_BASE_COLOR, UI_TEXT_COLOR, UI_BRIGHT_COLOR = (255, 255, 255, 255), (247, 247, 247, 255), (0, 0, 0, 255), (255, 255, 255, 255)
  UI_BASE_STRING, UI_ALTERNATE_BASE_STRING, UI_TEXT_STRING, UI_BRIGHT_STRING = str(UI_BASE_COLOR), str(UI_ALTERNATE_BASE_COLOR), str(UI_TEXT_COLOR), str(UI_BRIGHT_COLOR)
  # prepare QSTYLESHEET
  if(DARK_MODE):
    BLU_STRING = 'rgba(180, 180, 255, 255)'
  else:
    BLU_STRING = 'blue'
  if(value == 'default'):
    UI_BRIGHT_COLOR, UI_BRIGHT_STRING = (160, 160, 160, 255), '(160, 160, 160, 255)'
  qStyleSheet = {'QTableView::item:focus': '{background-color: rgba' + UI_BASE_STRING + '; color: rgba' + UI_TEXT_STRING + ';}',
                 'QTableView::item:selected': '{background-color: #0078d7; color: white;}',
                 'QTableView::item:focus:selected': '{background-color: rgba' + UI_BASE_STRING + '; color: rgba' + UI_TEXT_STRING + ';}',
                 'QTableView::item:!active:selected': '{background-color: #7799ee; color: white;}',
                 'QTableView': '{background-color: rgba' + UI_ALTERNATE_BASE_STRING + ';}',
                 'QTableView#data QTableCornerButton': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #b0aae8, stop: 0.3 #9288e8, stop: 0.8 #9288e8, stop: 1.0 #8077e8);}',
                 'QWidget': '{font-size:' + WIDGET_FONTSIZE + 'pt;}',
                 'QComboBox:hover': '{color: #6258b8;}',
                 'QToolButton:hover': '{color: #6258b8;}',
                 'QPushButton:hover': '{color: #6258b8;}',
                 'QPushButton:checked': '{background-color: #7066ff; color: black;}',
                 'QPushButton:checked:hover': '{background-color: #9088ff; color: white;}',
                 'QPushButton:checked:!enabled': '{background-color: #c0bbff; color: #787878;}',
                 'QPushButton#listos': '{margin-left: 2px; margin-top: 2px;}',
                 'QTabBar::tab:!selected:hover': '{color: #6258b8;}',
                 'QTabBar::tab:selected': '{background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #b0aae8, stop: 0.3 #9288e8, stop: 0.8 #9288e8, stop: 1.0 #8077e8);}',
                 'QTabBar::tab:selected:hover': '{color: white;}',
                 'QLabel#advanced': '{color: rgba' + UI_BRIGHT_STRING + ';}',
                 'QRadioButton#listos::indicator': '{subcontrol-position: center;}',
                 'QLineEdit': '{border-width: 1px; border-color: #b1b1b1; border-style: solid; border-radius: 4px;}',
                 'QGroupBox': '{border-style: none;}',
                 'QToolTip': '{font-size: ' + WIDGET_FONTSIZE + 'pt; color:black; background-color: #ffffe1; padding: 2px;}',
                 '#global': '{color: ' + BLU_STRING + ';}',
                 'QPushButton#leftalign': '{text-align: left; padding-left: 5px; padding-top: 2px;}',
                 'QTreeView': '{alternate-background-color: rgba' + UI_ALTERNATE_BASE_STRING + ';}',
                 'QTreeView::item:hover': '{background-color: #0078d7; color: white;}',
                 'QTableWidget': '{background-color: rgba' + UI_ALTERNATE_BASE_STRING + ';}',
                 '#mini': '{font-size: ' + str(.3 * BASE_SIZE) + 'pt; min-height: ' + str(int(0.75 * BASE_SIZE - 1)) + 'px; }'
                }

  if(platform == 'darwin'):
    # special treatment for OS X (native style is just killing me)
    # set style sheet to approx. retain correct look
    # there still seems to be a problem with dark theme on OSX
    # => try to solve by hard-coding colors for widget
    updateSheet = {'QComboBox': '{font-size: ' + WIDGET_FONTSIZE + 'pt; margin: 0px; padding-left: 3px;}',
                   'QWidget': '{font-size: ' + WIDGET_FONTSIZE + 'pt;  color: rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_BASE_STRING + ';margin: 0px; padding: 0px;}',
                   'QRadioButton': '{font-size: ' + WIDGET_FONTSIZE + 'pt; margin: 0px; padding: 0px; spacing: 3px;}',
                   'QRadioButton::indicator': '{width: 12px; height: 12px;}',
                   'QTableView#data': 'QTableCornerButton {background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #b0aae8, stop: 0.3 #9288e8, stop: 0.8 #9288e8, stop: 1.0 #8077e8);}',
                   'QToolTip': '{color:black; font-size: ' + WIDGET_FONTSIZE + 'pt;}',
                   'QGroupBox': '{font-size: ' + WIDGET_FONTSIZE + 'pt; margin: 0px; border-style: none;}'
                  }
    if(value != 'default'):
      nodefSheet = {'QWidget': '{font-size:' + WIDGET_FONTSIZE + 'pt;color: rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_BASE_STRING + '; margin: 0px; padding: 0px;}',
                  'QComboBox': '{border: 1px solid rgba' + UI_TEXT_STRING + '; border-radius: 2px; margin: 0px; padding-left: 3px;}'
                  }
      updateSheet.update(nodefSheet)
  else:
    updateSheet = {
                }
  
  # adjust further for non-default styles
  if(value == 'default'):
    extraSheet = {}
  else:
    if(DARK_MODE):
      TOP_COLOR = UI_TEXT_STRING
    else:
      TOP_COLOR = UI_BRIGHT_STRING
      
    # don't touch QComboBox::item:selected or QComboBox::indicator as Fusion style then completely craps up -- all praise to qss ... phew
    # same shoot for QRadioButton which on top are drawn as images?! ... this is so 80's
    extraSheet = {'QWidget': '{font-size:' + WIDGET_FONTSIZE + 'pt;color: rgba' + UI_TEXT_STRING + '; background-color: rgba' + UI_BASE_STRING + ';}',
                  'QComboBox': '{border: 1px solid rgba' + UI_TEXT_STRING + '; border-radius: 2px;}',
                  'QComboBox::item': '{selection-background-color: #0078d7;}',
                  'QToolButton': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba' + TOP_COLOR + ', stop: 0.1 rgba' + UI_BASE_STRING + ', stop: 0.9 rgba' + UI_BASE_STRING + ', stop: 1.0 rgba' + UI_BASE_STRING + ');\
                                    min-height: ' + str(int(BASE_SIZE - 1)) + 'px; padding-left: 4px; padding-right: 4px;\
                                    border: 1px solid rgba' + UI_TEXT_STRING + '; border-radius: 2px;}',
                  'QToolButton:hover': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba' + TOP_COLOR + ', stop: 0.1 rgba' + UI_BRIGHT_STRING + ', stop: 0.9 rgba' + UI_BRIGHT_STRING + ', stop: 1.0 rgba' + UI_BASE_STRING + ');}',
                  'QToolButton::menu-button': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba' + TOP_COLOR + ', stop: 0.1 rgba' + UI_BASE_STRING + ', stop: 0.9 rgba' + UI_BASE_STRING + ', stop: 1.0 rgba' + UI_BASE_STRING + ');\
                                    border: 1px solid rgba' + UI_TEXT_STRING + '; border-radius: 2px;}',
                  'QToolButton::menu-button:hover': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba' + TOP_COLOR + ', stop: 0.1 rgba' + UI_BRIGHT_STRING + ', stop: 0.9 rgba' + UI_BRIGHT_STRING + ', stop: 1.0 rgba' + UI_BASE_STRING + ');}',
                  'QPushButton': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba' + TOP_COLOR + ', stop: 0.1 rgba' + UI_BASE_STRING + ', stop: 0.9 rgba' + UI_BASE_STRING + ', stop: 1.0 rgba' + UI_BASE_STRING + ');\
                                    min-height: ' + str(int(BASE_SIZE - 1)) + 'px; padding-left: 4px; padding-right: 4px;\
                                    border: 1px solid rgba' + UI_TEXT_STRING + '; border-radius: 2px;}',
                  'QPushButton:hover': '{background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 rgba' + TOP_COLOR + ', stop: 0.1 rgba' + UI_BRIGHT_STRING + ', stop: 0.9 rgba' + UI_BRIGHT_STRING + ', stop: 1.0 rgba' + UI_BASE_STRING + ');}',
                  'QPushButton:!enabled': '{color: rgba' + UI_BRIGHT_STRING + ';}',
                  '#hdivide': '{border-bottom: 2px solid rgba' + UI_BRIGHT_STRING + ';}',
                  '#vdivide': '{border-right: 2px solid rgba' + UI_BRIGHT_STRING + ';}',
                  '#mini': '{font-size: ' + str(.3 * BASE_SIZE) + 'pt; min-height: ' + str(int(0.75 * BASE_SIZE - 1)) + 'px; padding-left: 1px; padding-right: 1px;}'
                 }

  qStyleSheet.update(extraSheet)
  qStyleSheet.update(updateSheet)
  QSTYLESHEET = [i + ' ' + j for i, j in qStyleSheet.items()]
  QSTYLESHEET = '\n'.join(QSTYLESHEET)
  
  return QSTYLESHEET
  
if __name__ ==  "__main__":
  # suppress warnings (e.g. missing font glyph)
  # missing glyphs still produce output to stdout even though warning is suppressed
  # short of suppressing redirecting entire stdout, not easy to suppress => live with it
  #warnings.simplefilter('ignore')
  # move to very top of program
  
  # determine default font
  try:
    ffamily = matplotlib.rcParams['font.family']
    fontList = matplotlib.rcParams['font.' + ffamily[0]]
    SAFE_FONT = fontList[0]
  except:
    SAFE_FONT = 'DejaVu Sans'
  
  # define custom markers
  CUSTOM_MARKERS = {'dag': '$\u2020$', 'ddag': '$\u2021$', 'spade': '$\u2664$', 'fllspd': '$\\spadesuit$', 'heart': '$\u2661$', 'fllhrt': '$\u2665$', 'club': '$\u2667$', 'fllclb': '$\\clubsuit$',
                    'diamond': '$\\diamondsuit$', 'flldmnd': '$\u2666$', 'oslash': '$\\oslash$', 'ocross': '$\u2297$', 'owheel': '$\u229b$', 'xdot': '$\u203b$', '+dot': '$\u205c$',
                    'female': '$\u2640$', 'male': '$\u2642$', 'ostar': '$\u272a$', 'malt': '$\u2720$', 'ankh': '$\u2625$', 'smile': '$\u263b$',
                    'skull': '$\u2620$', 'biohzd': '$\u2623$', 'flag': '$\u2691$', 'target': '$\u2316$',
                    'benzene': '$\u232c$'}
  # parsed with svgpath2mpl (thanks!!)
  pac = matplotlib.path.Path([[ -4.57334163e-07,   1.00000000e+00],
       [ -1.31319357e-01,   1.00000045e+00], [ -2.61361423e-01,   9.74133816e-01], [ -3.82684344e-01,   9.23880437e-01], [ -5.04007266e-01,   8.73627058e-01],
       [ -6.14251596e-01,   7.99964248e-01], [ -7.07108048e-01,   7.07107732e-01], [ -7.99964500e-01,   6.14251215e-01], [ -8.73627234e-01,   5.04006835e-01],
       [ -9.23880529e-01,   3.82683879e-01], [ -9.74133825e-01,   2.61360922e-01], [ -1.00000037e+00,   1.31318839e-01], [ -9.99999828e-01,  -6.09627091e-08],
       [ -1.00000030e+00,  -1.31318912e-01], [ -9.74133693e-01,  -2.61360934e-01], [ -9.23880364e-01,  -3.82683824e-01], [ -8.73627035e-01,  -5.04006714e-01],
       [ -7.99964290e-01,  -6.14251026e-01], [ -7.07107845e-01,  -7.07107482e-01], [ -6.14251400e-01,  -7.99963938e-01], [ -5.04007096e-01,  -8.73626696e-01],
       [ -3.82684212e-01,  -9.23880039e-01], [ -2.61361327e-01,  -9.74133382e-01], [ -1.31319309e-01,  -1.00000000e+00], [ -4.57327544e-07,  -9.99999547e-01],
       [  1.67377288e-01,  -9.99798760e-01], [  3.32056481e-01,  -9.57577983e-01], [  4.78883935e-01,  -8.77222221e-01], [  6.25711390e-01,  -7.96866459e-01],
       [  7.50041091e-01,  -6.80918384e-01], [  8.40432667e-01,  -5.40047121e-01], [  2.30754229e-01,  -6.53483713e-01], [ -1.62094774e-01,  -2.70024166e-01],
       [ -1.47296937e-01,  -1.77718060e-01], [ -1.27785774e-01,  -2.16696348e-01], [  5.63238397e-01,  -2.84483501e-01], [  9.93759973e-01,  -1.08050406e-01],
       [  9.99999285e-01,   1.31318824e-01], [  9.74132743e-01,   2.61360894e-01], [  9.23879459e-01,   3.82683839e-01], [  8.73626174e-01,   5.04006784e-01],
       [  7.99963456e-01,   6.14251157e-01], [  7.07107022e-01,   7.07107671e-01], [  6.14250588e-01,   7.99964185e-01], [  5.04006278e-01,   8.73626998e-01],
       [  3.82683377e-01,   9.23880387e-01], [  2.61360475e-01,   9.74133777e-01], [  1.31318428e-01,   1.00000043e+00], [ -4.57338189e-07,   1.00000000e+00],
       [ -4.57334163e-07,   1.00000000e+00], [  2.61853154e-01,   6.53803236e-01], [  3.17592952e-01,   6.53801526e-01], [  3.71106303e-01,   6.31634574e-01],
       [  4.10520248e-01,   5.92220543e-01], [  4.49934194e-01,   5.52806511e-01], [  4.72101029e-01,   4.99293111e-01], [  4.72102617e-01,   4.43553314e-01],
       [  4.72100876e-01,   3.87813616e-01], [  4.49933974e-01,   3.34300365e-01], [  4.10520045e-01,   2.94886457e-01], [  3.71106116e-01,   2.55472549e-01],
       [  3.17592852e-01,   2.33305677e-01], [  2.61853154e-01,   2.33303967e-01], [  2.06113447e-01,   2.33305661e-01], [  1.52600168e-01,   2.55472527e-01],
       [  1.13186226e-01,   2.94886437e-01], [  7.37722849e-02,   3.34300346e-01], [  5.16053750e-02,   3.87813606e-01], [  5.16036345e-02,   4.43553314e-01],
       [  5.16052226e-02,   4.99293121e-01], [  7.37720652e-02,   5.52806530e-01], [  1.13186023e-01,   5.92220563e-01], [  1.52599981e-01,   6.31634596e-01],
       [  2.06113347e-01,   6.53801542e-01], [  2.61853154e-01,   6.53803236e-01], [  2.61853154e-01,   6.53803236e-01],
       [  2.61853154e-01,   6.53803236e-01]], [ 1,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  1,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  1,  4,  4,  4,  4,  4,  1,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  1, 79,  1,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  2, 79])
  ghost = matplotlib.path.Path([[  1.70144218e-05,   1.00000000e+00],
       [ -1.89252585e-01,   9.99902986e-01], [ -3.71050753e-01,   9.25162344e-01], [ -5.05658763e-01,   7.92107188e-01], [ -6.40266773e-01,   6.59052032e-01],
       [ -7.17111726e-01,   4.78133291e-01], [ -7.19404763e-01,   2.88877558e-01], [ -7.19846940e-01,   2.80170075e-01], [ -7.19846940e-01,  -7.40170066e-01],
       [ -6.70758522e-01,  -8.43356038e-01], [ -6.47629357e-01,  -1.00000000e+00], [ -5.48452382e-01,  -1.00000000e+00], [ -4.00686376e-01,  -1.00000000e+00],
       [ -4.21966629e-01,  -6.51700680e-01], [ -2.74200677e-01,  -6.51700680e-01], [ -1.26434534e-01,  -6.51700680e-01], [ -1.47749007e-01,  -1.00000000e+00],
       [  1.70144218e-05,  -1.00000000e+00], [  1.47783001e-01,  -1.00000000e+00], [  1.26468737e-01,  -6.51700680e-01], [  2.74234688e-01,  -6.51700680e-01],
       [  4.22000710e-01,  -6.51700680e-01], [  4.00686428e-01,  -1.00000000e+00], [  5.48452380e-01,  -1.00000000e+00], [  6.47607832e-01,  -1.00000000e+00],
       [  6.70780639e-01,  -8.43425350e-01], [  7.19846940e-01,  -7.40238089e-01], [  7.19846940e-01,   2.80170075e-01], [  7.17247557e-01,   4.78150916e-01],
       [  6.40388501e-01,   6.59085018e-01], [  5.05758522e-01,   7.92143020e-01], [  3.71128542e-01,   9.25201022e-01], [  1.89304252e-01,   9.99929806e-01],
       [  1.70144218e-05,   1.00000000e+00], [ -2.63928564e-01,   4.27789113e-01], [ -2.12132956e-01,   4.27785942e-01], [ -1.62406707e-01,   4.07186871e-01],
       [ -1.25781682e-01,   3.70561846e-01], [ -8.91566563e-02,   3.33936821e-01], [ -6.85575857e-02,   2.84210571e-01], [ -6.85544141e-02,   2.32414964e-01],
       [ -6.85575857e-02,   1.80619356e-01], [ -8.91566563e-02,   1.30893107e-01], [ -1.25781682e-01,   9.42680815e-02], [ -1.62406707e-01,   5.76430563e-02],
       [ -2.12132956e-01,   3.70439857e-02], [ -2.63928564e-01,   3.70408141e-02], [ -2.89587283e-01,   3.70379165e-02], [ -3.14997058e-01,   4.20894226e-02],
       [ -3.38703605e-01,   5.19062187e-02], [ -3.62410152e-01,   6.17230147e-02], [ -3.83952409e-01,   7.61141761e-02], [ -4.02097444e-01,   9.42560505e-02],
       [ -4.20242478e-01,   1.12397925e-01], [ -4.34637391e-01,   1.33937675e-01], [ -4.44458315e-01,   1.57642513e-01], [ -4.54279240e-01,   1.81347350e-01],
       [ -4.59335171e-01,   2.06756245e-01], [ -4.59336742e-01,   2.32414964e-01], [ -4.59335171e-01,   2.58073683e-01], [ -4.54279240e-01,   2.83482578e-01],
       [ -4.44458315e-01,   3.07187415e-01], [ -4.34637391e-01,   3.30892252e-01], [ -4.20242478e-01,   3.52432003e-01], [ -4.02097444e-01,   3.70573877e-01],
       [ -3.83952409e-01,   3.88715752e-01], [ -3.62410152e-01,   4.03106913e-01], [ -3.38703605e-01,   4.12923709e-01], [ -3.14997058e-01,   4.22740505e-01],
       [ -2.89587283e-01,   4.27792011e-01], [ -2.63928564e-01,   4.27789113e-01], [ -2.63928564e-01,   4.27789113e-01], [  2.63928564e-01,   4.27789113e-01],
       [  2.89587282e-01,   4.27792009e-01], [  3.14997056e-01,   4.22740501e-01], [  3.38703602e-01,   4.12923704e-01], [  3.62410149e-01,   4.03106907e-01],
       [  3.83952405e-01,   3.88715746e-01], [  4.02097439e-01,   3.70573872e-01], [  4.20242472e-01,   3.52431998e-01], [  4.34637385e-01,   3.30892249e-01],
       [  4.44458311e-01,   3.07187412e-01], [  4.54279236e-01,   2.83482576e-01], [  4.59335169e-01,   2.58073682e-01], [  4.59336742e-01,   2.32414964e-01],
       [  4.59335169e-01,   2.06756245e-01], [  4.54279236e-01,   1.81347352e-01], [  4.44458311e-01,   1.57642515e-01], [  4.34637385e-01,   1.33937679e-01],
       [  4.20242472e-01,   1.12397930e-01], [  4.02097439e-01,   9.42560556e-02], [  3.83952405e-01,   7.61141816e-02], [  3.62410149e-01,   6.17230202e-02],
       [  3.38703602e-01,   5.19062233e-02], [  3.14997056e-01,   4.20894265e-02], [  2.89587282e-01,   3.70379188e-02], [  2.63928564e-01,   3.70408141e-02],
       [  2.12132958e-01,   3.70439903e-02], [  1.62406711e-01,   5.76430623e-02], [  1.25781687e-01,   9.42680866e-02], [  8.91566623e-02,   1.30893111e-01],
       [  6.85575903e-02,   1.80619358e-01], [  6.85544141e-02,   2.32414964e-01], [  6.85575903e-02,   2.84210570e-01], [  8.91566623e-02,   3.33936817e-01],
       [  1.25781687e-01,   3.70561841e-01], [  1.62406711e-01,   4.07186865e-01], [  2.12132958e-01,   4.27785937e-01], [  2.63928564e-01,   4.27789113e-01],
       [  2.63928564e-01,   4.27789113e-01]], [ 1,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  2,  4,  4,  4,  4,  4,  1,
        1,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4, 79,  1,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4, 79])
  stark = matplotlib.path.Path([[ -1.35511295e-01,   9.50000000e-01], [ -1.36811295e-01,   8.94370102e-01],
       [ -9.52546968e-02,   8.37967504e-01], [ -3.20347989e-02,   7.81147506e-01], [ -1.67004794e-01,   7.51957507e-01], [ -2.90659690e-01,   7.62264206e-01],
       [ -3.99329587e-01,   8.25034304e-01], [ -3.76789687e-01,   7.74874306e-01], [ -3.36919789e-01,   7.33069007e-01], [ -2.79739791e-01,   6.99629008e-01],
       [ -4.14399786e-01,   6.47529010e-01], [ -5.43710412e-01,   6.46180710e-01], [ -6.67630428e-01,   6.95600608e-01], [ -6.42820429e-01,   6.58430610e-01],
       [ -5.97491480e-01,   6.21839211e-01], [ -5.29671462e-01,   5.85869212e-01], [ -7.03791457e-01,   5.13399215e-01], [ -8.35858732e-01,   4.88556815e-01],
       [ -8.92908760e-01,   5.48676813e-01], [ -8.78188731e-01,   5.07106815e-01], [ -8.15542653e-01,   4.73654916e-01], [ -7.23162686e-01,   4.45214917e-01],
       [ -8.06472683e-01,   4.16554918e-01], [ -1.01339843e+00,   3.41300720e-01], [ -1.10208840e+00,   4.08930818e-01], [ -1.07194845e+00,   3.63080720e-01],
       [ -1.01974628e+00,   3.27157521e-01], [ -9.53846258e-01,   2.97397522e-01], [ -1.01506628e+00,   2.67087523e-01], [ -1.08353948e+00,   2.50791823e-01],
       [ -1.14516945e+00,   2.40971824e-01], [ -1.20841950e+00,   2.30871824e-01], [ -1.26443018e+00,   2.27978524e-01], [ -1.29514021e+00,   2.54858523e-01],
       [ -1.26492023e+00,   2.10818525e-01], [ -1.21511993e+00,   1.77553726e-01], [ -1.14910995e+00,   1.53183727e-01], [ -1.28801993e+00,   5.50436298e-02],
       [ -1.37676990e+00,   5.15390299e-02], [ -1.44160995e+00,   9.31690286e-02], [ -1.37230995e+00,   2.23190309e-02], [ -1.29577013e+00,  -3.60972671e-02],
       [ -1.19660018e+00,  -5.55272665e-02], [ -1.20310013e+00,  -8.33672656e-02], [ -1.22570933e+00,  -7.45198658e-02], [ -1.24496933e+00,  -1.07499965e-01],
       [ -1.21687928e+00,  -1.10309865e-01], [ -1.19010128e+00,  -8.37583655e-02], [ -1.16301128e+00,  -9.04783653e-02], [ -1.20510128e+00,  -1.50648463e-01],
       [ -1.15316175e+00,  -1.98256462e-01], [ -1.21945170e+00,  -2.70976459e-01], [ -1.14806175e+00,  -2.73926459e-01], [ -1.18779375e+00,  -2.40202460e-01],
       [ -1.05332377e+00,  -2.47392460e-01], [ -1.03696381e+00,  -2.48262460e-01], [ -1.02066998e+00,  -2.43962260e-01], [ -9.96399977e-01,  -2.34692261e-01],
       [ -1.04550998e+00,  -3.39052357e-01], [ -9.87130357e-01,  -3.70861356e-01], [ -1.04430038e+00,  -4.71191253e-01], [ -1.03967288e+00,  -4.68811353e-01],
       [ -1.03506751e+00,  -4.66650853e-01], [ -1.03048688e+00,  -4.64672753e-01], [ -9.61777358e-01,  -4.35000654e-01], [ -8.98125380e-01,  -4.48105953e-01],
       [ -8.32894132e-01,  -4.30893454e-01], [ -8.31554102e-01,  -5.02683452e-01], [ -8.34838352e-01,  -5.75716949e-01], [ -8.75008351e-01,  -6.53037047e-01],
       [ -8.32308382e-01,  -6.25711947e-01], [ -7.95311183e-01,  -6.25666347e-01], [ -7.55462485e-01,  -6.25981347e-01], [ -7.50481355e-01,  -6.26021347e-01],
       [ -7.45461535e-01,  -6.26071347e-01], [ -7.40374585e-01,  -6.26071347e-01], [ -7.04765637e-01,  -6.26061347e-01], [ -6.66143178e-01,  -6.23745148e-01],
       [ -6.18792529e-01,  -6.01095748e-01], [ -6.65672558e-01,  -6.76195746e-01], [ -6.06417230e-01,  -7.50835743e-01], [ -6.54637278e-01,  -8.22345741e-01],
       [ -6.52285728e-01,  -8.20978941e-01], [ -6.49934178e-01,  -8.19712841e-01], [ -6.47591408e-01,  -8.18522541e-01], [ -5.74964181e-01,  -7.81622842e-01],
       [ -5.06541233e-01,  -8.26972341e-01], [ -4.42322785e-01,  -7.94586942e-01], [ -4.58982835e-01,  -9.87146935e-01], [ -4.02280087e-01,  -1.09049453e+00],
       [ -3.30350189e-01,  -1.17531453e+00], [ -3.23580189e-01,  -1.06536453e+00], [ -3.28956489e-01,  -9.49360437e-01], [ -2.30916492e-01,  -8.85070439e-01],
       [ -2.09936593e-01,  -9.24940438e-01], [ -1.86193494e-01,  -9.62823736e-01], [ -1.61043495e-01,  -9.99723735e-01], [ -1.62213495e-01,  -8.79963739e-01],
       [ -1.39902795e-01,  -7.73674643e-01], [ -5.98227980e-02,  -7.00514745e-01], [ -4.04627987e-02,  -7.53024743e-01], [ -1.07899996e-02,  -7.96719442e-01],
       [  2.52699992e-02,  -8.34899441e-01], [  2.36043992e-02,  -8.27829441e-01], [  2.20275993e-02,  -8.20753341e-01], [  2.05530993e-02,  -8.13673941e-01],
       [ -1.56259995e-03,  -7.07482245e-01], [  6.14899980e-04,  -6.00093148e-01], [  1.10377296e-01,  -4.87321352e-01], [  1.29577296e-01,  -5.38621350e-01],
       [  1.53302195e-01,  -5.86516349e-01], [  1.82052194e-01,  -6.30656247e-01], [  1.81232194e-01,  -6.23916348e-01], [  1.80506394e-01,  -6.17191848e-01],
       [  1.79884194e-01,  -6.10500048e-01], [  1.70551294e-01,  -5.10123751e-01], [  1.84855894e-01,  -4.15517054e-01], [  2.64449591e-01,  -3.36823357e-01],
       [  2.76779591e-01,  -3.72753256e-01], [  2.96308290e-01,  -4.05413355e-01], [  3.21798289e-01,  -4.35363354e-01], [  3.21600289e-01,  -4.30220254e-01],
       [  3.21551289e-01,  -4.25063754e-01], [  3.21637289e-01,  -4.19894554e-01], [  3.24294689e-01,  -2.59650160e-01], [  4.61042285e-01,  -8.96737653e-02],
       [  5.98566080e-01,  -8.22627656e-02], [  6.42856079e-01,  -7.98827657e-02], [  6.88484577e-01,  -1.09956165e-01], [  7.20294576e-01,  -1.52326263e-01],
       [  7.25024576e-01,  -2.43566260e-01], [  7.01981777e-01,  -2.02964762e-01], [  6.93121677e-01,  -2.55304760e-01], [  6.96731777e-01,  -2.52279760e-01],
       [  7.00454377e-01,  -2.49400260e-01], [  7.04327777e-01,  -2.46676860e-01], [  7.31441676e-01,  -2.27612761e-01], [  7.65470274e-01,  -2.16448261e-01],
       [  8.12711573e-01,  -2.20368161e-01], [  8.13901573e-01,  -2.64258160e-01], [  7.84161074e-01,  -2.60677660e-01], [  8.16271173e-01,  -3.52057657e-01],
       [  8.26931172e-01,  -2.28847661e-01], [  9.66061568e-01,  -3.10986758e-01], [  1.00125157e+00,  -1.04806665e-01], [  1.06154156e+00,  -5.18666666e-02],
       [  1.09776906e+00,   3.49633155e-03], [  1.08635896e+00,   6.36064295e-02], [  1.03090897e+00,   8.30663289e-02], [  1.01512447e+00,   9.65873134e-03],
       [  9.63634368e-01,  -4.51268318e-04], [  9.57894368e-01,   2.74087308e-02], [  9.40184769e-01,   4.74722301e-02], [  9.15704769e-01,   6.31522296e-02],
       [  9.07854670e-01,   3.99122303e-02], [  8.94262870e-01,   2.79373307e-02], [  8.78072871e-01,   2.10673310e-02], [  8.82462871e-01,   5.04773300e-02],
       [  8.71858771e-01,   7.92731290e-02], [  8.47618772e-01,   1.07493128e-01], [  8.23318773e-01,   7.45131292e-02], [  7.92794274e-01,   6.74711294e-02],
       [  7.60724275e-01,   6.67411294e-02], [  7.60898275e-01,   7.15286293e-02], [  7.60841275e-01,   7.62446291e-02], [  7.60548275e-01,   8.08915290e-02],
       [  7.58496975e-01,   1.13419728e-01], [  7.45233775e-01,   1.42569527e-01], [  7.24015076e-01,   1.69309526e-01], [  7.13425076e-01,   1.35099527e-01],
       [  6.92584077e-01,   1.29936527e-01], [  6.68014078e-01,   1.35266527e-01], [  6.78954077e-01,   1.71276526e-01], [  6.71949078e-01,   1.95536125e-01],
       [  6.50099078e-01,   2.10076125e-01], [  6.17339079e-01,   1.36856127e-01], [  4.72343884e-01,   2.38797224e-01], [  5.96353980e-01,   2.99197222e-01],
       [  6.05903880e-01,   2.68057223e-01], [  6.22657979e-01,   2.40926924e-01], [  6.53687878e-01,   2.21707024e-01], [  6.70197878e-01,   2.41027024e-01],
       [  6.88101277e-01,   2.59140823e-01], [  7.14171276e-01,   2.70090823e-01], [  7.36961275e-01,   2.44770824e-01], [  7.41392075e-01,   2.05098825e-01],
       [  7.82682074e-01,   1.96628925e-01], [  8.04812073e-01,   2.10948825e-01], [  8.23734573e-01,   2.37908724e-01], [  8.52994572e-01,   2.53508723e-01],
       [  8.73614571e-01,   2.29338724e-01], [  8.63291571e-01,   2.02522625e-01], [  8.87491670e-01,   1.76472626e-01], [  9.20341669e-01,   1.83582626e-01],
       [  9.34593769e-01,   2.32715824e-01], [  9.69903768e-01,   2.25295824e-01], [  9.90803767e-01,   2.12915825e-01], [  9.92998667e-01,   1.87926925e-01],
       [  9.84698667e-01,   1.55876926e-01], [  1.02498867e+00,   1.47236927e-01], [  1.06532146e+00,   1.63990126e-01], [  1.10562146e+00,   1.98870025e-01],
       [  1.11093146e+00,   1.70380126e-01], [  1.08871006e+00,   1.36293527e-01], [  1.06172016e+00,   1.01223528e-01], [  1.11955006e+00,   9.41336285e-02],
       [  1.17533126e+00,   1.05756428e-01], [  1.21938126e+00,   2.24856424e-01], [  1.26422126e+00,   2.39766424e-01], [  1.29453516e+00,   2.65822123e-01],
       [  1.30359516e+00,   3.08162021e-01], [  1.34777506e+00,   3.23482121e-01], [  1.37520015e+00,   3.60383620e-01], [  1.38422015e+00,   4.21013618e-01],
       [  1.42238015e+00,   4.55233616e-01], [  1.44160995e+00,   4.94320415e-01], [  1.43797995e+00,   5.39270414e-01], [  1.23818986e+00,   5.48530413e-01],
       [  1.20371116e+00,   6.21842911e-01], [  9.59591268e-01,   6.42292910e-01], [  9.02911270e-01,   7.13822908e-01], [  8.65204671e-01,   7.26630207e-01],
       [  8.64654771e-01,   7.51570207e-01], [  8.56884771e-01,   7.50390207e-01], [  8.50779072e-01,   7.44836007e-01], [  8.41349072e-01,   7.47996007e-01],
       [  3.88067787e-01,   8.65344703e-01], [  1.95117793e-01,   7.74534706e-01], [ -6.97712977e-02,   8.40598004e-01], [ -1.35511295e-01,   9.49997900e-01],
       [ -1.35511295e-01,   9.50000000e-01],
       [ -1.35511295e-01,   9.50000000e-01]], [ 1,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  2,  4,  4,  4,  2, 79])
  chaos = matplotlib.path.Path([[  4.29028097e-04,   9.99503600e-01], [  4.29028097e-04,   9.99503600e-01],
       [ -7.49585660e-02,   6.03847063e-01], [ -1.60687423e-01,   4.28706735e-01], [ -1.66497221e-01,   4.16837428e-01], [ -1.00115223e-01,   4.62305952e-01],
       [ -5.07279255e-02,   4.97175041e-01], [ -5.07279255e-02,   1.23401855e-01], [ -3.15028488e-01,   3.87702418e-01], [ -2.55451307e-01,   3.97965977e-01],
       [ -1.76354777e-01,   4.12757192e-01], [ -1.88855822e-01,   4.17041532e-01], [ -3.73318417e-01,   4.80265061e-01], [ -7.06403054e-01,   7.06723805e-01],
       [ -7.06403054e-01,   7.06723805e-01], [ -6.90148467e-01,   6.90386302e-01], [ -7.06475565e-01,   7.06640908e-01], [ -7.06475565e-01,   7.06640908e-01],
       [ -4.80016921e-01,   3.73566656e-01], [ -4.16793353e-01,   1.89104061e-01], [ -4.12508733e-01,   1.76603237e-01], [ -3.97720183e-01,   2.55688099e-01],
       [ -3.87454238e-01,   3.15266323e-01], [ -1.23164020e-01,   5.09761047e-02], [ -4.96937146e-01,   5.09761047e-02], [ -4.62067095e-01,   1.00361016e-01],
       [ -4.16589510e-01,   1.66745761e-01], [ -4.28458556e-01,   1.60935603e-01], [ -6.03599104e-01,   7.52065448e-02], [ -9.99255422e-01,  -1.80848865e-04],
       [ -9.99255422e-01,  -1.80848865e-04], [ -9.76215108e-01,  -2.42996241e-04], [ -9.99255422e-01,  -3.05143618e-04], [ -9.99255422e-01,  -3.05143618e-04],
       [ -6.03599144e-01,  -7.56924771e-02], [ -4.28458556e-01,  -1.61421595e-01], [ -4.16589450e-01,  -1.67231332e-01], [ -4.62057713e-01,  -1.00849394e-01],
       [ -4.96926802e-01,  -5.14620972e-02], [ -1.23164020e-01,  -5.14620972e-02], [ -3.87464583e-01,  -3.15762660e-01], [ -3.97728142e-01,  -2.56185398e-01],
       [ -4.12509053e-01,  -1.77088808e-01], [ -4.16793353e-01,  -1.89590014e-01], [ -4.80016921e-01,  -3.74052468e-01], [ -7.06485910e-01,  -7.07137265e-01],
       [ -7.06485910e-01,  -7.07137265e-01], [ -6.90148467e-01,  -6.90882659e-01], [ -7.06392709e-01,  -7.07220102e-01], [ -7.06392709e-01,  -7.07220102e-01],
       [ -3.73318537e-01,  -4.80751073e-01], [ -1.88855822e-01,  -4.17527545e-01], [ -1.76354937e-01,  -4.13242984e-01], [ -2.55450204e-01,  -3.98454375e-01],
       [ -3.15028488e-01,  -3.88188430e-01], [ -5.07279255e-02,  -1.23887828e-01], [ -5.07279255e-02,  -4.97681742e-01], [ -1.00112777e-01,  -4.62812053e-01],
       [ -1.66497361e-01,  -4.17333344e-01], [ -1.60687423e-01,  -4.29203092e-01], [ -7.49584658e-02,  -6.04343420e-01], [  4.29028097e-04,  -1.00000000e+00],
       [  4.29028097e-04,  -1.00000000e+00], [  4.91175474e-04,  -9.76959562e-01], [  5.43299079e-04,  -1.00000000e+00], [  5.43299079e-04,  -1.00000000e+00],
       [  7.59308331e-02,  -6.04343420e-01], [  1.61659731e-01,  -4.29203092e-01], [  1.67469528e-01,  -4.17333725e-01], [  1.01087590e-01,  -4.62801868e-01],
       [  5.17002527e-02,  -4.97671338e-01], [  5.17002527e-02,  -1.23887828e-01], [  3.16010919e-01,  -3.88198775e-01], [  2.56434219e-01,  -3.98462554e-01],
       [  1.77336627e-01,  -4.13242563e-01], [  1.89838253e-01,  -4.17527545e-01], [  3.74300487e-01,  -4.80751073e-01], [  7.07385445e-01,  -7.07220102e-01],
       [  7.07385445e-01,  -7.07220102e-01], [  6.91130838e-01,  -6.90882659e-01], [  7.07468341e-01,  -7.07137265e-01], [  7.07468341e-01,  -7.07137265e-01],
       [  4.80999312e-01,  -3.74062652e-01], [  4.17775724e-01,  -1.89600418e-01], [  4.13491003e-01,  -1.77099373e-01], [  3.98702414e-01,  -2.56184235e-01],
       [  3.88436669e-01,  -3.15762680e-01], [  1.24136067e-01,  -5.14621173e-02], [  4.97929922e-01,  -5.14621173e-02], [  4.63060252e-01,  -1.00846828e-01],
       [  4.17581544e-01,  -1.67231573e-01], [  4.29451332e-01,  -1.61421615e-01], [  6.04591820e-01,  -7.56927177e-02], [  1.00024822e+00,  -3.05163665e-04],
       [  1.00024822e+00,  -3.05163665e-04], [  9.77207781e-01,  -2.43016289e-04], [  1.00024822e+00,  -1.80868913e-04], [  1.00024822e+00,  -1.80868913e-04],
       [  6.04591459e-01,   7.52068856e-02], [  4.29451332e-01,   1.60935583e-01], [  4.17581965e-01,   1.66745380e-01], [  4.63050108e-01,   1.00363221e-01],
       [  4.97919577e-01,   5.09760847e-02], [  1.24146412e-01,   5.09760847e-02], [  3.88447014e-01,   3.15276727e-01], [  3.98710733e-01,   2.55699807e-01],
       [  4.13490582e-01,   1.76602595e-01], [  4.17775724e-01,   1.89104061e-01], [  4.80999312e-01,   3.73566656e-01], [  7.07468341e-01,   7.06640908e-01],
       [  7.07468341e-01,   7.06640908e-01], [  6.91130838e-01,   6.90396647e-01], [  7.07385445e-01,   7.06734149e-01], [  7.07385445e-01,   7.06734149e-01],
       [  3.74300286e-01,   4.80265061e-01], [  1.89838253e-01,   4.17041532e-01], [  1.77337208e-01,   4.12756812e-01], [  2.56432214e-01,   3.97968001e-01],
       [  3.16010919e-01,   3.87702418e-01], [  5.16999720e-02,   1.23391531e-01], [  5.16999720e-02,   4.97185385e-01], [  1.01084723e-01,   4.62315074e-01],
       [  1.67469408e-01,   4.16837428e-01], [  1.61659450e-01,   4.28706735e-01], [  7.59313543e-02,   6.03847063e-01], [  5.53362945e-04,   9.99503600e-01],
       [  5.53362945e-04,   9.99503600e-01], [  4.91215569e-04,   9.76463283e-01], [  4.29028097e-04,   9.99503600e-01],
       [  4.29028097e-04,   9.99503600e-01]], [ 1,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,
        4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,  4,
        4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,
        4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,
        4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,
        4,  4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,
        4,  2,  2,  4,  4,  4,  4,  4,  4,  2,  2,  4,  4,  4,  4,  4,  4,
        2,  2,  4,  4,  4,  4,  4,  4,  2,  2, 79])
  firefox = matplotlib.path.Path([[28.4  , 22.469], [28.879, 21.505], [29.251, 20.478], [29.495, 19.403], [30.448, 15.742],
       [30.161, 12.549], [30.161, 12.549], [29.834, 14.653], [29.834, 14.653], [29.365, 10.757], [28.79 ,  9.3  ],
       [27.909,  7.069], [27.517,  7.086], [27.516,  7.09 ], [28.058,  8.469], [28.01 ,  9.259], [27.999,  9.378],
       [27.989,  9.362], [27.98 ,  9.346], [27.972,  9.331], [27.841,  9.007], [27.175,  7.512], [25.747,  6.453],
       [23.245,  3.972], [19.804,  2.439], [16.002,  2.438], [11.946,  2.438], [ 8.297,  4.183], [ 5.764,  6.963],
       [ 5.444,  6.5  ], [ 5.183,  5.938], [ 5.159,  5.317], [ 5.159,  5.317], [ 5.157,  5.319], [ 5.153,  5.322],
       [ 5.153,  5.311], [ 5.15 ,  5.301], [ 5.15 ,  5.291], [ 5.15 ,  5.291], [ 3.54 ,  6.538], [ 3.714,  9.903],
       [ 3.415, 10.477], [ 3.154, 11.075], [ 2.937, 11.694], [ 2.562, 12.511], [ 2.187, 13.698], [ 1.878, 15.44 ],
       [ 1.878, 15.44 ], [ 2.011, 15.018], [ 2.277, 14.452], [ 2.213, 14.934], [ 2.174, 15.423], [ 2.161, 15.919],
       [ 2.071, 16.764], [ 2.043, 17.784], [ 2.122, 19.007], [ 2.122, 19.007], [ 2.154, 18.601], [ 2.258, 17.986],
       [ 3.092, 24.84 ], [ 8.925, 30.151], [16.001, 30.151], [16.001, 30.151], [17.861, 30.151], [19.637, 29.781],
       [21.257, 29.115], [24.938, 27.771], [27.116, 25.196], [28.4  , 22.469], [28.4  , 22.469], [16.002,  3.356],
       [18.448,  3.356], [20.732,  4.036], [22.682,  5.216], [20.408,  4.688], [19.249,  4.955], [19.259,  4.968],
       [19.272,  4.983], [22.643,  5.557], [23.24 ,  6.379], [23.24 ,  6.379], [21.809,  6.379], [20.384,  6.789],
       [20.319,  6.808], [25.626,  7.452], [26.711, 12.755], [26.711, 12.755], [26.129, 11.542], [25.41 , 11.335],
       [25.883, 12.774], [25.761, 15.505], [25.31 , 16.863], [25.252, 17.037], [25.192, 16.108], [24.306, 15.708],
       [24.59 , 17.745], [24.288, 20.976], [22.874, 21.866], [22.765, 21.936], [23.761, 18.677], [23.075, 19.936],
       [18.982, 26.212], [14.116, 22.475], [12.141, 21.144], [13.726, 21.532], [15.408, 21.252], [16.383, 20.585],
       [17.365, 19.913], [17.947, 19.423], [18.47 , 19.538], [18.992, 19.655], [19.34 , 19.131], [18.934, 18.666],
       [18.529, 18.2  ], [17.542, 17.561], [16.209, 17.909], [15.269, 18.156], [14.102, 19.196], [12.323, 18.142],
       [10.805, 17.243], [10.816, 16.512], [10.816, 16.047], [10.816, 15.681], [11.073, 15.167], [11.55 , 15.019],
       [12.13 , 15.081], [12.594, 15.233], [13.087, 15.485], [13.092, 15.35 ], [13.093, 15.17 ], [13.086, 14.966],
       [13.125, 14.889], [13.101, 14.655], [13.039, 14.37 ], [13.003, 14.083], [12.942, 13.788], [12.849, 13.519],
       [12.859, 13.517], [12.866, 13.512], [12.87 , 13.498], [12.946, 13.154], [15.017, 11.954], [15.169, 11.839],
       [15.322, 11.725], [15.719, 11.461], [15.675, 10.656], [15.66 , 10.391], [15.617, 10.362], [13.443, 10.37 ],
       [12.526, 10.373], [12.018,  9.476], [11.854,  9.125], [12.076,  7.894], [12.717,  7.015], [13.773,  6.421],
       [13.793,  6.41 ], [13.788,  6.4  ], [13.765,  6.394], [13.984,  6.267], [11.241,  6.388], [10.005,  7.998],
       [ 9.674,  8.045], [ 9.219,  7.95 ], [ 8.71 ,  7.95 ], [ 8.072,  7.95 ], [ 7.571,  8.02 ], [ 7.107,  8.137],
       [ 7.057,  8.15 ], [ 6.985,  8.148], [ 6.899,  8.136], [ 6.769,  8.04 ], [ 6.575,  7.88 ], [ 6.365,  7.672],
       [ 6.526,  7.492], [ 6.689,  7.316], [ 6.86 ,  7.146], [ 9.201,  4.804], [12.43 ,  3.357], [16.002,  3.356],
       [16.002,  3.356]], [ 1,  4,  4,  4,  4,  4,  4,  2,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  2,  4,  4,  4,  4,  4,
        4, 79,  1,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
        4,  4,  4,  4,  4,  4,  4,  4,  4, 79])
  # shift to origin
  bbox = firefox.get_extents()
  xshift, yshift = (bbox.xmin + bbox.xmax) / 2., (bbox.ymin + bbox.ymax) / 2.
  for item in firefox._vertices:
    item[0] -= xshift
    item[1] -= yshift
    # and mirror along y axis
    item[1] *= -1
    
  sge = matplotlib.path.Path([[0.507, 0.0283], [0.507, 0.0283], [0.558, -0.00152], [0.612, -0.0247], [0.667, -0.041],
                              [0.721, -0.0573], [0.777, -0.0666], [0.833, -0.0688], [0.953, -0.0745], [   1, 0.0241],
                              [0.961, 0.0409], [0.945, 0.0477], [0.604, 0.175], [0.604, 0.175], [0.604, 0.175], [0.562, 0.197],
                              [0.594, 0.202], [0.625, 0.206], [0.767, 0.204], [0.827, 0.237], [0.901, 0.275], [0.914, 0.365],
                              [0.897, 0.364], [0.749, 0.354], [0.609, 0.357], [0.601, 0.359], [0.598, 0.359], [0.598, 0.359],
                              [0.582, 0.366], [0.597, 0.383], [0.611,  0.4], [0.72, 0.52], [0.741, 0.55], [0.799, 0.626],
                              [0.76, 0.741], [0.75, 0.731], [0.75, 0.731], [0.715, 0.687], [0.678, 0.647], [0.639, 0.61],
                              [ 0.6, 0.573], [0.56, 0.539], [0.517, 0.509], [0.51, 0.509], [0.512, 0.531], [0.517, 0.547],
                              [0.517, 0.547], [0.528, 0.566], [0.538, 0.585], [0.547, 0.606], [0.555, 0.626], [0.563, 0.647],
                              [0.57, 0.669], [0.598, 0.811], [0.591, 0.901], [0.53, 0.937], [0.519, 0.943], [0.515, 0.935],
                              [0.502, 0.891], [0.488, 0.847], [0.482, 0.769], [0.428, 0.738], [0.399, 0.723], [0.349, 0.662],
                              [0.348, 0.635], [0.347, 0.608], [0.359, 0.571], [0.344, 0.558], [0.33, 0.546], [0.313, 0.566],
                              [0.295, 0.547], [0.277, 0.527], [0.228, 0.461], [0.21, 0.443], [0.193, 0.426], [0.158, 0.398],
                              [0.136, 0.414], [0.0888, 0.476], [0.112, 0.659], [0.112, 0.659], [0.112, 0.659], [0.117, 0.658],
                              [0.123, 0.658], [0.129, 0.658], [0.135, 0.658], [0.14, 0.658], [0.146, 0.659], [0.146, 0.659],
                              [0.154, 0.661], [0.161, 0.665], [0.168, 0.671], [0.174, 0.677], [0.18, 0.685], [0.184, 0.694],
                              [0.188, 0.703], [0.191, 0.713], [0.192, 0.723], [0.193, 0.734], [0.193, 0.744], [0.191, 0.754],
                              [0.191, 0.754], [0.185, 0.747], [0.179, 0.741], [0.172, 0.737], [0.165, 0.733], [0.158, 0.731],
                              [0.15, 0.731], [0.131, 0.728], [0.126, 0.739], [0.121, 0.762], [0.121, 0.762], [0.118, 0.774],
                              [0.117, 0.786], [0.117, 0.799], [0.117, 0.811], [0.118, 0.823], [0.121, 0.835], [0.13, 0.874],
                              [0.159, 0.892], [0.176, 0.91], [0.193, 0.927], [0.184, 0.995], [0.184, 0.995], [0.184, 0.995],
                              [0.181, 0.988], [0.179, 0.982], [0.176, 0.976], [0.173, 0.971], [0.169, 0.965], [0.165, 0.96],
                              [0.149, 0.94], [0.127, 0.946], [0.0807, 0.95], [0.0807, 0.95], [0.0672, 0.949], [0.0536, 0.952],
                              [0.041, 0.958], [0.0283, 0.965], [0.0169, 0.975], [0.00749, 0.988], [-6.15e-08,    1], [-0.0611, 0.991],
                              [-0.079, 0.99], [-0.079, 0.99], [-0.0994, 0.987], [-0.12, 0.982], [-0.14, 0.977], [-0.16, 0.971],
                              [-0.18, 0.964], [-0.199, 0.956], [-0.199, 0.956], [-0.213, 0.95], [-0.225, 0.943], [-0.238, 0.936],
                              [-0.251, 0.928], [-0.263, 0.919], [-0.275, 0.91], [-0.285, 0.899], [-0.281, 0.888], [-0.281, 0.888],
                              [-0.272, 0.855], [-0.272, 0.855], [-0.262, 0.864], [-0.25, 0.873], [-0.239, 0.88], [-0.227, 0.888],
                              [-0.215, 0.894], [-0.203, 0.899], [-0.169, 0.911], [-0.165, 0.891], [-0.161, 0.871], [-0.161, 0.871],
                              [-0.159, 0.864], [-0.158, 0.857], [-0.156, 0.85], [-0.155, 0.843], [-0.154, 0.836], [-0.153, 0.828],
                              [-0.153, 0.828], [-0.171, 0.823], [-0.189, 0.82], [-0.207, 0.818], [-0.225, 0.817], [-0.244, 0.817],
                              [-0.262, 0.819], [-0.315, 0.822], [-0.318, 0.853], [-0.317, 0.872], [-0.317, 0.878], [-0.332, 0.872],
                              [-0.338, 0.853], [-0.349, 0.822], [-0.352, 0.769], [-0.244, 0.771], [-0.244, 0.771], [-0.227, 0.772],
                              [-0.211, 0.775], [-0.195, 0.78], [-0.179, 0.784], [-0.163, 0.791], [-0.148, 0.799], [-0.14, 0.761],
                              [-0.148, 0.755], [-0.162, 0.752], [-0.162, 0.752], [-0.175, 0.748], [-0.188, 0.746], [-0.201, 0.747],
                              [-0.214, 0.748], [-0.228, 0.751], [-0.24, 0.757], [-0.24, 0.757], [-0.247, 0.726], [-0.242, 0.719],
                              [-0.242, 0.719], [-0.226, 0.707], [-0.208, 0.699], [-0.19, 0.694], [-0.172, 0.689], [-0.154, 0.687],
                              [-0.135, 0.689], [-0.124, 0.689], [-0.115, 0.699], [-0.109, 0.693], [-0.109, 0.693], [-0.105, 0.685],
                              [-0.102, 0.677], [-0.0996, 0.669], [-0.097, 0.66], [-0.095, 0.651], [-0.0937, 0.642], [-0.0937, 0.631],
                              [-0.0954, 0.623], [-0.123, 0.623], [-0.15, 0.624], [-0.159, 0.623], [-0.158, 0.616], [-0.158, 0.616],
                              [-0.152, 0.608], [-0.147, 0.601], [-0.141, 0.594], [-0.135, 0.587], [-0.129, 0.58], [-0.123, 0.573],
                              [-0.118, 0.566], [-0.0905, 0.604], [-0.0905, 0.604], [-0.0905, 0.604], [-0.0911, 0.601],
                              [-0.0914, 0.598], [-0.0914, 0.594], [-0.0914, 0.591], [-0.0911, 0.587], [-0.0905, 0.584],
                              [-0.0882, 0.569], [-0.0807, 0.45], [-0.137, 0.415], [-0.158,  0.4], [-0.194, 0.427], [-0.21, 0.445],
                              [-0.227, 0.462], [-0.278, 0.529], [-0.295, 0.548], [-0.312, 0.567], [-0.332, 0.548], [-0.344, 0.558],
                              [-0.355, 0.569], [-0.347, 0.611], [-0.348, 0.635], [-0.349, 0.659], [-0.4, 0.723], [-0.427, 0.738],
                              [-0.481, 0.768], [-0.487, 0.845], [-0.501, 0.891], [-0.516, 0.937], [-0.519, 0.943], [-0.53, 0.937],
                              [-0.591, 0.903], [-0.598, 0.813], [-0.569, 0.669], [-0.569, 0.669], [-0.563, 0.648], [-0.556, 0.626],
                              [-0.547, 0.606], [-0.539, 0.586], [-0.529, 0.566], [-0.519, 0.548], [-0.513, 0.531], [-0.511, 0.507],
                              [-0.519, 0.51], [-0.519, 0.51], [-0.561, 0.54], [-0.602, 0.574], [-0.64, 0.611], [-0.679, 0.648],
                              [-0.716, 0.688], [-0.751, 0.731], [-0.761, 0.744], [-0.801, 0.629], [-0.742, 0.551], [-0.72, 0.521],
                              [-0.613, 0.403], [-0.597, 0.384], [-0.581, 0.365], [-0.598, 0.359], [-0.598, 0.359], [-0.601, 0.359],
                              [-0.61, 0.359], [-0.749, 0.354], [-0.897, 0.365], [-0.914, 0.365], [-0.901, 0.278], [-0.827, 0.238],
                              [-0.767, 0.205], [-0.626, 0.207], [-0.594, 0.202], [-0.562, 0.198], [-0.604, 0.176], [-0.604, 0.176],
                              [-0.604, 0.176], [-0.945, 0.0485], [-0.961, 0.0416], [  -1, 0.0248], [-0.953, -0.073], [-0.834, -0.068],
                              [-0.834, -0.068], [-0.777, -0.0658], [-0.722, -0.0565], [-0.667, -0.0403], [-0.612, -0.024],
                              [-0.558, -0.00078], [-0.507, 0.029], [-0.464, 0.0508], [-0.489, 0.0107], [-0.499, 0.00229],
                              [-0.563, -0.0577], [-0.751, -0.227], [-0.84, -0.303], [-0.853, -0.314], [-0.855, -0.327],
                              [-0.84, -0.347], [-0.771, -0.445], [-0.639, -0.332], [-0.586, -0.294], [-0.525, -0.249],
                              [-0.38, -0.103], [-0.369, -0.12], [-0.364, -0.126], [-0.373, -0.18], [-0.462, -0.307],
                              [-0.462, -0.307], [-0.458, -0.313], [-0.453, -0.319], [-0.448, -0.325], [-0.443, -0.33],
                              [-0.439, -0.336], [-0.434, -0.341], [-0.434, -0.341], [-0.476, -0.409], [-0.506, -0.452],
                              [-0.517, -0.469], [-0.545, -0.506], [-0.553, -0.504], [-0.56, -0.502], [-0.598, -0.447],
                              [-0.612, -0.445], [-0.62, -0.445], [-0.638, -0.462], [-0.651, -0.452], [-0.651, -0.452],
                              [-0.66, -0.449], [-0.669, -0.449], [-0.678, -0.451], [-0.687, -0.452], [-0.696, -0.456],
                              [-0.704, -0.462], [-0.724, -0.474], [-0.721, -0.538], [-0.715, -0.51], [-0.709, -0.481],
                              [-0.669, -0.482], [-0.648, -0.514], [-0.648, -0.514], [-0.643, -0.521], [-0.637, -0.528],
                              [-0.631, -0.533], [-0.624, -0.539], [-0.617, -0.543], [-0.61, -0.547], [-0.598, -0.55],
                              [-0.591, -0.556], [-0.596, -0.56], [-0.596, -0.56], [-0.608, -0.566], [-0.619, -0.572],
                              [-0.63, -0.579], [-0.642, -0.586], [-0.652, -0.594], [-0.663, -0.602], [-0.669, -0.616],
                              [-0.666, -0.62], [-0.668, -0.627], [-0.696, -0.642], [-0.705, -0.656], [-0.696, -0.694],
                              [-0.696, -0.694], [-0.695, -0.7], [-0.693, -0.705], [-0.691, -0.71], [-0.69, -0.715], [-0.688, -0.72],
                              [-0.686, -0.725], [-0.681, -0.714], [-0.683, -0.677], [-0.654, -0.669], [-0.651, -0.669],
                              [-0.646, -0.674], [-0.641, -0.674], [-0.641, -0.674], [-0.629, -0.669], [-0.617, -0.663],
                              [-0.607, -0.655], [-0.596, -0.647], [-0.586, -0.638], [-0.577, -0.627], [-0.569, -0.614],
                              [-0.571, -0.634], [-0.577, -0.647], [-0.601, -0.707], [-0.592, -0.744], [-0.59, -0.745],
                              [-0.588, -0.746], [-0.581, -0.742], [-0.578, -0.754], [-0.578, -0.754], [-0.573, -0.772],
                              [-0.566, -0.789], [-0.557, -0.803], [-0.547, -0.818], [-0.536, -0.83], [-0.523, -0.839],
                              [-0.523, -0.839], [-0.528, -0.825], [-0.531, -0.81], [-0.533, -0.794], [-0.536, -0.779],
                              [-0.537, -0.763], [-0.536, -0.748], [-0.532, -0.745], [-0.528, -0.748], [-0.523, -0.745],
                              [-0.523, -0.745], [-0.519, -0.738], [-0.516, -0.73], [-0.514, -0.722], [-0.512, -0.713],
                              [-0.511, -0.705], [-0.511, -0.696], [-0.511, -0.672], [-0.522, -0.663], [-0.522, -0.649],
                              [-0.522, -0.649], [-0.521, -0.643], [-0.52, -0.638], [-0.519, -0.632], [-0.518, -0.627],
                              [-0.516, -0.622], [-0.513, -0.617], [-0.513, -0.617], [-0.51, -0.626], [-0.506, -0.635],
                              [-0.502, -0.643], [-0.498, -0.651], [-0.494, -0.659], [-0.489, -0.667], [-0.467, -0.698],
                              [-0.471, -0.705], [-0.447, -0.693], [-0.442, -0.69], [-0.41, -0.759], [-0.41, -0.759],
                              [-0.41, -0.759], [-0.407, -0.749], [-0.405, -0.738], [-0.404, -0.727], [-0.403, -0.716],
                              [-0.403, -0.705], [-0.405, -0.694], [-0.407, -0.683], [-0.416, -0.678], [-0.422, -0.667],
                              [-0.428, -0.657], [-0.411, -0.644], [-0.419, -0.629], [-0.465, -0.571], [-0.469, -0.591],
                              [-0.481, -0.553], [-0.489, -0.527], [-0.474, -0.51], [-0.457, -0.484], [-0.417, -0.42],
                              [-0.416, -0.421], [-0.392, -0.383], [-0.383, -0.392], [-0.377, -0.4], [-0.364, -0.414],
                              [-0.319, -0.327], [-0.28, -0.248], [-0.238, -0.228], [-0.196, -0.207], [-0.177, -0.24],
                              [-0.177, -0.274], [-0.179, -0.333], [-0.269, -0.461], [-0.269, -0.461], [-0.269, -0.461],
                              [-0.285, -0.482], [-0.3, -0.503], [-0.314, -0.525], [-0.329, -0.547], [-0.342, -0.569],
                              [-0.355, -0.593], [-0.355, -0.593], [-0.355, -0.593], [-0.359, -0.601], [-0.362, -0.61],
                              [-0.365, -0.619], [-0.367, -0.628], [-0.369, -0.638], [-0.37, -0.647], [-0.37, -0.647],
                              [-0.371, -0.661], [-0.371, -0.674], [-0.37, -0.687], [-0.369, -0.701], [-0.368, -0.714],
                              [-0.366, -0.727], [-0.362, -0.752], [-0.358, -0.771], [-0.358, -0.771], [-0.358, -0.771],
                              [-0.345, -0.707], [-0.342, -0.7], [-0.333, -0.678], [-0.332, -0.7], [-0.319, -0.743],
                              [-0.319, -0.743], [-0.313, -0.759], [-0.307, -0.776], [-0.3, -0.792], [-0.294, -0.808],
                              [-0.287, -0.824], [-0.279, -0.839], [-0.279, -0.73], [-0.279, -0.701], [-0.276, -0.663],
                              [-0.267, -0.691], [-0.267, -0.691], [-0.261, -0.707], [-0.254, -0.722], [-0.247, -0.737],
                              [-0.241, -0.752], [-0.233, -0.766], [-0.226, -0.781], [-0.226, -0.781], [-0.226, -0.758],
                              [-0.224, -0.731], [-0.224, -0.731], [-0.224, -0.731], [-0.222, -0.709], [-0.219, -0.686],
                              [-0.215, -0.664], [-0.211, -0.641], [-0.207, -0.619], [-0.201, -0.598], [-0.201, -0.598],
                              [-0.194, -0.582], [-0.165, -0.515], [-0.13, -0.507], [-0.0948, -0.5], [-0.0917, -0.584],
                              [-0.094, -0.602], [-0.094, -0.602], [-0.096, -0.617], [-0.0986, -0.632], [-0.102, -0.646],
                              [-0.105, -0.661], [-0.109, -0.676], [-0.113, -0.69], [-0.117, -0.699], [-0.144, -0.766],
                              [-0.148, -0.781], [-0.148, -0.781], [-0.161, -0.818], [-0.164, -0.86], [-0.157, -0.899],
                              [-0.15, -0.939], [-0.134, -0.974], [-0.11,   -1], [-0.11,   -1], [-0.108, -0.978], [-0.106, -0.957],
                              [-0.102, -0.936], [-0.0989, -0.915], [-0.0942, -0.894], [-0.0885, -0.874], [-0.0813, -0.86],
                              [-0.0715, -0.851], [-0.0643, -0.864], [-0.0643, -0.864], [-0.0552, -0.887], [-0.0452, -0.91],
                              [-0.0344, -0.931], [-0.0236, -0.953], [-0.0121, -0.973], [0.000288, -0.994], [0.000288, -0.994],
                              [0.000288, -0.994], [0.0126, -0.973], [0.0242, -0.953], [0.035, -0.931], [0.0458, -0.91],
                              [0.0557, -0.887], [0.0649, -0.864], [0.0721, -0.851], [0.0819, -0.86], [0.0891, -0.874],
                              [0.0891, -0.874], [0.0949, -0.894], [0.0996, -0.915], [0.103, -0.936], [0.107, -0.957],
                              [0.11, -0.978], [0.111,   -1], [0.111,   -1], [0.135, -0.975], [0.152, -0.94], [0.16, -0.9],
                              [0.167, -0.861], [0.165, -0.82], [0.152, -0.782], [0.149, -0.769], [0.121, -0.7], [0.118, -0.691],
                              [0.118, -0.691], [0.113, -0.677], [0.109, -0.662], [0.106, -0.648], [0.103, -0.633], [ 0.1, -0.618],
                              [0.0983, -0.603], [0.0957, -0.585], [0.0983, -0.502], [0.134, -0.508], [0.17, -0.515], [0.199, -0.585],
                              [0.205, -0.599], [0.205, -0.599], [0.205, -0.599], [0.211, -0.621], [0.215, -0.643], [0.219, -0.665],
                              [0.222, -0.687], [0.225, -0.71], [0.227, -0.733], [0.227, -0.733], [0.228, -0.759], [0.228, -0.782],
                              [0.228, -0.782], [0.228, -0.782], [0.236, -0.768], [0.243, -0.753], [0.25, -0.738], [0.256, -0.723],
                              [0.263, -0.708], [0.269, -0.693], [0.278, -0.664], [0.28, -0.702], [0.281, -0.731], [0.281, -0.84],
                              [0.281, -0.84], [0.289, -0.825], [0.296, -0.809], [0.303, -0.793], [0.309, -0.777], [0.315, -0.76],
                              [0.321, -0.743], [0.334, -0.701], [0.336, -0.679], [0.344, -0.701], [0.347, -0.708], [0.361, -0.772],
                              [0.361, -0.772], [0.361, -0.772], [0.365, -0.752], [0.368, -0.728], [0.368, -0.728], [0.37, -0.715],
                              [0.372, -0.701], [0.372, -0.688], [0.373, -0.675], [0.373, -0.661], [0.372, -0.648], [0.372, -0.648],
                              [0.371, -0.639], [0.369, -0.629], [0.367, -0.62], [0.364, -0.611], [0.361, -0.602], [0.357, -0.594],
                              [0.357, -0.594], [0.357, -0.594], [0.344, -0.57], [0.331, -0.547], [0.316, -0.526], [0.302, -0.504],
                              [0.287, -0.482], [0.271, -0.462], [0.271, -0.462], [0.181, -0.334], [0.179, -0.275], [0.179, -0.241],
                              [0.197, -0.209], [0.239, -0.23], [0.28, -0.25], [0.319, -0.328], [0.364, -0.416], [0.377, -0.402],
                              [0.383, -0.394], [0.392, -0.385], [0.416, -0.423], [0.417, -0.423], [0.457, -0.486], [0.474, -0.512],
                              [0.489, -0.529], [0.481, -0.555], [0.469, -0.593], [0.465, -0.573], [0.419, -0.631], [0.411, -0.647],
                              [0.428, -0.66], [0.422, -0.669], [0.416, -0.678], [0.407, -0.684], [0.405, -0.696], [0.405, -0.696],
                              [0.404, -0.707], [0.404, -0.718], [0.405, -0.728], [0.406, -0.739], [0.408, -0.749], [0.411, -0.759],
                              [0.411, -0.759], [0.443, -0.691], [0.447, -0.693], [0.471, -0.706], [0.467, -0.699], [0.49, -0.667],
                              [0.49, -0.667], [0.495, -0.659], [0.499, -0.652], [0.503, -0.643], [0.508, -0.635], [0.511, -0.627],
                              [0.515, -0.618], [0.515, -0.618], [0.517, -0.623], [0.519, -0.628], [0.52, -0.633], [0.522, -0.638],
                              [0.522, -0.644], [0.523, -0.649], [0.523, -0.664], [0.513, -0.672], [0.512, -0.697], [0.512, -0.697],
                              [0.513, -0.706], [0.514, -0.714], [0.516, -0.722], [0.518, -0.731], [0.521, -0.738], [0.524, -0.746],
                              [0.53, -0.75], [0.533, -0.746], [0.538, -0.749], [0.538, -0.749], [0.538, -0.764], [0.537, -0.78],
                              [0.535, -0.795], [0.533, -0.81], [0.529, -0.825], [0.524, -0.84], [0.524, -0.84], [0.537, -0.831],
                              [0.548, -0.819], [0.556, -0.804], [0.565, -0.79], [0.572, -0.774], [0.577, -0.756], [0.58, -0.744],
                              [0.583, -0.75], [0.589, -0.747], [0.595, -0.744], [ 0.6, -0.709], [0.576, -0.649], [0.571, -0.637],
                              [0.569, -0.616], [0.576, -0.629], [0.576, -0.629], [0.585, -0.64], [0.595, -0.649], [0.606, -0.657],
                              [0.616, -0.665], [0.628, -0.672], [0.639, -0.676], [0.645, -0.676], [0.65, -0.67], [0.653, -0.671],
                              [0.682, -0.68], [0.68, -0.716], [0.685, -0.727], [0.685, -0.727], [0.687, -0.722], [0.688, -0.717],
                              [0.69, -0.712], [0.692, -0.707], [0.693, -0.702], [0.695, -0.697], [0.704, -0.655], [0.697, -0.643],
                              [0.666, -0.628], [0.664, -0.621], [0.666, -0.617], [0.661, -0.604], [0.661, -0.604], [0.65, -0.595],
                              [0.64, -0.587], [0.628, -0.58], [0.617, -0.573], [0.606, -0.567], [0.594, -0.561], [0.589, -0.557],
                              [0.594, -0.552], [0.608, -0.548], [0.608, -0.548], [0.615, -0.544], [0.622, -0.54], [0.629, -0.534],
                              [0.635, -0.529], [0.641, -0.522], [0.646, -0.515], [0.667, -0.484], [0.707, -0.483], [0.713, -0.511],
                              [0.718, -0.539], [0.722, -0.476], [0.702, -0.463], [0.702, -0.463], [0.694, -0.457], [0.685, -0.453],
                              [0.676, -0.452], [0.667, -0.45], [0.658, -0.45], [0.649, -0.453], [0.636, -0.463], [0.618, -0.446],
                              [0.61, -0.447], [0.596, -0.447], [0.558, -0.503], [0.552, -0.505], [0.546, -0.507], [0.517, -0.47],
                              [0.505, -0.453], [0.476, -0.411], [0.433, -0.342], [0.433, -0.342], [0.433, -0.342], [0.438, -0.337],
                              [0.443, -0.331], [0.447, -0.326], [0.452, -0.32], [0.457, -0.314], [0.462, -0.308], [0.372, -0.181],
                              [0.364, -0.127], [0.368, -0.121], [0.379, -0.105], [0.525, -0.25], [0.585, -0.295], [0.638, -0.333],
                              [0.77, -0.446], [0.84, -0.348], [0.854, -0.328], [0.852, -0.316], [0.84, -0.304], [0.751, -0.228],
                              [0.562, -0.0592], [0.498, 0.00115], [0.489, 0.00955], [0.463, 0.0497], [0.506, 0.0279]],
                              [1, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4,
                               4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4,
                               2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4, 2, 4,
                               4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4,
                               4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                               2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 2, 4, 
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4,
                               4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4])

  CUSTOM_MARKERS.update({'pac': pac, 'ghost': ghost, 'stark': stark, 'chaos': chaos, 'firefox': firefox, 'sge': sge})

  # are we on win or linux platform?
  if((platform == 'linux') or (platform == 'darwin')):
    PATH_SEPARATOR = '/'
  else:
    PATH_SEPARATOR = '\\'
    
  # determine working directories
  # go via inspect as this seems to be the cross-platform way almost guaranteed to work
  # (abspath is required for some Linux installations in addition -- precise reason unclear but here we go ...)
  from inspect import getsourcefile
  WORKINGDIR = dirname(abspath(getsourcefile(lambda: 0)))
  # abandon earlier, more intuitive approach
  # import sys
  # if(hasattr(sys, 'frozen')):
  #   WORKINGDIR = dirname(sys.executable)
  # else:
  #   WORKINGDIR = dirname(__file__)

  HOMEDIR = expanduser('~')
  REMEMBERDIR = HOMEDIR
  
  # open .config file to read out settings from previous program run
  RECENTFILES, RECENTSTATES, EXPORTFILTER = [], [], ''
  if(platform.startswith('win')):
    APPDIR = getenv("LOCALAPPDATA")
  elif(platform == 'darwin'):
    APPDIR = "~/Library/Application Support"
  elif(platform == 'linux'):
    APPDIR = "~/.local/share"
  else:
    APPDIR = HOMEDIR
  
  if(exists(APPDIR)):
    SETTINGSFILE = APPDIR + PATH_SEPARATOR + 'fit-o-mat' + PATH_SEPARATOR + '.config'
    if(isfile(SETTINGSFILE)):
      # better except this
      try:
        with open(SETTINGSFILE, 'r') as readhandle:
          lines = readhandle.readlines()
          
        mode = 0
        for line in lines:
          if(line.startswith('<')):
            mode = 0
            if(line.startswith('<RECENTFILES>')):
              mode = 1
            elif(line.startswith('<RECENTSTATES>')):
              mode = 2
            elif(line.startswith('<WORKDIR>')):
              mode = 3
            elif(line.startswith('<EXPORT>')):
              mode = 4
          elif(mode == 1):
            if(',' in line):
              filename, filetype = line.split(',')[-2:]
              RECENTFILES.insert(0, [filename, int(filetype)])
          elif(mode == 2):
            filename = line.strip()
            RECENTSTATES.insert(0, filename)
          elif(mode == 3):
            directory = line.strip()
            if(exists(directory)):
              REMEMBERDIR = directory
          elif(mode == 4):
            EXPORTFILTER = line.strip()
      except:
        pass

  # set name of temp. preview file
  TEMP_PREVIEWFILE = 'temp_fit-o-mat_preview.png'
  
  # check for command line arguments
  STATEFILE, grabNext, grabReal, calcFontSize = '', False, False, True
  if(len(argv) > 1):
    for argument in argv[1:]:
      if(argument.startswith('-')):
        grabNext = True
        if(argument.startswith('-font')):
          grabReal = True
      elif(grabNext):
        grabNext = False
        # enable dynamic font sizing under Linux
        if(grabReal):
          try:
            float(argument)
            WIDGET_FONTSIZE = str(argument)
            calcFontSize = False
            SCALEFONT = 1.
            SCALEFONT = float(WIDGET_FONTSIZE) / 8.
            SCALEFONT = max(1, SCALEFONT)
            SCALEFONT = min(1.5, SCALEFONT)
          except:
            pass
          grabReal = False
      elif(STATEFILE == ''):
        STATEFILE = argument
        
  app = QtWidgets.QApplication(argv)
  # set scale factor based on screen dpi
  '''
  if(calcFontSize):
    # get screen width via Qt
    screenWidth = app.primaryScreen().availableGeometry().width()
    
    # if on Windows, also take into account scale factor
    winScaling = 1.
    if(not (platform in ['linux', 'darwin'])):
      try:
        from ctypes import windll
        winScaling = windll.shcore.GetScaleFactorForDevice(0) / 100.
      except:
        pass
    
    SCALEFONT = 2. / winScaling * screenWidth / 1920.
    SCALEFONT = max(1., SCALEFONT)
    SCALEFONT = min(1.5, SCALEFONT)
    WIDGET_FONTSIZE = str(8. * SCALEFONT)
  BASE_SIZE = SCALEFONT * 16 + 6
  '''
  
  # kudos to PyQt6 -- we no longer need to take care of -DPI awareness ourselves but leave it to PyQt6
  BASE_SIZE = 30
  SCALEFONT = 1.5
  WIDGET_FONTSIZE = '12'
  
  # prepare icons for buttons
  blubbi = QtWidgets.QWidget()
  iconList = [['FOM_ICON_FUNCTION', 'SP_FileIcon', 'symbol_fx.svg'],
              ['FOM_ICON_OPEN', 'SP_DialogOpenButton', None],
              ['FOM_ICON_SAVE', 'SP_DialogSaveButton', None],
              ['FOM_ICON_COPY', 'SP_FileIcon', 'symbol_copy.svg'],
              ['FOM_ICON_TOOL', 'SP_FileDialogDetailedView', 'symbol_tool.svg'],
              ['FOM_ICON_GEAR', 'SP_DialogApplyButton', 'symbol_gearwheel.svg'],
              ['FOM_ICON_DICE', 'SP_MessageBoxQuestion', 'symbol_dice.svg'],
              ['FOM_ICON_RESET', 'SP_ArrowLeft', 'symbol_reset.svg'],
              ['FOM_ICON_ARROW_RIGHT', 'SP_ArrowRight', None],
              ['FOM_ICON_DESKTOP', 'SP_DesktopIcon', None],
              ['FOM_ICON_BROWSER_RELOAD', 'SP_BrowserReload', None],
              ['FOM_ICON_DIALOG_CANCEL_BUTTON', 'SP_DialogCancelButton', None],
              ['FOM_ICON_DIALOG_OPEN_BUTTON', 'SP_DialogOpenButton', None],
              ['FOM_ICON_DIALOG_SAVE_BUTTON', 'SP_DialogSaveButton', None],
              ['FOM_ICON_DIALOG_CLOSE_BUTTON', 'SP_DialogCloseButton', None],
              ['FOM_ICON_DIALOG_DISCARD_BUTTON', 'SP_DialogCloseButton', None],
              ['FOM_ICON_DIALOG_HELP_BUTTON', 'SP_DialogHelpButton', None],
              ['FOM_ICON_COMMAND_LINK', 'SP_CommandLink', None],
              ['FOM_ICON_FILE', 'SP_FileIcon', None],
              ['FOM_ICON_DIR', 'SP_DirIcon', None],
              ['FOM_ICON_MESSAGE_BOX_QUESTION', 'SP_MessageBoxQuestion', None],
              ['FOM_ICON_MEDIA_PLAY', 'SP_MediaPlay', None],
              ['FOM_ICON_MEDIA_SEEK_FORWARD', 'SP_MediaSeekForward', None]
      ]
  resPath = Path(WORKINGDIR + PATH_SEPARATOR + 'resources').__str__()
  for entry in iconList:
    ident, standardIcon, customIcon = entry
    if((customIcon != None) and isfile(resPath + PATH_SEPARATOR + customIcon)):
      try:
        globals()[ident] = QtGui.QIcon(resPath + PATH_SEPARATOR + customIcon)
      except:
        pixmapi = getattr(QtWidgets.QStyle.StandardPixmap, standardIcon)
        globals()[ident] = blubbi.style().standardIcon(pixmapi)
    else:
      pixmapi = getattr(QtWidgets.QStyle.StandardPixmap, standardIcon)
      globals()[ident] = blubbi.style().standardIcon(pixmapi)
  
  # prepare styles w/ default settings
  UI_STYLE = 'default'
  QSTYLE, DARK_MODE, PALIM = setQStyle(value=UI_STYLE)
  try:
    UI_BASE_COLOR = PALIM.color(QtGui.QPalette.ColorRole.Base).getRgb()
    UI_ALTERNATE_BASE_COLOR = PALIM.color(QtGui.QPalette.ColorRole.AlternateBase).getRgb()
    UI_TEXT_COLOR = PALIM.color(QtGui.QPalette.ColorRole.WindowText).getRgb()
    UI_BRIGHT_COLOR = PALIM.color(QtGui.QPalette.ColorRole.BrightText).getRgb()
  except:
    UI_BASE_COLOR, UI_ALTERNATE_BASE_COLOR, UI_TEXT_COLOR, UI_BRIGHT_COLOR = (255, 255, 255, 255), (247, 247, 247, 255), (0, 0, 0, 255), (255, 255, 255, 255)
  UI_BASE_STRING, UI_ALTERNATE_BASE_STRING, UI_TEXT_STRING, UI_BRIGHT_STRING = str(UI_BASE_COLOR), str(UI_ALTERNATE_BASE_COLOR), str(UI_TEXT_COLOR), str(UI_BRIGHT_COLOR)
  UI_NOTIFICATION_COLOR, UI_ALERT_COLOR = (0, 0, 255, 255), (255, 0, 0, 255)

  if(UI_STYLE == 'default'):
    UI_BRIGHT_COLOR, UI_BRIGHT_STRING = (160, 160, 160, 255), '(160, 160, 160, 255)'

  # start app
  PICK_TOLERANCE = 10
  myapp = MyForm()
  
  # define custom key bindings
  myapp.bindings = []
  for index in range(myapp.ui.tabWidget.count() + 1):
    # count up one more to account for non-visible 2nd Axes tab
    myapp.bindings.append(QtGui.QShortcut(QtGui.QKeySequence('Ctrl+' + str(index + 1)), myapp))
    myapp.bindings[index].activated.connect(partial(switchTab, index))
  
  # now check whether state should be loaded
  if(STATEFILE != ''):
    QtCore.QCoreApplication.processEvents()
    myapp.ui.loadState(stateFile=STATEFILE)

  # check whether a different UI theme has been set
  if(UI_STYLE != myapp.ui.plotArea.themeUI):
    setColorScheme(value=myapp.ui.plotArea.themeUI, caller=None, initOnly=True)
  else:
    QSTYLESHEET = setQStyleSheet(value=UI_STYLE, PALIM=PALIM)
    
  # set theme and style
  if(QSTYLE != None):
    myapp.setStyle(QSTYLE)

  if(QSTYLESHEET != None):
    myapp.setStyleSheet(QSTYLESHEET)
  myapp.show()

  # due to stylesheet shenanigans, have to readjust diverse tables ... (a new feature? of recent PyQt5 versions)
  myapp.ui.dataarea.tableWidget.readjustSize()

  # now we have to again adjust splitter sizes
  myapp.ui.masterwidget.setSizes([int(422 * SCALEFONT), myapp.ui.masterwidget.size().width() - int(422 * SCALEFONT)])
  useHeight = myapp.ui.plotArea.masterwidget.size().height()
  myapp.ui.plotArea.masterwidget.setSizes([int(useHeight * 0.65), int(useHeight * 0.35)])
  
  # and update ruler to current canvas size
  QtCore.QCoreApplication.processEvents()
  myapp.ui.plotArea.horizontalRuler.updateRuler()
  myapp.ui.plotArea.verticalRuler.updateRuler()
  myapp.ui.plotArea.verticalRulerResid.updateRuler()
  
  # initial show of tooltip tooltip :)
  QtWidgets.QToolTip.showText(myapp.ui.dataarea.importButton.mapToGlobal(QtCore.QPoint(50, 12)), '\u2196 Click here to open your data', myapp.ui.dataarea.importButton)

  # deal with uncaught exceptions
  import sys
  originalhook = sys.excepthook
  sys.excepthook = criticalFailure

  # print Fit-o-mat information (set counter to 1 to counteract initial widget resizes [purely empirical])
  myapp.ui.plotArea.drawAboutLogo(aspect=1.2, destructCounter=1)
  
  # display a message box about openpyxl/ods3 (the latter under Linux only)
  if((not OPENPYXL_PRESENT) or (not XLRD_PRESENT) or ((not ODS3_PRESENT) and (platform == 'linux'))):
    msgBox = QtWidgets.QMessageBox()
    if((not OPENPYXL_PRESENT) or (not XLRD_PRESENT)):
      if(OPENPYXL_PRESENT):
        items = ['xlrd']
      elif(XLRD_PRESENT):
        items = ['openpyxl']
      else:
        items = ['openpyxl', 'xlrd']
      msgBox.setWindowTitle('Missing Python module ' + ' and '.join(items))
      text = ''
      for item in items:
        text += '<span style="text-align:left;white-space:nowrap;"><p>The Python module <i>' + item + '</i> is missing from your system!'
        if(item == 'openpyxl'):
          text += '\n<br/>Without <i>openpyxl</i>, Excel XML files (.xlsx) cannot be imported.</p>'
        else:
          text += '\n<br/>Without <i>xlrd</i>, old-style Excel files (.xls) cannot be imported.</p>'
        text += '<p>Try installing <i>' + item + '</i> via:'
        text += '\n<br/>' + '&nbsp;' * 3 + '<b>python3 -m pip install ' + item + '</b></p>'
        # add dummy line to prevent layout desaster in QMessageBox :(
        ###text += '\n<br/></span>'
        text += '\n</span>'
    else:
      msgBox.setWindowTitle('Missing Python module pyexcel_ods3')
      text = '<span style="text-align:left;white-space:nowrap;"><p>The Python module <i>pyexcel_ods3</i> is missing from your system!'
      text += '\n<br/>Without <i>pyexcel_ods3</i>, open-office spreadsheet files (.ods) cannot be imported.</p>'
      text += '<p>Try installing <i>pyexcel_ods3</i> via:'
      text += '\n<br/>' + '&nbsp;' * 3 + '<b>python3 -m pip install pyexcel_ods3</b></p>'
      # add dummy line to prevent layout desaster in QMessageBox :(
      ###text += '\n<br/></span>'
      text += '\n</span>'
    msgBox.setText(text)
    msgBox.setTextFormat(QtCore.Qt.TextFormat.RichText)
    msgBox.setIcon(QtWidgets.QMessageBox.Icon.Information)
    msgBox.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok)
    # apply styles and show
    if(QSTYLE != None):
      msgBox.setStyle(QSTYLE)
    if(QSTYLESHEET != None):
      msgBox.setStyleSheet(QSTYLESHEET)
    reply = msgBox.exec()
  
  # and start the application
  exit(app.exec())