import weakref
import sys
if __name__ == '__main__':
    sys.path.append('../../')
import numpy as np
import pyqtgraph as pg
from pyqtgraph import ImageItem
from cued_datalogger.api.pyqt_widgets import matplotlib_lookup_table
from PyQt5.QtWidgets import(QWidget,QMenu,QAction,QActionGroup,QWidgetAction,QGridLayout,
                            QCheckBox,QRadioButton,QLineEdit,QSpinBox,QComboBox,
                            QLabel, QApplication, QVBoxLayout, QHBoxLayout, QPushButton)
from PyQt5.QtGui import QDoubleValidator
from PyQt5.QtCore import QMetaObject,QSize,QCoreApplication, QTimer, pyqtSignal


class InteractivePlotWidget(QWidget):
    """A QWidget containing a :class:`CustomPlotWidget` with mouse tracking
    crosshairs, a :class:`LinearRegionItem`, and spinboxes
    to display and control the values of the bounds of the linear region.
    Any additional arguments to :method:`__init__` are passed to the
    CustomPlotWidget.

    Attributes
    ----------
    PlotWidget : pg.PlotWidget
        The PlotWidget contained in the InteractivePlotWidget.
    ViewBox : pg.ViewBox
        The ViewBox contained in the InteractivePlotWidget.
    region : pg.LinearRegionItem
        The LinearRegionItem contained in the InteractivePlotWidget.
    vline : pg.InfiniteLine
        Vertical mouse-tracking line.
    hline : pg.InfiniteLine
        Horizontal mouse-tracking line.
    label : pg.LabelItem
        LabelItem displaying current mouse position.
    lower_box : QSpinBox
        QSpinBox displaying lower bound of :attr:`region`.
    upper_box : QSpinBox
        QSpinBox displaying upper bound of :attr:`region`.
    zoom_btn : QPushButton
        Press to zoom to the :attr:`region` with a set amount of padding.
    show_region : bool
        Controls whether the region is displayed.
    show_crosshair : bool
        Controls whether the crosshair is displayed.
    sig_region_changed : pyqtSignal([int, int])
        The signal emitted when the region is changed.
    """

    sig_region_changed = pyqtSignal([float, float])

    def __init__(self, parent=None,
                 show_region=True, show_crosshair=True, show_label=True,
                 *args, **kwargs):
        self.parent = parent
        self.show_region = show_region
        self.show_crosshair = show_crosshair
        self.show_label = show_label

        super().__init__(parent)

        layout = QVBoxLayout(self)

        # # Set up the PlotWidget
        self.PlotWidget = CustomPlotWidget(self, *args, **kwargs)

        self.PlotItem = self.PlotWidget.getPlotItem()
        self.PlotItem.disableAutoRange()

        self.ViewBox = self.PlotWidget.getViewBox()
        self.ViewBox.menu.show_crosshair_action.setChecked(self.show_crosshair)
        self.ViewBox.menu.show_region_action.setChecked(self.show_region)
        self.ViewBox.menu.show_label_action.setChecked(self.show_label)


        self.vline = pg.InfiniteLine(angle=90)
        self.hline = pg.InfiniteLine(angle=0)

        self.region = pg.LinearRegionItem(bounds=[0, None])
        self.region.sigRegionChanged.connect(self.updateBoxFromRegion)

        layout.addWidget(self.PlotWidget)

        self.label = pg.LabelItem(angle = 0)
        self.label.setParentItem(self.ViewBox)

        self.proxy = pg.SignalProxy(self.PlotWidget.scene().sigMouseMoved, rateLimit=30, slot=self.mouseMoved)

        self.ViewBox.menu.sig_show_region.connect(self.set_show_region)
        self.ViewBox.menu.sig_show_crosshair.connect(self.set_show_crosshair)
        self.ViewBox.menu.sig_show_label.connect(self.set_show_label)

        # # Set up the region controls
        control_layout = QHBoxLayout()
        self.lower_box = pg.SpinBox(self, bounds=(None, None))
        self.lower_box_proxy = pg.SignalProxy(self.lower_box.valueChanged, rateLimit=30, slot=self.on_region_changed)
        self.upper_box = pg.SpinBox(self, bounds=(None, None))
        self.upper_box_proxy = pg.SignalProxy(self.upper_box.valueChanged, rateLimit=30, slot=self.on_region_changed)
        self.zoom_btn = QPushButton('Zoom', self)
        self.zoom_btn.clicked.connect(self.zoomToRegion)

        control_layout.addWidget(QLabel('Lower', self))
        control_layout.addWidget(self.lower_box)
        control_layout.addWidget(QLabel('Upper', self))
        control_layout.addWidget(self.upper_box)
        control_layout.addWidget(self.zoom_btn)
        layout.addLayout(control_layout)

        # # Create a QTimer for smooth updating of the region
        self.updatetimer = QTimer(self)
        self.updatetimer.timeout.connect(self.updateRegionFromBox)
        self.updatetimer.start(20)

        self.clear()

    def on_region_changed(self):
        lower = self.lower_box.value()
        upper = self.upper_box.value()
        self.sig_region_changed.emit(lower, upper)

    def mouseMoved(self, mouse_moved_event):
        """
        Update the mouse position with crosshair
        """
        mouse_position = mouse_moved_event[0]  ## using signal proxy turns original arguments into a tuple
        # If the mouse is in the PlotItem
        if self.PlotItem.sceneBoundingRect().contains(mouse_position):
            # Convert it to the coordinate system
            try:
                mousePoint = self.ViewBox.mapSceneToView(mouse_position)
                # Update the label
                self.label.setText((("<span style='font-size: 12pt;color: black'>"
                                     "x=%0.4f,"
                                     "<span style='color: red'>y1=%0.4f</span>" )
                                    % (mousePoint.x(), mousePoint.y()) ))
                self.vline.setPos(mousePoint.x())
                self.hline.setPos(mousePoint.y())
            except np.linalg.LinAlgError:
                print("Warning: Exception raised in calculating mouse position"
                      " (np.linalg.LinAlgError)")

    def clear(self):
        """Clear the PlotItem and add the default items back in."""
        self.PlotItem.clear()
        if self.show_crosshair:
            self.PlotItem.addItem(self.vline)
            self.PlotItem.addItem(self.hline)
        if self.show_region:
            self.PlotItem.addItem(self.region)

    def updateRegionFromBox(self):
        # Get the bounds of the region as defined by the spinboxes
        region_bounds = [self.lower_box.value(),
                         self.upper_box.value()]
        # Sort them so that the lower one is first
        region_bounds.sort()
        # Update the region
        self.region.setRegion(region_bounds)
        # Update the spinboxes
        self.lower_box.setValue(region_bounds[0])
        self.upper_box.setValue(region_bounds[1])

    def updateBoxFromRegion(self):
        # Get the region bounds as defined by the region
        region_bounds = list(self.region.getRegion())
        # Sort it so the lowest is first
        region_bounds.sort()
        # Set the spinboxes to reflect the bounds of the region
        self.lower_box.setValue(region_bounds[0])
        self.upper_box.setValue(region_bounds[1])

    def zoomToRegion(self, padding=0.1):
        """Zoom to the region, with given padding."""
        pos = self.region.getRegion()
        self.PlotItem.setXRange(pos[0],pos[1],padding=padding)

    def getRegionBounds(self):
        """Return the lower and upper bounds of the region."""
        return self.lower_box.value(), self.upper_box.value()

    def closeEvent(self, close_event):
        # Tidy everything up when told to close
        #self.proxy.disconnect()
        if self.updatetimer.isActive():
            self.updatetimer.stop()
        close_event.accept()

    def plot(self, x=None, y=None, *args, **kwargs):
        """:func:`update_limits` from the x and y values, then plot
        the data on the plotWidget."""
        self.update_limits(x, y)
        self.PlotWidget.plot(x, y, *args, **kwargs)
        self.ViewBox.autoRange()

    def update_limits(self, x, y):
        """Set the increment of the spinboxes, the limits of zooming and
        scrolling the PlotItem, and move the region to x=0"""

        if x is not None and y is not None:
            # Update the increment of the spinboxes
            self.lower_box.setSingleStep(x.max()/100)
            self.upper_box.setSingleStep(x.max()/100)

            # Set the linear region to be in view
            #self.lower_box.setValue(x.max()*0.4)
            #self.upper_box.setValue(x.max()*0.6)
            #self.lower_box.setValue(0)
            #self.upper_box.setValue(0)

            # Set the limits of the PlotItem
            self.PlotItem.setLimits(xMin=x.min(), xMax=x.max())
            self.PlotItem.setRange(xRange=(x.min(), x.max()),
                                   yRange=(y.min(), y.max()),
                                   padding=0.2)


    def getPlotItem(self):
        """Return the PlotItem (reimplemented from
        :method:`pg.PlotWidget.getPlotItem`)."""
        return self.PlotWidget.getPlotItem()

    def set_show_crosshair(self, show_crosshair):
        """Set whether the crosshair is visible."""
        self.show_crosshair = show_crosshair

        if self.show_crosshair:
            self.PlotItem.addItem(self.vline)
            self.PlotItem.addItem(self.hline)
        else:
            self.PlotItem.removeItem(self.vline)
            self.PlotItem.removeItem(self.hline)

    def set_show_region(self, show_region):
        """Set whether the region is visible."""
        self.show_region = show_region

        if self.show_region:
            self.PlotItem.addItem(self.region)
        else:
            self.PlotItem.removeItem(self.region)

    def set_show_label(self, show_label):
        """Set whether the label is visible."""
        self.show_label = show_label

        if self.show_label:
            self.label.show()
        else:
            self.label.hide()


class CustomPlotWidget(pg.PlotWidget):
    '''
    Reimplementation of the original PlotWidget.
    This is where you would implement options to modify the plots.
    For that, reimplement the ctrlMenu variable. Currently, only certain options
    are removed, rather than adding any new custom actions.
    Also, bring out the PlotItem and ViewBox for easy access.

    Attributes
    ----------
    PlotItem: PlotItem

    ViewBox: ViewBox
    '''
    def __init__(self, *args, **kwargs):
        super().__init__(*args, viewBox=CustomViewBox(cparent=self), **kwargs)

        self.PlotItem = self.getPlotItem()
        self.ViewBox = self.PlotItem.getViewBox()

        # Removing some plot options, HERE!!!!!
        ext_menu = self.PlotItem.ctrlMenu
        ext_submenus = self.PlotItem.subMenus
        ext_menu.removeAction(ext_submenus[1].menuAction())
        ext_menu.removeAction(ext_submenus[2].menuAction())
        ext_menu.removeAction(ext_submenus[3].menuAction())
        ext_menu.removeAction(ext_submenus[5].menuAction())

    def getViewBox(self):
        return self.ViewBox

    def getViewedDataRegion(self, data_for_slice, axis='bottom'):
        """Return the indices of data_for_slice that are currently in the
        visible plot window"""

        # Get the axis limits
        axis_lower, axis_upper = self.PlotItem.getAxis(axis).range

        # The data that is in view is where the data is within the axis limits
        data_in_display = (data_for_slice >= axis_lower) & (data_for_slice <= axis_upper)

        indices_of_data_in_display = np.where(data_in_display)[0]
        return indices_of_data_in_display[0], indices_of_data_in_display[-1]


class CustomViewBox(pg.ViewBox):
    '''
    A custom ViewBox, reimplemented to allow custom right click context menu
    '''
    def __init__(self, cparent=None, *arg, **kwarg):
        super().__init__(*arg,**kwarg)
        self.cparent = cparent
        self.menu = CustomViewMenu(self)

    def raiseContextMenu(self, ev):
        menu = self.getMenu(ev)
        menu.addMenu(self.cparent.getPlotItem().ctrlMenu)
        menu.popup(ev.screenPos().toPoint())

    # Temporarily disable the autorange
    #def autoRange(self, padding= None, items=None):
    #    super().autoRange(padding=padding, items= None)
        #r = self.viewRect()
        #self.setLimits(xMin = r.left(), xMax = r.right())


class CustomViewMenu(QMenu):
    '''
    A custom right click context menu. This is a complete implementation of the
    original context menu. The purpose of these is to allow custom implementation
    of actions. In this case, there is extra options to toggle on/off the crosshair.
    Note that this context menu only affects the view, not the data, i.e. 'Plot Options'.
    For that, you would need to reimplement ctrlMenu in the PlotItem, demonstrated in
    InteractivePlotWidget.
    '''
    sig_show_crosshair = pyqtSignal(bool)
    sig_show_region = pyqtSignal(bool)
    sig_show_label = pyqtSignal(bool)

    def __init__(self, view):
        QMenu.__init__(self)

        self.view = weakref.ref(view)  ## keep weakref to view to avoid circular reference (don't know why, but this prevents the ViewBox from being collected)
        self.valid = False  ## tells us whether the ui needs to be updated
        self.viewMap = weakref.WeakValueDictionary()  ## weakrefs to all views listed in the link combos

        # Autorange option
        self.setTitle("ViewBox options")
        self.autorange_action = QAction("Autorange", self)
        self.autorange_action.triggered.connect(self.autoRange)
        self.addAction(self.autorange_action)

        # Display menu, our custom actions
        self.display_menu = QMenu("Display options")

        self.show_crosshair_action = QAction("Show crosshair", self.display_menu)
        self.show_crosshair_action.setCheckable(True)
        #show_crosshair_action.setChecked(self.view().cparent.parent().show_crosshair)
        self.show_crosshair_action.triggered.connect(self.sig_show_crosshair.emit)
        self.display_menu.addAction(self.show_crosshair_action)

        self.show_region_action = QAction("Show region", self.display_menu)
        self.show_region_action.setCheckable(True)
        #show_region_action.setChecked(self.view().cparent.parent().show_region)
        self.show_region_action.triggered.connect(self.sig_show_region.emit)
        self.display_menu.addAction(self.show_region_action)

        self.show_label_action = QAction("Show Label", self.display_menu)
        self.show_label_action.setCheckable(True)
        #show_label_action.setChecked(self.view().cparent.parent().show_label)
        self.show_label_action.triggered.connect(self.sig_show_label.emit)
        self.display_menu.addAction(self.show_label_action)

        self.addMenu(self.display_menu)

        # One-to-one reimplementation of the original options
        self.axes = []
        self.ctrl = []
        self.widgetGroups = []
        self.dv = QDoubleValidator(self)
        for axis in 'XY':
            m = QMenu()
            m.setTitle("%s Axis" % axis)
            w = QWidget()
            ui = CustomUITemplate()
            ui.setupUi(w)
            a = QWidgetAction(self)
            a.setDefaultWidget(w)
            m.addAction(a)
            self.addMenu(m)
            self.axes.append(m)
            self.ctrl.append(ui)
            self.widgetGroups.append(w)

            connects = [
                (ui.mouseCheck.toggled, 'MouseToggled'),
                (ui.manualRadio.clicked, 'ManualClicked'),
                (ui.minText.editingFinished, 'MinTextChanged'),
                (ui.maxText.editingFinished, 'MaxTextChanged'),
                (ui.autoRadio.clicked, 'AutoClicked'),
                (ui.autoPercentSpin.valueChanged, 'AutoSpinChanged'),
                (ui.linkCombo.currentIndexChanged, 'LinkComboChanged'),
                (ui.autoPanCheck.toggled, 'AutoPanToggled'),
                (ui.visibleOnlyCheck.toggled, 'VisibleOnlyToggled')
            ]

            for sig, fn in connects:
                sig.connect(getattr(self, axis.lower()+fn))

        self.ctrl[0].invertCheck.toggled.connect(self.xInvertToggled)
        self.ctrl[1].invertCheck.toggled.connect(self.yInvertToggled)

        self.leftMenu = QMenu("Mouse Mode")
        group = QActionGroup(self)

        pan = QAction("3 button", self.leftMenu)
        zoom = QAction("1 button", self.leftMenu)
        self.leftMenu.addAction(pan)
        self.leftMenu.addAction(zoom)
        pan.triggered.connect(self.set3ButtonMode)
        zoom.triggered.connect(self.set1ButtonMode)

        pan.setCheckable(True)
        zoom.setCheckable(True)
        pan.setActionGroup(group)
        zoom.setActionGroup(group)
        self.mouseModes = [pan, zoom]
        self.addMenu(self.leftMenu)

        self.view().sigStateChanged.connect(self.viewStateChanged)

        self.updateState()

    def viewStateChanged(self):
        self.valid = False
        if self.ctrl[0].minText.isVisible() or self.ctrl[1].minText.isVisible():
            self.updateState()

    def updateState(self):
        ## Something about the ViewBox has changed; update the menu GUI
        state = self.view().getState(copy=False)
        if state['mouseMode'] == pg.ViewBox.PanMode:
            self.mouseModes[0].setChecked(True)
        else:
            self.mouseModes[1].setChecked(True)

        for i in [0,1]:  # x, y
            tr = state['targetRange'][i]
            self.ctrl[i].minText.setText("%0.5g" % tr[0])
            self.ctrl[i].maxText.setText("%0.5g" % tr[1])
            if state['autoRange'][i] is not False:
                self.ctrl[i].autoRadio.setChecked(True)
                if state['autoRange'][i] is not True:
                    self.ctrl[i].autoPercentSpin.setValue(state['autoRange'][i]*100)
            else:
                self.ctrl[i].manualRadio.setChecked(True)
            self.ctrl[i].mouseCheck.setChecked(state['mouseEnabled'][i])

            ## Update combo to show currently linked view
            c = self.ctrl[i].linkCombo
            c.blockSignals(True)
            try:
                view = state['linkedViews'][i]  ## will always be string or None
                if view is None:
                    view = ''

                ind = c.findText(view)

                if ind == -1:
                    ind = 0
                c.setCurrentIndex(ind)
            finally:
                c.blockSignals(False)

            self.ctrl[i].autoPanCheck.setChecked(state['autoPan'][i])
            self.ctrl[i].visibleOnlyCheck.setChecked(state['autoVisibleOnly'][i])
            xy = ['x', 'y'][i]
            self.ctrl[i].invertCheck.setChecked(state.get(xy+'Inverted', False))

        self.valid = True

    def popup(self, *args):
        if not self.valid:
            self.updateState()
        QMenu.popup(self, *args)

    def autoRange(self):
        self.view().autoRange()  ## don't let signal call this directly--it'll add an unwanted argument

    def xMouseToggled(self, b):
        self.view().setMouseEnabled(x=b)

    def xManualClicked(self):
        self.view().enableAutoRange(pg.ViewBox.XAxis, False)

    def xMinTextChanged(self):
        self.ctrl[0].manualRadio.setChecked(True)
        self.view().setXRange(float(self.ctrl[0].minText.text()), float(self.ctrl[0].maxText.text()), padding=0)

    def xMaxTextChanged(self):
        self.ctrl[0].manualRadio.setChecked(True)
        self.view().setXRange(float(self.ctrl[0].minText.text()), float(self.ctrl[0].maxText.text()), padding=0)

    def xAutoClicked(self):
        val = self.ctrl[0].autoPercentSpin.value() * 0.01
        self.view().enableAutoRange(pg.ViewBox.XAxis, val)

    def xAutoSpinChanged(self, val):
        self.ctrl[0].autoRadio.setChecked(True)
        self.view().enableAutoRange(pg.ViewBox.XAxis, val*0.01)

    def xLinkComboChanged(self, ind):
        self.view().setXLink(str(self.ctrl[0].linkCombo.currentText()))

    def xAutoPanToggled(self, b):
        self.view().setAutoPan(x=b)

    def xVisibleOnlyToggled(self, b):
        self.view().setAutoVisible(x=b)


    def yMouseToggled(self, b):
        self.view().setMouseEnabled(y=b)

    def yManualClicked(self):
        self.view().enableAutoRange(pg.ViewBox.YAxis, False)

    def yMinTextChanged(self):
        self.ctrl[1].manualRadio.setChecked(True)
        self.view().setYRange(float(self.ctrl[1].minText.text()), float(self.ctrl[1].maxText.text()), padding=0)

    def yMaxTextChanged(self):
        self.ctrl[1].manualRadio.setChecked(True)
        self.view().setYRange(float(self.ctrl[1].minText.text()), float(self.ctrl[1].maxText.text()), padding=0)

    def yAutoClicked(self):
        val = self.ctrl[1].autoPercentSpin.value() * 0.01
        self.view().enableAutoRange(pg.ViewBox.YAxis, val)

    def yAutoSpinChanged(self, val):
        self.ctrl[1].autoRadio.setChecked(True)
        self.view().enableAutoRange(pg.ViewBox.YAxis, val*0.01)

    def yLinkComboChanged(self, ind):
        self.view().setYLink(str(self.ctrl[1].linkCombo.currentText()))

    def yAutoPanToggled(self, b):
        self.view().setAutoPan(y=b)

    def yVisibleOnlyToggled(self, b):
        self.view().setAutoVisible(y=b)

    def yInvertToggled(self, b):
        self.view().invertY(b)

    def xInvertToggled(self, b):
        self.view().invertX(b)

    def exportMethod(self):
        act = self.sender()
        self.exportMethods[str(act.text())]()

    def set3ButtonMode(self):
        self.view().setLeftButtonAction('pan')

    def set1ButtonMode(self):
        self.view().setLeftButtonAction('rect')

    def setViewList(self, views):
        names = ['']
        self.viewMap.clear()

        ## generate list of views to show in the link combo
        for v in views:
            name = v.name
            if name is None:  ## unnamed views do not show up in the view list (although they are linkable)
                continue
            names.append(name)
            self.viewMap[name] = v

        for i in [0,1]:
            c = self.ctrl[i].linkCombo
            current = c.currentText()
            c.blockSignals(True)
            changed = True
            try:
                c.clear()
                for name in names:
                    c.addItem(name)
                    if name == current:
                        changed = False
                        c.setCurrentIndex(c.count()-1)
            finally:
                c.blockSignals(False)

            if changed:
                c.setCurrentIndex(0)
                c.currentIndexChanged.emit(c.currentIndex())

class CustomUITemplate(object):
    '''
    A custom template for the X and Y Axis options in the right click context menu.
    This is a complete implementation of the original template.
    The purpose of these is to allow custom implementation of templates.
    Currently, it is a complete one-to-one copy of the original ones.
    '''
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(186, 154)
        Form.setMaximumSize(QSize(200, 16777215))
        self.gridLayout = QGridLayout(Form)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setSpacing(0)
        self.gridLayout.setObjectName("gridLayout")
        self.label = QLabel(Form)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 7, 0, 1, 2)
        self.linkCombo = QComboBox(Form)
        self.linkCombo.setSizeAdjustPolicy(QComboBox.AdjustToContents)
        self.linkCombo.setObjectName("linkCombo")
        self.gridLayout.addWidget(self.linkCombo, 7, 2, 1, 2)
        self.autoPercentSpin = QSpinBox(Form)
        self.autoPercentSpin.setEnabled(True)
        self.autoPercentSpin.setMinimum(1)
        self.autoPercentSpin.setMaximum(100)
        self.autoPercentSpin.setSingleStep(1)
        self.autoPercentSpin.setProperty("value", 100)
        self.autoPercentSpin.setObjectName("autoPercentSpin")
        self.gridLayout.addWidget(self.autoPercentSpin, 2, 2, 1, 2)
        self.autoRadio = QRadioButton(Form)
        self.autoRadio.setChecked(True)
        self.autoRadio.setObjectName("autoRadio")
        self.gridLayout.addWidget(self.autoRadio, 2, 0, 1, 2)
        self.manualRadio = QRadioButton(Form)
        self.manualRadio.setObjectName("manualRadio")
        self.gridLayout.addWidget(self.manualRadio, 1, 0, 1, 2)
        self.minText = QLineEdit(Form)
        self.minText.setObjectName("minText")
        self.gridLayout.addWidget(self.minText, 1, 2, 1, 1)
        self.maxText = QLineEdit(Form)
        self.maxText.setObjectName("maxText")
        self.gridLayout.addWidget(self.maxText, 1, 3, 1, 1)
        self.invertCheck = QCheckBox(Form)
        self.invertCheck.setObjectName("invertCheck")
        self.gridLayout.addWidget(self.invertCheck, 5, 0, 1, 4)
        self.mouseCheck = QCheckBox(Form)
        self.mouseCheck.setChecked(True)
        self.mouseCheck.setObjectName("mouseCheck")
        self.gridLayout.addWidget(self.mouseCheck, 6, 0, 1, 4)
        self.visibleOnlyCheck = QCheckBox(Form)
        self.visibleOnlyCheck.setObjectName("visibleOnlyCheck")
        self.gridLayout.addWidget(self.visibleOnlyCheck, 3, 2, 1, 2)
        self.autoPanCheck = QCheckBox(Form)
        self.autoPanCheck.setObjectName("autoPanCheck")
        self.gridLayout.addWidget(self.autoPanCheck, 4, 2, 1, 2)

        self.retranslateUi(Form)
        QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "Link Axis:"))
        self.linkCombo.setToolTip(_translate("Form", "<html><head/><body><p>Links this axis with another view. When linked, both views will display the same data range.</p></body></html>"))
        self.autoPercentSpin.setToolTip(_translate("Form", "<html><head/><body><p>Percent of data to be visible when auto-scaling. It may be useful to decrease this value for data with spiky noise.</p></body></html>"))
        self.autoPercentSpin.setSuffix(_translate("Form", "%"))
        self.autoRadio.setToolTip(_translate("Form", "<html><head/><body><p>Automatically resize this axis whenever the displayed data is changed.</p></body></html>"))
        self.autoRadio.setText(_translate("Form", "Auto"))
        self.manualRadio.setToolTip(_translate("Form", "<html><head/><body><p>Set the range for this axis manually. This disables automatic scaling. </p></body></html>"))
        self.manualRadio.setText(_translate("Form", "Manual"))
        self.minText.setToolTip(_translate("Form", "<html><head/><body><p>Minimum value to display for this axis.</p></body></html>"))
        self.minText.setText(_translate("Form", "0"))
        self.maxText.setToolTip(_translate("Form", "<html><head/><body><p>Maximum value to display for this axis.</p></body></html>"))
        self.maxText.setText(_translate("Form", "0"))
        self.invertCheck.setToolTip(_translate("Form", "<html><head/><body><p>Inverts the display of this axis. (+y points downward instead of upward)</p></body></html>"))
        self.invertCheck.setText(_translate("Form", "Invert Axis"))
        self.mouseCheck.setToolTip(_translate("Form", "<html><head/><body><p>Enables mouse interaction (panning, scaling) for this axis.</p></body></html>"))
        self.mouseCheck.setText(_translate("Form", "Mouse Enabled"))
        self.visibleOnlyCheck.setToolTip(_translate("Form", "<html><head/><body><p>When checked, the axis will only auto-scale to data that is visible along the orthogonal axis.</p></body></html>"))
        self.visibleOnlyCheck.setText(_translate("Form", "Visible Data Only"))
        self.autoPanCheck.setToolTip(_translate("Form", "<html><head/><body><p>When checked, the axis will automatically pan to center on the current data, but the scale along this axis will not change.</p></body></html>"))
        self.autoPanCheck.setText(_translate("Form", "Auto Pan Only"))


class ColorMapPlotWidget(InteractivePlotWidget):
    """An InteractivePlotWidget optimised for plotting color(heat) maps.
    Uses the Matplotlib colormap given by *cmap* to color the map.

    Attributes
    ----------
    lookup_table : ndarray
        The lookup table generated from *cmap* to colour the image with
    num_contours : int
        The number of different colour levels to plot
    contour_spacing : int
        How closely spaced the colour levels are
    """
    def __init__(self, parent=None, cmap="jet"):
        self.lookup_table = matplotlib_lookup_table(cmap)
        self.num_contours = 5
        self.contour_spacing_dB = 5
        self.parent = parent
        super().__init__(parent=self.parent)

    def plot_colormap(self, x, y, z, num_contours=5, contour_spacing_dB=5):
        """Plot *x*, *y* and *z* on a colourmap, with colour intervals defined
        by *num_contours* at *contour_spacing_dB* intervals."""

        #self.PlotWidget.removeItem(self.z_img)

        self.x = x
        self.y = y
        self.z = z

        self.num_contours = num_contours
        self.contour_spacing_dB = contour_spacing_dB
        self.update_lowest_contour()

        # Set up axes:
        x_axis = self.PlotWidget.getAxis('bottom')
        y_axis = self.PlotWidget.getAxis('left')

        self.x_scale_fact = self.get_scale_fact(x)
        self.y_scale_fact = self.get_scale_fact(y)

        x_axis.setScale(self.x_scale_fact)
        y_axis.setScale(self.y_scale_fact)

        #self.autoRange()

        self.z_img = ImageItem(z.transpose())
        self.z_img.setLookupTable(self.lookup_table)
        self.z_img.setLevels([self.lowest_contour, self.highest_contour])

        self.PlotWidget.addItem(self.z_img)

        self.PlotWidget.autoRange()
        #self.PlotWidget.ViewBox.autoRange()

    def get_scale_fact(self, var):
        return var.max() / var.size

    def update_lowest_contour(self):
        """Find the lowest contour to plot, as determined by the number of
        contours and the contour spacing."""
        self.lowest_contour = self.z.max() - (self.num_contours * self.contour_spacing_dB)
        self.highest_contour = self.z.max()


if __name__ == '__main__':

    pg.setConfigOption('background', 'w')
    pg.setConfigOption('foreground', 'k')
    pg.setConfigOption('antialias', True)
    defaultpen = pg.mkPen('k')

    app = 0
    app = QApplication(sys.argv)
    w = InteractivePlotWidget()
    x = np.linspace(0, 20*np.pi, 1e4)
    y = np.sin(x)
    w.PlotItem.plot(x, y, pen=defaultpen)
    w.PlotItem.autoRange()
    w.show()

    sys.exit(app.exec_())
