Ejemplo n.º 1
0
    def __init__(self, parent=None, name=None, labels=None, **kargs):
        QtGui.QGraphicsWidget.__init__(self, parent)

        ## Set up control buttons

        self.ctrlBtn = QtGui.QToolButton()
        self.ctrlBtn.setText('?')
        self.autoBtn = QtGui.QToolButton()
        self.autoBtn.setText('A')
        self.autoBtn.hide()
        self.proxies = []
        for b in [self.ctrlBtn, self.autoBtn]:
            proxy = QtGui.QGraphicsProxyWidget(self)
            proxy.setWidget(b)
            proxy.setAcceptHoverEvents(False)
            b.setStyleSheet(
                "background-color: #000000; color: #888; font-size: 6pt")
            self.proxies.append(proxy)
        #QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
        self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
        #QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
        self.autoBtn.clicked.connect(self.enableAutoScale)

        self.layout = QtGui.QGraphicsGridLayout()
        self.layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.layout)
        self.layout.setHorizontalSpacing(0)
        self.layout.setVerticalSpacing(0)

        self.vb = ViewBox()
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
        self.vb.sigXRangeChanged.connect(self.xRangeChanged)
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
        self.vb.sigYRangeChanged.connect(self.yRangeChanged)
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
        self.vb.sigRangeChangedManually.connect(self.enableManualScale)

        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
        self.vb.sigRangeChanged.connect(self.viewRangeChanged)

        self.layout.addItem(self.vb, 2, 1)
        self.alpha = 1.0
        self.autoAlpha = True
        self.spectrumMode = False

        self.autoScale = [True, True]

        ## Create and place scale items
        self.scales = {
            'top': {
                'item': ScaleItem(orientation='top', linkView=self.vb),
                'pos': (1, 1)
            },
            'bottom': {
                'item': ScaleItem(orientation='bottom', linkView=self.vb),
                'pos': (3, 1)
            },
            'left': {
                'item': ScaleItem(orientation='left', linkView=self.vb),
                'pos': (2, 0)
            },
            'right': {
                'item': ScaleItem(orientation='right', linkView=self.vb),
                'pos': (2, 2)
            }
        }
        for k in self.scales:
            self.layout.addItem(self.scales[k]['item'], *self.scales[k]['pos'])

        ## Create and place label items
        #self.labels = {
        #'title':  {'item': LabelItem('title', size='11pt'),  'pos': (0, 2), 'text': ''},
        #'top':    {'item': LabelItem('top'),    'pos': (1, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'bottom': {'item': LabelItem('bottom'), 'pos': (5, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'left':   {'item': LabelItem('left'),   'pos': (3, 0), 'text': '', 'units': '', 'unitPrefix': ''},
        #'right':  {'item': LabelItem('right'),  'pos': (3, 4), 'text': '', 'units': '', 'unitPrefix': ''}
        #}
        #self.labels['left']['item'].setAngle(-90)
        #self.labels['right']['item'].setAngle(-90)
        #for k in self.labels:
        #self.layout.addItem(self.labels[k]['item'], *self.labels[k]['pos'])
        self.titleLabel = LabelItem('', size='11pt')
        self.layout.addItem(self.titleLabel, 0, 1)
        self.setTitle(None)  ## hide

        for i in range(4):
            self.layout.setRowPreferredHeight(i, 0)
            self.layout.setRowMinimumHeight(i, 0)
            self.layout.setRowSpacing(i, 0)
            self.layout.setRowStretchFactor(i, 1)

        for i in range(3):
            self.layout.setColumnPreferredWidth(i, 0)
            self.layout.setColumnMinimumWidth(i, 0)
            self.layout.setColumnSpacing(i, 0)
            self.layout.setColumnStretchFactor(i, 1)
        self.layout.setRowStretchFactor(2, 100)
        self.layout.setColumnStretchFactor(1, 100)

        ## Wrap a few methods from viewBox
        for m in [
                'setXRange', 'setYRange', 'setRange', 'autoRange', 'viewRect',
                'setMouseEnabled'
        ]:
            setattr(self, m, getattr(self.vb, m))

        self.items = []
        self.curves = []
        self.dataItems = []
        self.paramList = {}
        self.avgCurves = {}

        ### Set up context menu

        w = QtGui.QWidget()
        self.ctrl = c = Ui_Form()
        c.setupUi(w)
        dv = QtGui.QDoubleValidator(self)
        self.ctrlMenu = QtGui.QMenu()
        self.menuAction = QtGui.QWidgetAction(self)
        self.menuAction.setDefaultWidget(w)
        self.ctrlMenu.addAction(self.menuAction)

        if HAVE_WIDGETGROUP:
            self.stateGroup = WidgetGroup(self.ctrlMenu)

        self.fileDialog = None

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False

        #self.ctrlBtn.setFixedWidth(60)
        self.setAcceptHoverEvents(True)

        ## Connect control widgets
        #QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMinText.editingFinished.connect(self.setManualXScale)
        #QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMaxText.editingFinished.connect(self.setManualXScale)
        #QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMinText.editingFinished.connect(self.setManualYScale)
        #QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMaxText.editingFinished.connect(self.setManualYScale)

        #QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xManualRadio.clicked.connect(lambda: self.updateXScale())
        #QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yManualRadio.clicked.connect(lambda: self.updateYScale())

        #QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xAutoRadio.clicked.connect(self.updateXScale)
        #QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yAutoRadio.clicked.connect(self.updateYScale)

        #QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.xAutoPercentSpin.valueChanged.connect(self.replot)
        #QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.yAutoPercentSpin.valueChanged.connect(self.replot)

        #QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
        #QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)

        #QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.alphaGroup.toggled.connect(self.updateAlpha)
        #QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
        c.alphaSlider.valueChanged.connect(self.updateAlpha)
        #QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.autoAlphaCheck.toggled.connect(self.updateAlpha)

        #QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        c.gridGroup.toggled.connect(self.updateGrid)
        #QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
        c.gridAlphaSlider.valueChanged.connect(self.updateGrid)

        #QtCore.QObject.connect(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
        c.powerSpectrumGroup.toggled.connect(self.updateSpectrumMode)
        #QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
        c.saveSvgBtn.clicked.connect(self.saveSvgClicked)
        #QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
        c.saveImgBtn.clicked.connect(self.saveImgClicked)
        #QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
        c.saveCsvBtn.clicked.connect(self.saveCsvClicked)

        #QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        #QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)

        #QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
        self.ctrl.xLinkCombo.currentIndexChanged.connect(
            self.xLinkComboChanged)
        #QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
        self.ctrl.yLinkCombo.currentIndexChanged.connect(
            self.yLinkComboChanged)

        #QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
        c.downsampleSpin.valueChanged.connect(self.updateDownsampling)

        #QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
        self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
        #QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
        self.ctrl.averageGroup.toggled.connect(self.avgToggled)

        #QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
        #QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)

        #QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
        self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation)
        #QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
        self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation)
        #QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.xMouseCheck.toggled.connect(self.mouseCheckChanged)
        #QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.yMouseCheck.toggled.connect(self.mouseCheckChanged)

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False
        self.manager = None

        #self.showLabel('right', False)
        #self.showLabel('top', False)
        #self.showLabel('title', False)
        #self.showLabel('left', False)
        #self.showLabel('bottom', False)
        self.showScale('right', False)
        self.showScale('top', False)
        self.showScale('left', True)
        self.showScale('bottom', True)

        if name is not None:
            self.registerPlot(name)

        if labels is not None:
            for k in labels:
                if isinstance(labels[k], basestring):
                    labels[k] = (labels[k], )
                self.setLabel(k, *labels[k])

        if len(kargs) > 0:
            self.plot(**kargs)
Ejemplo n.º 2
0
class PlotItem(QtGui.QGraphicsWidget):

    sigYRangeChanged = QtCore.Signal(object, object)
    sigXRangeChanged = QtCore.Signal(object, object)
    sigRangeChanged = QtCore.Signal(object, object)
    """Plot graphics item that can be added to any graphics scene. Implements axis titles, scales, interactive viewbox."""
    lastFileDir = None
    managers = {}

    def __init__(self, parent=None, name=None, labels=None, **kargs):
        QtGui.QGraphicsWidget.__init__(self, parent)

        ## Set up control buttons

        self.ctrlBtn = QtGui.QToolButton()
        self.ctrlBtn.setText('?')
        self.autoBtn = QtGui.QToolButton()
        self.autoBtn.setText('A')
        self.autoBtn.hide()
        self.proxies = []
        for b in [self.ctrlBtn, self.autoBtn]:
            proxy = QtGui.QGraphicsProxyWidget(self)
            proxy.setWidget(b)
            proxy.setAcceptHoverEvents(False)
            b.setStyleSheet(
                "background-color: #000000; color: #888; font-size: 6pt")
            self.proxies.append(proxy)
        #QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
        self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
        #QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
        self.autoBtn.clicked.connect(self.enableAutoScale)

        self.layout = QtGui.QGraphicsGridLayout()
        self.layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.layout)
        self.layout.setHorizontalSpacing(0)
        self.layout.setVerticalSpacing(0)

        self.vb = ViewBox()
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
        self.vb.sigXRangeChanged.connect(self.xRangeChanged)
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
        self.vb.sigYRangeChanged.connect(self.yRangeChanged)
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
        self.vb.sigRangeChangedManually.connect(self.enableManualScale)

        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
        self.vb.sigRangeChanged.connect(self.viewRangeChanged)

        self.layout.addItem(self.vb, 2, 1)
        self.alpha = 1.0
        self.autoAlpha = True
        self.spectrumMode = False

        self.autoScale = [True, True]

        ## Create and place scale items
        self.scales = {
            'top': {
                'item': ScaleItem(orientation='top', linkView=self.vb),
                'pos': (1, 1)
            },
            'bottom': {
                'item': ScaleItem(orientation='bottom', linkView=self.vb),
                'pos': (3, 1)
            },
            'left': {
                'item': ScaleItem(orientation='left', linkView=self.vb),
                'pos': (2, 0)
            },
            'right': {
                'item': ScaleItem(orientation='right', linkView=self.vb),
                'pos': (2, 2)
            }
        }
        for k in self.scales:
            self.layout.addItem(self.scales[k]['item'], *self.scales[k]['pos'])

        ## Create and place label items
        #self.labels = {
        #'title':  {'item': LabelItem('title', size='11pt'),  'pos': (0, 2), 'text': ''},
        #'top':    {'item': LabelItem('top'),    'pos': (1, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'bottom': {'item': LabelItem('bottom'), 'pos': (5, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'left':   {'item': LabelItem('left'),   'pos': (3, 0), 'text': '', 'units': '', 'unitPrefix': ''},
        #'right':  {'item': LabelItem('right'),  'pos': (3, 4), 'text': '', 'units': '', 'unitPrefix': ''}
        #}
        #self.labels['left']['item'].setAngle(-90)
        #self.labels['right']['item'].setAngle(-90)
        #for k in self.labels:
        #self.layout.addItem(self.labels[k]['item'], *self.labels[k]['pos'])
        self.titleLabel = LabelItem('', size='11pt')
        self.layout.addItem(self.titleLabel, 0, 1)
        self.setTitle(None)  ## hide

        for i in range(4):
            self.layout.setRowPreferredHeight(i, 0)
            self.layout.setRowMinimumHeight(i, 0)
            self.layout.setRowSpacing(i, 0)
            self.layout.setRowStretchFactor(i, 1)

        for i in range(3):
            self.layout.setColumnPreferredWidth(i, 0)
            self.layout.setColumnMinimumWidth(i, 0)
            self.layout.setColumnSpacing(i, 0)
            self.layout.setColumnStretchFactor(i, 1)
        self.layout.setRowStretchFactor(2, 100)
        self.layout.setColumnStretchFactor(1, 100)

        ## Wrap a few methods from viewBox
        for m in [
                'setXRange', 'setYRange', 'setRange', 'autoRange', 'viewRect',
                'setMouseEnabled'
        ]:
            setattr(self, m, getattr(self.vb, m))

        self.items = []
        self.curves = []
        self.dataItems = []
        self.paramList = {}
        self.avgCurves = {}

        ### Set up context menu

        w = QtGui.QWidget()
        self.ctrl = c = Ui_Form()
        c.setupUi(w)
        dv = QtGui.QDoubleValidator(self)
        self.ctrlMenu = QtGui.QMenu()
        self.menuAction = QtGui.QWidgetAction(self)
        self.menuAction.setDefaultWidget(w)
        self.ctrlMenu.addAction(self.menuAction)

        if HAVE_WIDGETGROUP:
            self.stateGroup = WidgetGroup(self.ctrlMenu)

        self.fileDialog = None

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False

        #self.ctrlBtn.setFixedWidth(60)
        self.setAcceptHoverEvents(True)

        ## Connect control widgets
        #QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMinText.editingFinished.connect(self.setManualXScale)
        #QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMaxText.editingFinished.connect(self.setManualXScale)
        #QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMinText.editingFinished.connect(self.setManualYScale)
        #QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMaxText.editingFinished.connect(self.setManualYScale)

        #QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xManualRadio.clicked.connect(lambda: self.updateXScale())
        #QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yManualRadio.clicked.connect(lambda: self.updateYScale())

        #QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xAutoRadio.clicked.connect(self.updateXScale)
        #QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yAutoRadio.clicked.connect(self.updateYScale)

        #QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.xAutoPercentSpin.valueChanged.connect(self.replot)
        #QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.yAutoPercentSpin.valueChanged.connect(self.replot)

        #QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
        #QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)

        #QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.alphaGroup.toggled.connect(self.updateAlpha)
        #QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
        c.alphaSlider.valueChanged.connect(self.updateAlpha)
        #QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.autoAlphaCheck.toggled.connect(self.updateAlpha)

        #QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        c.gridGroup.toggled.connect(self.updateGrid)
        #QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
        c.gridAlphaSlider.valueChanged.connect(self.updateGrid)

        #QtCore.QObject.connect(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
        c.powerSpectrumGroup.toggled.connect(self.updateSpectrumMode)
        #QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
        c.saveSvgBtn.clicked.connect(self.saveSvgClicked)
        #QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
        c.saveImgBtn.clicked.connect(self.saveImgClicked)
        #QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
        c.saveCsvBtn.clicked.connect(self.saveCsvClicked)

        #QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        #QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)

        #QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
        self.ctrl.xLinkCombo.currentIndexChanged.connect(
            self.xLinkComboChanged)
        #QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
        self.ctrl.yLinkCombo.currentIndexChanged.connect(
            self.yLinkComboChanged)

        #QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
        c.downsampleSpin.valueChanged.connect(self.updateDownsampling)

        #QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
        self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
        #QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
        self.ctrl.averageGroup.toggled.connect(self.avgToggled)

        #QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
        #QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)

        #QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
        self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation)
        #QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
        self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation)
        #QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.xMouseCheck.toggled.connect(self.mouseCheckChanged)
        #QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.yMouseCheck.toggled.connect(self.mouseCheckChanged)

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False
        self.manager = None

        #self.showLabel('right', False)
        #self.showLabel('top', False)
        #self.showLabel('title', False)
        #self.showLabel('left', False)
        #self.showLabel('bottom', False)
        self.showScale('right', False)
        self.showScale('top', False)
        self.showScale('left', True)
        self.showScale('bottom', True)

        if name is not None:
            self.registerPlot(name)

        if labels is not None:
            for k in labels:
                if isinstance(labels[k], basestring):
                    labels[k] = (labels[k], )
                self.setLabel(k, *labels[k])

        if len(kargs) > 0:
            self.plot(**kargs)

    #def paint(self, *args):
    #prof = debug.Profiler('PlotItem.paint', disabled=True)
    #QtGui.QGraphicsWidget.paint(self, *args)
    #prof.finish()

    def close(self):
        #print "delete", self
        ## All this crap is needed to avoid PySide trouble.
        ## The problem seems to be whenever scene.clear() leads to deletion of widgets (either through proxies or qgraphicswidgets)
        ## the solution is to manually remove all widgets before scene.clear() is called
        if self.ctrlMenu is None:  ## already shut down
            return
        self.ctrlMenu.setParent(None)
        self.ctrlMenu = None

        self.ctrlBtn.setParent(None)
        self.ctrlBtn = None
        self.autoBtn.setParent(None)
        self.autoBtn = None

        for k in self.scales:
            i = self.scales[k]['item']
            i.close()

        self.scales = None
        self.scene().removeItem(self.vb)
        self.vb = None
        for i in range(self.layout.count()):
            self.layout.removeAt(i)

        for p in self.proxies:
            try:
                p.setWidget(None)
            except RuntimeError:
                break
            self.scene().removeItem(p)
        self.proxies = []

        self.menuAction.releaseWidget(self.menuAction.defaultWidget())
        self.menuAction.setParent(None)
        self.menuAction = None

        if self.manager is not None:
            self.manager.sigWidgetListChanged.disconnect(self.updatePlotList)
            self.manager.removeWidget(self.name)
        #else:
        #print "no manager"

    def registerPlot(self, name):
        self.name = name
        win = str(self.window())
        #print "register", name, win
        if win not in PlotItem.managers:
            PlotItem.managers[win] = PlotWidgetManager()
        self.manager = PlotItem.managers[win]
        self.manager.addWidget(self, name)
        #QtCore.QObject.connect(self.manager, QtCore.SIGNAL('widgetListChanged'), self.updatePlotList)
        self.manager.sigWidgetListChanged.connect(self.updatePlotList)
        self.updatePlotList()

    def updatePlotList(self):
        """Update the list of all plotWidgets in the "link" combos"""
        #print "update plot list", self
        try:
            for sc in [self.ctrl.xLinkCombo, self.ctrl.yLinkCombo]:
                current = str(sc.currentText())
                sc.clear()
                sc.addItem("")
                if self.manager is not None:
                    for w in self.manager.listWidgets():
                        #print w
                        if w == self.name:
                            continue
                        sc.addItem(w)
        except:
            import gc
            refs = gc.get_referrers(self)
            print "  error during update of", self
            print "  Referrers are:", refs
            raise

    def updateGrid(self, *args):
        g = self.ctrl.gridGroup.isChecked()
        if g:
            g = self.ctrl.gridAlphaSlider.value()
        for k in self.scales:
            self.scales[k]['item'].setGrid(g)

    def viewGeometry(self):
        """return the screen geometry of the viewbox"""
        v = self.scene().views()[0]
        b = self.vb.mapRectToScene(self.vb.boundingRect())
        wr = v.mapFromScene(b).boundingRect()
        pos = v.mapToGlobal(v.pos())
        wr.adjust(pos.x(), pos.y(), pos.x(), pos.y())
        return wr

    def viewRangeChanged(self, vb, range):
        #self.emit(QtCore.SIGNAL('viewChanged'), *args)
        self.sigRangeChanged.emit(self, range)

    def blockLink(self, b):
        self.linksBlocked = b

    def xLinkComboChanged(self):
        self.setXLink(str(self.ctrl.xLinkCombo.currentText()))

    def yLinkComboChanged(self):
        self.setYLink(str(self.ctrl.yLinkCombo.currentText()))

    def setXLink(self, plot=None):
        """Link this plot's X axis to another plot (pass either the PlotItem/PlotWidget or the registered name of the plot)"""
        if isinstance(plot, basestring):
            if self.manager is None:
                return
            if self.xLinkPlot is not None:
                self.manager.unlinkX(self, self.xLinkPlot)
            plot = self.manager.getWidget(plot)
        if not isinstance(plot, PlotItem) and hasattr(plot, 'getPlotItem'):
            plot = plot.getPlotItem()
        self.xLinkPlot = plot
        if plot is not None:
            self.setManualXScale()
            self.manager.linkX(self, plot)

    def setYLink(self, plot=None):
        """Link this plot's Y axis to another plot (pass either the PlotItem/PlotWidget or the registered name of the plot)"""
        if isinstance(plot, basestring):
            if self.manager is None:
                return
            if self.yLinkPlot is not None:
                self.manager.unlinkY(self, self.yLinkPlot)
            plot = self.manager.getWidget(plot)
        if not isinstance(plot, PlotItem) and hasattr(plot, 'getPlotItem'):
            plot = plot.getPlotItem()
        self.yLinkPlot = plot
        if plot is not None:
            self.setManualYScale()
            self.manager.linkY(self, plot)

    def linkXChanged(self, plot):
        """Called when a linked plot has changed its X scale"""
        #print "update from", plot
        if self.linksBlocked:
            return
        pr = plot.vb.viewRect()
        pg = plot.viewGeometry()
        if pg is None:
            #print "   return early"
            return
        sg = self.viewGeometry()
        upp = float(pr.width()) / pg.width()
        x1 = pr.left() + (sg.x() - pg.x()) * upp
        x2 = x1 + sg.width() * upp
        plot.blockLink(True)
        self.setManualXScale()
        self.setXRange(x1, x2, padding=0)
        plot.blockLink(False)
        self.replot()

    def linkYChanged(self, plot):
        """Called when a linked plot has changed its Y scale"""
        if self.linksBlocked:
            return
        pr = plot.vb.viewRect()
        pg = plot.vb.boundingRect()
        sg = self.vb.boundingRect()
        upp = float(pr.height()) / pg.height()
        y1 = pr.bottom() + (sg.y() - pg.y()) * upp
        y2 = y1 + sg.height() * upp
        plot.blockLink(True)
        self.setManualYScale()
        self.setYRange(y1, y2, padding=0)
        plot.blockLink(False)
        self.replot()

    def avgToggled(self, b):
        if b:
            self.recomputeAverages()
        for k in self.avgCurves:
            self.avgCurves[k][1].setVisible(b)

    def avgParamListClicked(self, item):
        name = str(item.text())
        self.paramList[name] = (item.checkState() == QtCore.Qt.Checked)
        self.recomputeAverages()

    def recomputeAverages(self):
        if not self.ctrl.averageGroup.isChecked():
            return
        for k in self.avgCurves:
            self.removeItem(self.avgCurves[k][1])
            #Qwt.QwtPlotCurve.detach(self.avgCurves[k][1])
        self.avgCurves = {}
        for c in self.curves:
            self.addAvgCurve(c)
        self.replot()

    def addAvgCurve(self, curve):
        """Add a single curve into the pool of curves averaged together"""

        ## If there are plot parameters, then we need to determine which to average together.
        remKeys = []
        addKeys = []
        if self.ctrl.avgParamList.count() > 0:

            ### First determine the key of the curve to which this new data should be averaged
            for i in range(self.ctrl.avgParamList.count()):
                item = self.ctrl.avgParamList.item(i)
                if item.checkState() == QtCore.Qt.Checked:
                    remKeys.append(str(item.text()))
                else:
                    addKeys.append(str(item.text()))

            if len(
                    remKeys
            ) < 1:  ## In this case, there would be 1 average plot for each data plot; not useful.
                return

        p = curve.meta().copy()
        for k in p:
            if type(k) is tuple:
                p['.'.join(k)] = p[k]
                del p[k]
        for rk in remKeys:
            if rk in p:
                del p[rk]
        for ak in addKeys:
            if ak not in p:
                p[ak] = None
        key = tuple(p.items())

        ### Create a new curve if needed
        if key not in self.avgCurves:
            plot = PlotCurveItem()
            plot.setPen(mkPen([0, 200, 0]))
            plot.setShadowPen(mkPen([0, 0, 0, 100], 3))
            plot.setAlpha(1.0, False)
            plot.setZValue(100)
            self.addItem(plot)
            #Qwt.QwtPlotCurve.attach(plot, self)
            self.avgCurves[key] = [0, plot]
        self.avgCurves[key][0] += 1
        (n, plot) = self.avgCurves[key]

        ### Average data together
        (x, y) = curve.getData()
        if plot.yData is not None:
            newData = plot.yData * (n - 1) / float(n) + y * 1.0 / float(n)
            plot.setData(plot.xData, newData)
        else:
            plot.setData(x, y)

    def mouseCheckChanged(self):
        state = [
            self.ctrl.xMouseCheck.isChecked(),
            self.ctrl.yMouseCheck.isChecked()
        ]
        self.vb.setMouseEnabled(*state)

    def xRangeChanged(self, _, range):
        if any(np.isnan(range)) or any(np.isinf(range)):
            raise Exception("yRange invalid: %s. Signal came from %s" %
                            (str(range), str(self.sender())))
        self.ctrl.xMinText.setText('%0.5g' % range[0])
        self.ctrl.xMaxText.setText('%0.5g' % range[1])

        ## automatically change unit scale
        maxVal = max(abs(range[0]), abs(range[1]))
        (scale, prefix) = siScale(maxVal)
        #for l in ['top', 'bottom']:
        #if self.getLabel(l).isVisible():
        #self.setLabel(l, unitPrefix=prefix)
        #self.getScale(l).setScale(scale)
        #else:
        #self.setLabel(l, unitPrefix='')
        #self.getScale(l).setScale(1.0)

        #self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
        self.sigXRangeChanged.emit(self, range)

    def yRangeChanged(self, _, range):
        if any(np.isnan(range)) or any(np.isinf(range)):
            raise Exception("yRange invalid: %s. Signal came from %s" %
                            (str(range), str(self.sender())))
        self.ctrl.yMinText.setText('%0.5g' % range[0])
        self.ctrl.yMaxText.setText('%0.5g' % range[1])

        ## automatically change unit scale
        maxVal = max(abs(range[0]), abs(range[1]))
        (scale, prefix) = siScale(maxVal)
        #for l in ['left', 'right']:
        #if self.getLabel(l).isVisible():
        #self.setLabel(l, unitPrefix=prefix)
        #self.getScale(l).setScale(scale)
        #else:
        #self.setLabel(l, unitPrefix='')
        #self.getScale(l).setScale(1.0)
        #self.emit(QtCore.SIGNAL('yRangeChanged'), self, range)
        self.sigYRangeChanged.emit(self, range)

    def enableAutoScale(self):
        self.ctrl.xAutoRadio.setChecked(True)
        self.ctrl.yAutoRadio.setChecked(True)
        self.autoBtn.hide()
        self.updateXScale()
        self.updateYScale()
        self.replot()

    def updateXScale(self):
        """Set plot to autoscale or not depending on state of radio buttons"""
        if self.ctrl.xManualRadio.isChecked():
            self.setManualXScale()
        else:
            self.setAutoXScale()
        self.replot()

    def updateYScale(self, b=False):
        """Set plot to autoscale or not depending on state of radio buttons"""
        if self.ctrl.yManualRadio.isChecked():
            self.setManualYScale()
        else:
            self.setAutoYScale()
        self.replot()

    def enableManualScale(self, v=[True, True]):
        if v[0]:
            self.autoScale[0] = False
            self.ctrl.xManualRadio.setChecked(True)
            #self.setManualXScale()
        if v[1]:
            self.autoScale[1] = False
            self.ctrl.yManualRadio.setChecked(True)
            #self.setManualYScale()
        self.autoBtn.show()
        #self.replot()

    def setManualXScale(self):
        self.autoScale[0] = False
        x1 = float(self.ctrl.xMinText.text())
        x2 = float(self.ctrl.xMaxText.text())
        self.ctrl.xManualRadio.setChecked(True)
        self.setXRange(x1, x2, padding=0)
        self.autoBtn.show()
        #self.replot()

    def setManualYScale(self):
        self.autoScale[1] = False
        y1 = float(self.ctrl.yMinText.text())
        y2 = float(self.ctrl.yMaxText.text())
        self.ctrl.yManualRadio.setChecked(True)
        self.setYRange(y1, y2, padding=0)
        self.autoBtn.show()
        #self.replot()

    def setAutoXScale(self):
        self.autoScale[0] = True
        self.ctrl.xAutoRadio.setChecked(True)
        #self.replot()

    def setAutoYScale(self):
        self.autoScale[1] = True
        self.ctrl.yAutoRadio.setChecked(True)
        #self.replot()

    def addItem(self, item, *args):
        self.items.append(item)
        self.vb.addItem(item, *args)

    def removeItem(self, item):
        if not item in self.items:
            return
        self.items.remove(item)
        if item in self.dataItems:
            self.dataItems.remove(item)

        if item.scene() is not None:
            self.vb.removeItem(item)
        if item in self.curves:
            self.curves.remove(item)
            self.updateDecimation()
            self.updateParamList()
            #item.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged)
            item.sigPlotChanged.connect(self.plotChanged)

    def clear(self):
        for i in self.items[:]:
            self.removeItem(i)
        self.avgCurves = {}

    def clearPlots(self):
        for i in self.curves[:]:
            self.removeItem(i)
        self.avgCurves = {}

    def plot(self,
             data=None,
             data2=None,
             x=None,
             y=None,
             clear=False,
             params=None,
             pen=None):
        """Add a new plot curve. Data may be specified a few ways:
        plot(yVals)   # x vals will be integers
        plot(xVals, yVals)
        plot(y=yVals, x=xVals)
        """
        if y is not None:
            data = y
        if data2 is not None:
            x = data
            data = data2

        if clear:
            self.clear()
        if params is None:
            params = {}
        if HAVE_METAARRAY and isinstance(data, MetaArray):
            curve = self._plotMetaArray(data, x=x)
        elif isinstance(data, np.ndarray):
            curve = self._plotArray(data, x=x)
        elif isinstance(data, list):
            if x is not None:
                x = np.array(x)
            curve = self._plotArray(np.array(data), x=x)
        elif data is None:
            curve = PlotCurveItem()
        else:
            raise Exception('Not sure how to plot object of type %s' %
                            type(data))

        #print data, curve
        self.addCurve(curve, params)
        if pen is not None:
            curve.setPen(mkPen(pen))

        return curve

    def addDataItem(self, item):
        self.addItem(item)
        self.dataItems.append(item)

    def addCurve(self, c, params=None):
        if params is None:
            params = {}
        c.setMeta(params)
        self.curves.append(c)
        #Qwt.QwtPlotCurve.attach(c, self)
        self.addItem(c)

        ## configure curve for this plot
        (alpha, auto) = self.alphaState()
        c.setAlpha(alpha, auto)
        c.setSpectrumMode(self.ctrl.powerSpectrumGroup.isChecked())
        c.setDownsampling(self.downsampleMode())
        c.setPointMode(self.pointMode())

        ## Hide older plots if needed
        self.updateDecimation()

        ## Add to average if needed
        self.updateParamList()
        if self.ctrl.averageGroup.isChecked():
            self.addAvgCurve(c)

        #c.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
        c.sigPlotChanged.connect(self.plotChanged)
        self.plotChanged()

    def plotChanged(self, curve=None):
        ## Recompute auto range if needed
        for ax in [0, 1]:
            if self.autoScale[ax]:
                percentScale = [
                    self.ctrl.xAutoPercentSpin.value(),
                    self.ctrl.yAutoPercentSpin.value()
                ][ax] * 0.01
                mn = None
                mx = None
                for c in self.curves + [c[1] for c in self.avgCurves.values()
                                        ] + self.dataItems:
                    if not c.isVisible():
                        continue
                    cmn, cmx = c.getRange(ax, percentScale)
                    if mn is None or cmn < mn:
                        mn = cmn
                    if mx is None or cmx > mx:
                        mx = cmx
                if mn is None or mx is None or any(np.isnan([mn, mx])) or any(
                        np.isinf([mn, mx])):
                    continue
                if mn == mx:
                    mn -= 1
                    mx += 1
                self.setRange(ax, mn, mx)
                #print "Auto range:", ax, mn, mx

    def replot(self):
        self.plotChanged()
        self.update()

    def updateParamList(self):
        self.ctrl.avgParamList.clear()
        ## Check to see that each parameter for each curve is present in the list
        #print "\nUpdate param list", self
        #print "paramList:", self.paramList
        for c in self.curves:
            #print "  curve:", c
            for p in c.meta().keys():
                #print "    param:", p
                if type(p) is tuple:
                    p = '.'.join(p)

                ## If the parameter is not in the list, add it.
                matches = self.ctrl.avgParamList.findItems(
                    p, QtCore.Qt.MatchExactly)
                #print "      matches:", matches
                if len(matches) == 0:
                    i = QtGui.QListWidgetItem(p)
                    if p in self.paramList and self.paramList[p] is True:
                        #print "      set checked"
                        i.setCheckState(QtCore.Qt.Checked)
                    else:
                        #print "      set unchecked"
                        i.setCheckState(QtCore.Qt.Unchecked)
                    self.ctrl.avgParamList.addItem(i)
                else:
                    i = matches[0]

                self.paramList[p] = (i.checkState() == QtCore.Qt.Checked)
        #print "paramList:", self.paramList

    ## This is bullshit.
    def writeSvg(self, fileName=None):
        if fileName is None:
            fileName = QtGui.QFileDialog.getSaveFileName()
            if isinstance(fileName, tuple):
                raise Exception("Not implemented yet..")
        fileName = str(fileName)
        PlotItem.lastFileDir = os.path.dirname(fileName)

        rect = self.vb.viewRect()
        xRange = rect.left(), rect.right()

        svg = ""
        fh = open(fileName, 'w')

        dx = max(rect.right(), 0) - min(rect.left(), 0)
        ymn = min(rect.top(), rect.bottom())
        ymx = max(rect.top(), rect.bottom())
        dy = max(ymx, 0) - min(ymn, 0)
        sx = 1.
        sy = 1.
        while dx * sx < 10:
            sx *= 1000
        while dy * sy < 10:
            sy *= 1000
        sy *= -1

        #fh.write('<svg viewBox="%f %f %f %f">\n' % (rect.left()*sx, rect.top()*sx, rect.width()*sy, rect.height()*sy))
        fh.write('<svg>\n')
        fh.write(
            '<path fill="none" stroke="#000000" stroke-opacity="0.5" stroke-width="1" d="M%f,0 L%f,0"/>\n'
            % (rect.left() * sx, rect.right() * sx))
        fh.write(
            '<path fill="none" stroke="#000000" stroke-opacity="0.5" stroke-width="1" d="M0,%f L0,%f"/>\n'
            % (rect.top() * sy, rect.bottom() * sy))

        for item in self.curves:
            if isinstance(item, PlotCurveItem):
                color = colorStr(item.pen.color())
                opacity = item.pen.color().alpha() / 255.
                color = color[:6]
                x, y = item.getData()
                mask = (x > xRange[0]) * (x < xRange[1])
                mask[:-1] += mask[1:]
                m2 = mask.copy()
                mask[1:] += m2[:-1]
                x = x[mask]
                y = y[mask]

                x *= sx
                y *= sy

                #fh.write('<g fill="none" stroke="#%s" stroke-opacity="1" stroke-width="1">\n' % color)
                fh.write(
                    '<path fill="none" stroke="#%s" stroke-opacity="%f" stroke-width="1" d="M%f,%f '
                    % (color, opacity, x[0], y[0]))
                for i in xrange(1, len(x)):
                    fh.write('L%f,%f ' % (x[i], y[i]))

                fh.write('"/>')
                #fh.write("</g>")
        for item in self.dataItems:
            if isinstance(item, ScatterPlotItem):

                pRect = item.boundingRect()
                vRect = pRect.intersected(rect)

                for point in item.points():
                    pos = point.pos()
                    if not rect.contains(pos):
                        continue
                    color = colorStr(point.brush.color())
                    opacity = point.brush.color().alpha() / 255.
                    color = color[:6]
                    x = pos.x() * sx
                    y = pos.y() * sy

                    fh.write(
                        '<circle cx="%f" cy="%f" r="1" fill="#%s" stroke="none" fill-opacity="%f"/>\n'
                        % (x, y, color, opacity))
                    #fh.write('<path fill="none" stroke="#%s" stroke-opacity="%f" stroke-width="1" d="M%f,%f ' % (color, opacity, x[0], y[0]))
                    #for i in xrange(1, len(x)):
                    #fh.write('L%f,%f ' % (x[i], y[i]))

                    #fh.write('"/>')

        ## get list of curves, scatter plots

        fh.write("</svg>\n")

    #def writeSvg(self, fileName=None):
    #if fileName is None:
    #fileName = QtGui.QFileDialog.getSaveFileName()
    #fileName = str(fileName)
    #PlotItem.lastFileDir = os.path.dirname(fileName)

    #self.svg = QtSvg.QSvgGenerator()
    #self.svg.setFileName(fileName)
    #res = 120.
    #view = self.scene().views()[0]
    #bounds = view.viewport().rect()
    #bounds = QtCore.QRectF(0, 0, bounds.width(), bounds.height())

    #self.svg.setResolution(res)
    #self.svg.setViewBox(bounds)

    #self.svg.setSize(QtCore.QSize(bounds.width(), bounds.height()))

    #painter = QtGui.QPainter(self.svg)
    #view.render(painter, bounds)

    #painter.end()

    ### Workaround to set pen widths correctly
    #import re
    #data = open(fileName).readlines()
    #for i in range(len(data)):
    #line = data[i]
    #m = re.match(r'(<g .*)stroke-width="1"(.*transform="matrix\(([^\)]+)\)".*)', line)
    #if m is not None:
    ##print "Matched group:", line
    #g = m.groups()
    #matrix = map(float, g[2].split(','))
    ##print "matrix:", matrix
    #scale = max(abs(matrix[0]), abs(matrix[3]))
    #if scale == 0 or scale == 1.0:
    #continue
    #data[i] = g[0] + ' stroke-width="%0.2g" ' % (1.0/scale) + g[1] + '\n'
    ##print "old line:", line
    ##print "new line:", data[i]
    #open(fileName, 'w').write(''.join(data))

    def writeImage(self, fileName=None):
        if fileName is None:
            fileName = QtGui.QFileDialog.getSaveFileName()
            if isinstance(fileName, tuple):
                raise Exception("Not implemented yet..")
        fileName = str(fileName)
        PlotItem.lastFileDir = os.path.dirname(fileName)
        self.png = QtGui.QImage(int(self.size().width()),
                                int(self.size().height()),
                                QtGui.QImage.Format_ARGB32)
        painter = QtGui.QPainter(self.png)
        painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing)
        self.scene().render(painter, QtCore.QRectF(),
                            self.mapRectToScene(self.boundingRect()))
        painter.end()
        self.png.save(fileName)

    def writeCsv(self, fileName=None):
        if fileName is None:
            fileName = QtGui.QFileDialog.getSaveFileName()
        fileName = str(fileName)
        PlotItem.lastFileDir = os.path.dirname(fileName)

        fd = open(fileName, 'w')
        data = [c.getData() for c in self.curves]
        i = 0
        while True:
            done = True
            for d in data:
                if i < len(d[0]):
                    fd.write('%g,%g,' % (d[0][i], d[1][i]))
                    done = False
                else:
                    fd.write(' , ,')
            fd.write('\n')
            if done:
                break
            i += 1
        fd.close()

    def saveState(self):
        if not HAVE_WIDGETGROUP:
            raise Exception("State save/restore requires WidgetGroup class.")
        state = self.stateGroup.state()
        state['paramList'] = self.paramList.copy()
        #print "\nSAVE %s:\n" % str(self.name), state
        #print "Saving state. averageGroup.isChecked(): %s  state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
        return state

    def restoreState(self, state):
        if not HAVE_WIDGETGROUP:
            raise Exception("State save/restore requires WidgetGroup class.")
        if 'paramList' in state:
            self.paramList = state['paramList'].copy()

        self.stateGroup.setState(state)
        self.updateSpectrumMode()
        self.updateDownsampling()
        self.updateAlpha()
        self.updateDecimation()

        self.stateGroup.setState(state)
        self.updateXScale()
        self.updateYScale()
        self.updateParamList()

        #print "\nRESTORE %s:\n" % str(self.name), state
        #print "Restoring state. averageGroup.isChecked(): %s  state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
        #avg = self.ctrl.averageGroup.isChecked()
        #if avg != state['averageGroup']:
        #print "  WARNING: avgGroup is %s, should be %s" % (str(avg), str(state['averageGroup']))

    def widgetGroupInterface(self):
        return (None, PlotItem.saveState, PlotItem.restoreState)

    def updateSpectrumMode(self, b=None):
        if b is None:
            b = self.ctrl.powerSpectrumGroup.isChecked()
        for c in self.curves:
            c.setSpectrumMode(b)
        self.enableAutoScale()
        self.recomputeAverages()

    def updateDownsampling(self):
        ds = self.downsampleMode()
        for c in self.curves:
            c.setDownsampling(ds)
        self.recomputeAverages()
        #for c in self.avgCurves.values():
        #c[1].setDownsampling(ds)

    def downsampleMode(self):
        if self.ctrl.decimateGroup.isChecked():
            if self.ctrl.manualDecimateRadio.isChecked():
                ds = self.ctrl.downsampleSpin.value()
            else:
                ds = True
        else:
            ds = False
        return ds

    def updateDecimation(self):
        if self.ctrl.maxTracesCheck.isChecked():
            numCurves = self.ctrl.maxTracesSpin.value()
        else:
            numCurves = -1

        curves = self.curves[:]
        split = len(curves) - numCurves
        for i in range(len(curves)):
            if numCurves == -1 or i >= split:
                curves[i].show()
            else:
                if self.ctrl.forgetTracesCheck.isChecked():
                    curves[i].free()
                    self.removeItem(curves[i])
                else:
                    curves[i].hide()

    def updateAlpha(self, *args):
        (alpha, auto) = self.alphaState()
        for c in self.curves:
            c.setAlpha(alpha**2, auto)

        #self.replot(autoRange=False)

    def alphaState(self):
        enabled = self.ctrl.alphaGroup.isChecked()
        auto = self.ctrl.autoAlphaCheck.isChecked()
        alpha = float(
            self.ctrl.alphaSlider.value()) / self.ctrl.alphaSlider.maximum()
        if auto:
            alpha = 1.0  ## should be 1/number of overlapping plots
        if not enabled:
            auto = False
            alpha = 1.0
        return (alpha, auto)

    def pointMode(self):
        if self.ctrl.pointsGroup.isChecked():
            if self.ctrl.autoPointsCheck.isChecked():
                mode = None
            else:
                mode = True
        else:
            mode = False
        return mode

    #def wheelEvent(self, ev):
    # disables panning the whole scene by mousewheel
    #ev.accept()

    def resizeEvent(self, ev):
        if self.ctrlBtn is None:  ## already closed down
            return
        self.ctrlBtn.move(0,
                          self.size().height() - self.ctrlBtn.size().height())
        self.autoBtn.move(self.ctrlBtn.width(),
                          self.size().height() - self.autoBtn.size().height())

    def hoverMoveEvent(self, ev):
        self.mousePos = ev.pos()
        self.mouseScreenPos = ev.screenPos()

    def ctrlBtnClicked(self):
        self.ctrlMenu.popup(self.mouseScreenPos)

    def getLabel(self, key):
        pass

    def _checkScaleKey(self, key):
        if key not in self.scales:
            raise Exception("Scale '%s' not found. Scales are: %s" %
                            (key, str(self.scales.keys())))

    def getScale(self, key):
        self._checkScaleKey(key)
        return self.scales[key]['item']

    def setLabel(self, key, text=None, units=None, unitPrefix=None, **args):
        self.getScale(key).setLabel(text=text,
                                    units=units,
                                    unitPrefix=unitPrefix,
                                    **args)

    def showLabel(self, key, show=True):
        self.getScale(key).showLabel(show)

    def setTitle(self, title=None, **args):
        if title is None:
            self.titleLabel.setVisible(False)
            self.layout.setRowFixedHeight(0, 0)
            self.titleLabel.setMaximumHeight(0)
        else:
            self.titleLabel.setMaximumHeight(30)
            self.layout.setRowFixedHeight(0, 30)
            self.titleLabel.setVisible(True)
            self.titleLabel.setText(title, **args)

    def showScale(self, key, show=True):
        s = self.getScale(key)
        p = self.scales[key]['pos']
        if show:
            s.show()
        else:
            s.hide()

    def _plotArray(self, arr, x=None):
        if arr.ndim != 1:
            raise Exception("Array must be 1D to plot (shape is %s)" %
                            arr.shape)
        if x is None:
            x = np.arange(arr.shape[0])
        if x.ndim != 1:
            raise Exception("X array must be 1D to plot (shape is %s)" %
                            x.shape)
        c = PlotCurveItem(arr, x=x)
        return c

    def _plotMetaArray(self, arr, x=None, autoLabel=True):
        inf = arr.infoCopy()
        if arr.ndim != 1:
            raise Exception(
                'can only automatically plot 1 dimensional arrays.')
        ## create curve
        try:
            xv = arr.xvals(0)
            #print 'xvals:', xv
        except:
            if x is None:
                xv = arange(arr.shape[0])
            else:
                xv = x
        c = PlotCurveItem()
        c.setData(x=xv, y=arr.view(np.ndarray))

        if autoLabel:
            name = arr._info[0].get('name', None)
            units = arr._info[0].get('units', None)
            self.setLabel('bottom', text=name, units=units)

            name = arr._info[1].get('name', None)
            units = arr._info[1].get('units', None)
            self.setLabel('left', text=name, units=units)

        return c

    def saveSvgClicked(self):
        self.fileDialog = QtGui.QFileDialog()
        #if PlotItem.lastFileDir is not None:
        #self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        if PlotItem.lastFileDir is not None:
            self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.show()
        #QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
        self.fileDialog.fileSelected.connect(self.writeSvg)

    #def svgFileSelected(self, fileName):
    ##PlotWidget.lastFileDir = os.path.split(fileName)[0]
    #self.writeSvg(str(fileName))

    def saveImgClicked(self):
        self.fileDialog = QtGui.QFileDialog()
        #if PlotItem.lastFileDir is not None:
        #self.fileDialog.setDirectory(PlotItem.lastFileDir)
        if PlotItem.lastFileDir is not None:
            self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        self.fileDialog.show()
        #QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeImage)
        self.fileDialog.fileSelected.connect(self.writeImage)

    def saveCsvClicked(self):
        self.fileDialog = QtGui.QFileDialog()
        #if PlotItem.lastFileDir is not None:
        #self.fileDialog.setDirectory(PlotItem.lastFileDir)
        if PlotItem.lastFileDir is not None:
            self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        self.fileDialog.show()
        #QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeCsv)
        self.fileDialog.fileSelected.connect(self.writeCsv)
Ejemplo n.º 3
0
    def __init__(self, parent=None, name=None, labels=None, **kargs):
        QtGui.QGraphicsWidget.__init__(self, parent)

        ## Set up control buttons

        self.ctrlBtn = QtGui.QToolButton()
        self.ctrlBtn.setText("?")
        self.autoBtn = QtGui.QToolButton()
        self.autoBtn.setText("A")
        self.autoBtn.hide()
        self.proxies = []
        for b in [self.ctrlBtn, self.autoBtn]:
            proxy = QtGui.QGraphicsProxyWidget(self)
            proxy.setWidget(b)
            proxy.setAcceptHoverEvents(False)
            b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
            self.proxies.append(proxy)
        # QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
        self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
        # QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
        self.autoBtn.clicked.connect(self.enableAutoScale)

        self.layout = QtGui.QGraphicsGridLayout()
        self.layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.layout)
        self.layout.setHorizontalSpacing(0)
        self.layout.setVerticalSpacing(0)

        self.vb = ViewBox()
        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
        self.vb.sigXRangeChanged.connect(self.xRangeChanged)
        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
        self.vb.sigYRangeChanged.connect(self.yRangeChanged)
        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
        self.vb.sigRangeChangedManually.connect(self.enableManualScale)

        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
        self.vb.sigRangeChanged.connect(self.viewRangeChanged)

        self.layout.addItem(self.vb, 2, 1)
        self.alpha = 1.0
        self.autoAlpha = True
        self.spectrumMode = False

        self.autoScale = [True, True]

        ## Create and place scale items
        self.scales = {
            "top": {"item": ScaleItem(orientation="top", linkView=self.vb), "pos": (1, 1)},
            "bottom": {"item": ScaleItem(orientation="bottom", linkView=self.vb), "pos": (3, 1)},
            "left": {"item": ScaleItem(orientation="left", linkView=self.vb), "pos": (2, 0)},
            "right": {"item": ScaleItem(orientation="right", linkView=self.vb), "pos": (2, 2)},
        }
        for k in self.scales:
            self.layout.addItem(self.scales[k]["item"], *self.scales[k]["pos"])

        ## Create and place label items
        # self.labels = {
        #'title':  {'item': LabelItem('title', size='11pt'),  'pos': (0, 2), 'text': ''},
        #'top':    {'item': LabelItem('top'),    'pos': (1, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'bottom': {'item': LabelItem('bottom'), 'pos': (5, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'left':   {'item': LabelItem('left'),   'pos': (3, 0), 'text': '', 'units': '', 'unitPrefix': ''},
        #'right':  {'item': LabelItem('right'),  'pos': (3, 4), 'text': '', 'units': '', 'unitPrefix': ''}
        # }
        # self.labels['left']['item'].setAngle(-90)
        # self.labels['right']['item'].setAngle(-90)
        # for k in self.labels:
        # self.layout.addItem(self.labels[k]['item'], *self.labels[k]['pos'])
        self.titleLabel = LabelItem("", size="11pt")
        self.layout.addItem(self.titleLabel, 0, 1)
        self.setTitle(None)  ## hide

        for i in range(4):
            self.layout.setRowPreferredHeight(i, 0)
            self.layout.setRowMinimumHeight(i, 0)
            self.layout.setRowSpacing(i, 0)
            self.layout.setRowStretchFactor(i, 1)

        for i in range(3):
            self.layout.setColumnPreferredWidth(i, 0)
            self.layout.setColumnMinimumWidth(i, 0)
            self.layout.setColumnSpacing(i, 0)
            self.layout.setColumnStretchFactor(i, 1)
        self.layout.setRowStretchFactor(2, 100)
        self.layout.setColumnStretchFactor(1, 100)

        ## Wrap a few methods from viewBox
        for m in ["setXRange", "setYRange", "setRange", "autoRange", "viewRect", "setMouseEnabled"]:
            setattr(self, m, getattr(self.vb, m))

        self.items = []
        self.curves = []
        self.dataItems = []
        self.paramList = {}
        self.avgCurves = {}

        ### Set up context menu

        w = QtGui.QWidget()
        self.ctrl = c = Ui_Form()
        c.setupUi(w)
        dv = QtGui.QDoubleValidator(self)
        self.ctrlMenu = QtGui.QMenu()
        self.menuAction = QtGui.QWidgetAction(self)
        self.menuAction.setDefaultWidget(w)
        self.ctrlMenu.addAction(self.menuAction)

        if HAVE_WIDGETGROUP:
            self.stateGroup = WidgetGroup(self.ctrlMenu)

        self.fileDialog = None

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False

        # self.ctrlBtn.setFixedWidth(60)
        self.setAcceptHoverEvents(True)

        ## Connect control widgets
        # QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMinText.editingFinished.connect(self.setManualXScale)
        # QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMaxText.editingFinished.connect(self.setManualXScale)
        # QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMinText.editingFinished.connect(self.setManualYScale)
        # QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMaxText.editingFinished.connect(self.setManualYScale)

        # QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xManualRadio.clicked.connect(lambda: self.updateXScale())
        # QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yManualRadio.clicked.connect(lambda: self.updateYScale())

        # QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xAutoRadio.clicked.connect(self.updateXScale)
        # QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yAutoRadio.clicked.connect(self.updateYScale)

        # QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.xAutoPercentSpin.valueChanged.connect(self.replot)
        # QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.yAutoPercentSpin.valueChanged.connect(self.replot)

        # QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
        # QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)

        # QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.alphaGroup.toggled.connect(self.updateAlpha)
        # QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
        c.alphaSlider.valueChanged.connect(self.updateAlpha)
        # QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.autoAlphaCheck.toggled.connect(self.updateAlpha)

        # QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        c.gridGroup.toggled.connect(self.updateGrid)
        # QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
        c.gridAlphaSlider.valueChanged.connect(self.updateGrid)

        # QtCore.QObject.connect(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
        c.powerSpectrumGroup.toggled.connect(self.updateSpectrumMode)
        # QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
        c.saveSvgBtn.clicked.connect(self.saveSvgClicked)
        # QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
        c.saveImgBtn.clicked.connect(self.saveImgClicked)
        # QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
        c.saveCsvBtn.clicked.connect(self.saveCsvClicked)

        # QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        # QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)

        # QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
        self.ctrl.xLinkCombo.currentIndexChanged.connect(self.xLinkComboChanged)
        # QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
        self.ctrl.yLinkCombo.currentIndexChanged.connect(self.yLinkComboChanged)

        # QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
        c.downsampleSpin.valueChanged.connect(self.updateDownsampling)

        # QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
        self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
        # QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
        self.ctrl.averageGroup.toggled.connect(self.avgToggled)

        # QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
        # QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)

        # QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
        self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation)
        # QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
        self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation)
        # QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.xMouseCheck.toggled.connect(self.mouseCheckChanged)
        # QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.yMouseCheck.toggled.connect(self.mouseCheckChanged)

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False
        self.manager = None

        # self.showLabel('right', False)
        # self.showLabel('top', False)
        # self.showLabel('title', False)
        # self.showLabel('left', False)
        # self.showLabel('bottom', False)
        self.showScale("right", False)
        self.showScale("top", False)
        self.showScale("left", True)
        self.showScale("bottom", True)

        if name is not None:
            self.registerPlot(name)

        if labels is not None:
            for k in labels:
                if isinstance(labels[k], basestring):
                    labels[k] = (labels[k],)
                self.setLabel(k, *labels[k])

        if len(kargs) > 0:
            self.plot(**kargs)
Ejemplo n.º 4
0
    def __init__(self,
                 xmin,
                 xmax,
                 ymax,
                 parent=None,
                 name=None,
                 labels=None,
                 **kargs):  # xmin, xmax, ymax,
        QtGui.QGraphicsWidget.__init__(self, parent)

        ## Set up control buttons

        self.ctrlBtn = QtGui.QToolButton()
        self.ctrlBtn.setIcon(QtGui.QIcon('gui/icons/configure_shortcuts.png'))
        self.autoBtn = QtGui.QToolButton()
        self.autoBtn.setIcon(QtGui.QIcon('gui/icons/zoom_fit_best.png'))

        self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
        self.autoBtn.clicked.connect(self.enableAutoScale)

        stylesheet = '''
        QToolBar {
         background: qlineargradient(x1: 0, y1:0, x2: 1, y2: 1,
                    stop: 0 #a6a6a6, stop: 0.08 #7f7f7f,
                    stop: 0.39999 #717171, stop: 0.4 #626262,
                    stop: 0.9 #4c4c4c, stop: 1 #333333);
         spacing: 10px; /* spacing between items in the tool bar */
         }'''
        self.toolBar = QtGui.QToolBar()
        self.toolBar.setStyleSheet(stylesheet)
        self.toolBar.addWidget(self.ctrlBtn)
        self.toolBar.addWidget(self.autoBtn)

        self.layout = QtGui.QGraphicsGridLayout()
        self.layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.layout)
        self.layout.setHorizontalSpacing(0)
        self.layout.setVerticalSpacing(0)

        self.vb = ViewBox(xmin, xmax, ymax)  #xmin, xmax, ymax
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChangedc'), self.xRangeChanged)
        self.vb.sigXRangeChanged.connect(self.xRangeChanged)
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
        self.vb.sigYRangeChanged.connect(self.yRangeChanged)
        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
        self.vb.sigRangeChangedManually.connect(self.enableManualScale)

        #QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
        self.vb.sigRangeChanged.connect(self.viewRangeChanged)

        self.layout.addItem(self.vb, 2, 1)

        self.alpha = 1.0
        self.autoAlpha = True
        self.spectrumMode = False

        self.autoScale = [True, True]

        ## Create and place scale items
        self.scales = {
            'top': {
                'item': ScaleItem(orientation='top', linkView=self.vb),
                'pos': (1, 1)
            },
            'bottom': {
                'item': ScaleItem(orientation='bottom', linkView=self.vb),
                'pos': (3, 1)
            },
            'left': {
                'item': ScaleItem(orientation='left', linkView=self.vb),
                'pos': (2, 0)
            },
            'right': {
                'item': ScaleItem(orientation='right', linkView=self.vb),
                'pos': (2, 2)
            }
        }
        for k in self.scales:
            #self.scales[k]['item'].setGrid(False)
            self.layout.addItem(self.scales[k]['item'], *self.scales[k]['pos'])

        self.titleLabel = LabelItem('', size='11pt', color='000')
        self.layout.addItem(self.titleLabel, 0, 1)
        #self.setTitle(None)  ## hide

        for i in range(4):
            self.layout.setRowPreferredHeight(i, 0)
            self.layout.setRowMinimumHeight(i, 0)
            self.layout.setRowSpacing(i, 0)
            self.layout.setRowStretchFactor(i, 1)

        for i in range(3):
            self.layout.setColumnPreferredWidth(i, 0)
            self.layout.setColumnMinimumWidth(i, 0)
            self.layout.setColumnSpacing(i, 0)
            self.layout.setColumnStretchFactor(i, 1)
        self.layout.setRowStretchFactor(2, 100)
        self.layout.setColumnStretchFactor(1, 100)

        ## Wrap a few methods from viewBox
        for m in [
                'setXRange', 'setYRange', 'setRange', 'autoRange', 'viewRect',
                'setMouseEnabled'
        ]:
            setattr(self, m, getattr(self.vb, m))

        self.items = []
        self.curves = []
        self.dataItems = []
        self.paramList = {}
        self.avgCurves = {}

        ### Set up context menu

        w = QtGui.QWidget()
        self.ctrl = c = Ui_Form()
        c.setupUi(w)
        dv = QtGui.QDoubleValidator(self)
        self.ctrlMenu = QtGui.QMenu()
        self.menuAction = QtGui.QWidgetAction(self)
        self.menuAction.setDefaultWidget(w)
        self.ctrlMenu.addAction(self.menuAction)

        if HAVE_WIDGETGROUP:
            self.stateGroup = WidgetGroup(self.ctrlMenu)

        self.fileDialog = None

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False

        #self.ctrlBtn.setFixedWidth(60)
        self.setAcceptHoverEvents(True)

        ## Connect control widgets
        #QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMinText.editingFinished.connect(self.setManualXScale)
        #QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMaxText.editingFinished.connect(self.setManualXScale)
        #QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMinText.editingFinished.connect(self.setManualYScale)
        #QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMaxText.editingFinished.connect(self.setManualYScale)

        #QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xManualRadio.clicked.connect(lambda: self.updateXScale())
        #QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yManualRadio.clicked.connect(lambda: self.updateYScale())

        #QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xAutoRadio.clicked.connect(self.updateXScale)
        #QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yAutoRadio.clicked.connect(self.updateYScale)

        #QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.xAutoPercentSpin.valueChanged.connect(self.replot)
        #QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.yAutoPercentSpin.valueChanged.connect(self.replot)

        #QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
        #QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)

        #QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.alphaGroup.toggled.connect(self.updateAlpha)
        #QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
        c.alphaSlider.valueChanged.connect(self.updateAlpha)
        #QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.autoAlphaCheck.toggled.connect(self.updateAlpha)

        #QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        c.gridGroup.toggled.connect(self.updateGrid)
        #QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
        c.gridAlphaSlider.valueChanged.connect(self.updateGrid)

        #QtCore.QObject.connect(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
        c.powerSpectrumGroup.toggled.connect(self.updateSpectrumMode)
        #QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
        c.saveSvgBtn.clicked.connect(self.saveSvgClicked)
        #QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
        c.saveImgBtn.clicked.connect(self.saveImgClicked)
        #QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
        c.saveCsvBtn.clicked.connect(self.saveCsvClicked)

        #QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        #QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)

        #QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
        self.ctrl.xLinkCombo.currentIndexChanged.connect(
            self.xLinkComboChanged)
        #QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
        self.ctrl.yLinkCombo.currentIndexChanged.connect(
            self.yLinkComboChanged)

        #QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
        c.downsampleSpin.valueChanged.connect(self.updateDownsampling)

        #QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
        self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
        #QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
        self.ctrl.averageGroup.toggled.connect(self.avgToggled)

        #QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
        #QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)

        #QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
        self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation)
        #QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
        self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation)
        #QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.xMouseCheck.toggled.connect(self.mouseCheckChanged)
        #QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.yMouseCheck.toggled.connect(self.mouseCheckChanged)

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False
        self.manager = None

        #self.showLabel('right', False)
        #self.showLabel('top', False)
        #self.showLabel('title', False)
        #self.showLabel('left', False)
        #self.showLabel('bottom', False)
        self.showScale('right', False)
        self.showScale('top', False)
        self.showScale('left', True)
        self.showScale('bottom', True)

        if name is not None:
            self.registerPlot(name)

        if labels is not None:
            for k in labels:
                if isinstance(labels[k], basestring):
                    labels[k] = (labels[k], )
                self.setLabel(k, *labels[k])

        if len(kargs) > 0:
            self.plot(**kargs)
Ejemplo n.º 5
0
class PlotItem(QtGui.QGraphicsWidget):

    sigYRangeChanged = QtCore.Signal(object, object)
    sigXRangeChanged = QtCore.Signal(object, object)
    sigRangeChanged = QtCore.Signal(object, object)

    """Plot graphics item that can be added to any graphics scene. Implements axis titles, scales, interactive viewbox."""
    lastFileDir = None
    managers = {}

    def __init__(self, parent=None, name=None, labels=None, **kargs):
        QtGui.QGraphicsWidget.__init__(self, parent)

        ## Set up control buttons

        self.ctrlBtn = QtGui.QToolButton()
        self.ctrlBtn.setText("?")
        self.autoBtn = QtGui.QToolButton()
        self.autoBtn.setText("A")
        self.autoBtn.hide()
        self.proxies = []
        for b in [self.ctrlBtn, self.autoBtn]:
            proxy = QtGui.QGraphicsProxyWidget(self)
            proxy.setWidget(b)
            proxy.setAcceptHoverEvents(False)
            b.setStyleSheet("background-color: #000000; color: #888; font-size: 6pt")
            self.proxies.append(proxy)
        # QtCore.QObject.connect(self.ctrlBtn, QtCore.SIGNAL('clicked()'), self.ctrlBtnClicked)
        self.ctrlBtn.clicked.connect(self.ctrlBtnClicked)
        # QtCore.QObject.connect(self.autoBtn, QtCore.SIGNAL('clicked()'), self.enableAutoScale)
        self.autoBtn.clicked.connect(self.enableAutoScale)

        self.layout = QtGui.QGraphicsGridLayout()
        self.layout.setContentsMargins(1, 1, 1, 1)
        self.setLayout(self.layout)
        self.layout.setHorizontalSpacing(0)
        self.layout.setVerticalSpacing(0)

        self.vb = ViewBox()
        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('xRangeChanged'), self.xRangeChanged)
        self.vb.sigXRangeChanged.connect(self.xRangeChanged)
        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('yRangeChanged'), self.yRangeChanged)
        self.vb.sigYRangeChanged.connect(self.yRangeChanged)
        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('rangeChangedManually'), self.enableManualScale)
        self.vb.sigRangeChangedManually.connect(self.enableManualScale)

        # QtCore.QObject.connect(self.vb, QtCore.SIGNAL('viewChanged'), self.viewChanged)
        self.vb.sigRangeChanged.connect(self.viewRangeChanged)

        self.layout.addItem(self.vb, 2, 1)
        self.alpha = 1.0
        self.autoAlpha = True
        self.spectrumMode = False

        self.autoScale = [True, True]

        ## Create and place scale items
        self.scales = {
            "top": {"item": ScaleItem(orientation="top", linkView=self.vb), "pos": (1, 1)},
            "bottom": {"item": ScaleItem(orientation="bottom", linkView=self.vb), "pos": (3, 1)},
            "left": {"item": ScaleItem(orientation="left", linkView=self.vb), "pos": (2, 0)},
            "right": {"item": ScaleItem(orientation="right", linkView=self.vb), "pos": (2, 2)},
        }
        for k in self.scales:
            self.layout.addItem(self.scales[k]["item"], *self.scales[k]["pos"])

        ## Create and place label items
        # self.labels = {
        #'title':  {'item': LabelItem('title', size='11pt'),  'pos': (0, 2), 'text': ''},
        #'top':    {'item': LabelItem('top'),    'pos': (1, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'bottom': {'item': LabelItem('bottom'), 'pos': (5, 2), 'text': '', 'units': '', 'unitPrefix': ''},
        #'left':   {'item': LabelItem('left'),   'pos': (3, 0), 'text': '', 'units': '', 'unitPrefix': ''},
        #'right':  {'item': LabelItem('right'),  'pos': (3, 4), 'text': '', 'units': '', 'unitPrefix': ''}
        # }
        # self.labels['left']['item'].setAngle(-90)
        # self.labels['right']['item'].setAngle(-90)
        # for k in self.labels:
        # self.layout.addItem(self.labels[k]['item'], *self.labels[k]['pos'])
        self.titleLabel = LabelItem("", size="11pt")
        self.layout.addItem(self.titleLabel, 0, 1)
        self.setTitle(None)  ## hide

        for i in range(4):
            self.layout.setRowPreferredHeight(i, 0)
            self.layout.setRowMinimumHeight(i, 0)
            self.layout.setRowSpacing(i, 0)
            self.layout.setRowStretchFactor(i, 1)

        for i in range(3):
            self.layout.setColumnPreferredWidth(i, 0)
            self.layout.setColumnMinimumWidth(i, 0)
            self.layout.setColumnSpacing(i, 0)
            self.layout.setColumnStretchFactor(i, 1)
        self.layout.setRowStretchFactor(2, 100)
        self.layout.setColumnStretchFactor(1, 100)

        ## Wrap a few methods from viewBox
        for m in ["setXRange", "setYRange", "setRange", "autoRange", "viewRect", "setMouseEnabled"]:
            setattr(self, m, getattr(self.vb, m))

        self.items = []
        self.curves = []
        self.dataItems = []
        self.paramList = {}
        self.avgCurves = {}

        ### Set up context menu

        w = QtGui.QWidget()
        self.ctrl = c = Ui_Form()
        c.setupUi(w)
        dv = QtGui.QDoubleValidator(self)
        self.ctrlMenu = QtGui.QMenu()
        self.menuAction = QtGui.QWidgetAction(self)
        self.menuAction.setDefaultWidget(w)
        self.ctrlMenu.addAction(self.menuAction)

        if HAVE_WIDGETGROUP:
            self.stateGroup = WidgetGroup(self.ctrlMenu)

        self.fileDialog = None

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False

        # self.ctrlBtn.setFixedWidth(60)
        self.setAcceptHoverEvents(True)

        ## Connect control widgets
        # QtCore.QObject.connect(c.xMinText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMinText.editingFinished.connect(self.setManualXScale)
        # QtCore.QObject.connect(c.xMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualXScale)
        c.xMaxText.editingFinished.connect(self.setManualXScale)
        # QtCore.QObject.connect(c.yMinText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMinText.editingFinished.connect(self.setManualYScale)
        # QtCore.QObject.connect(c.yMaxText, QtCore.SIGNAL('editingFinished()'), self.setManualYScale)
        c.yMaxText.editingFinished.connect(self.setManualYScale)

        # QtCore.QObject.connect(c.xManualRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xManualRadio.clicked.connect(lambda: self.updateXScale())
        # QtCore.QObject.connect(c.yManualRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yManualRadio.clicked.connect(lambda: self.updateYScale())

        # QtCore.QObject.connect(c.xAutoRadio, QtCore.SIGNAL('clicked()'), self.updateXScale)
        c.xAutoRadio.clicked.connect(self.updateXScale)
        # QtCore.QObject.connect(c.yAutoRadio, QtCore.SIGNAL('clicked()'), self.updateYScale)
        c.yAutoRadio.clicked.connect(self.updateYScale)

        # QtCore.QObject.connect(c.xAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.xAutoPercentSpin.valueChanged.connect(self.replot)
        # QtCore.QObject.connect(c.yAutoPercentSpin, QtCore.SIGNAL('valueChanged(int)'), self.replot)
        c.yAutoPercentSpin.valueChanged.connect(self.replot)

        # QtCore.QObject.connect(c.xLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setXLog)
        # QtCore.QObject.connect(c.yLogCheck, QtCore.SIGNAL('toggled(bool)'), self.setYLog)

        # QtCore.QObject.connect(c.alphaGroup, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.alphaGroup.toggled.connect(self.updateAlpha)
        # QtCore.QObject.connect(c.alphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateAlpha)
        c.alphaSlider.valueChanged.connect(self.updateAlpha)
        # QtCore.QObject.connect(c.autoAlphaCheck, QtCore.SIGNAL('toggled(bool)'), self.updateAlpha)
        c.autoAlphaCheck.toggled.connect(self.updateAlpha)

        # QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        c.gridGroup.toggled.connect(self.updateGrid)
        # QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)
        c.gridAlphaSlider.valueChanged.connect(self.updateGrid)

        # QtCore.QObject.connect(c.powerSpectrumGroup, QtCore.SIGNAL('toggled(bool)'), self.updateSpectrumMode)
        c.powerSpectrumGroup.toggled.connect(self.updateSpectrumMode)
        # QtCore.QObject.connect(c.saveSvgBtn, QtCore.SIGNAL('clicked()'), self.saveSvgClicked)
        c.saveSvgBtn.clicked.connect(self.saveSvgClicked)
        # QtCore.QObject.connect(c.saveImgBtn, QtCore.SIGNAL('clicked()'), self.saveImgClicked)
        c.saveImgBtn.clicked.connect(self.saveImgClicked)
        # QtCore.QObject.connect(c.saveCsvBtn, QtCore.SIGNAL('clicked()'), self.saveCsvClicked)
        c.saveCsvBtn.clicked.connect(self.saveCsvClicked)

        # QtCore.QObject.connect(c.gridGroup, QtCore.SIGNAL('toggled(bool)'), self.updateGrid)
        # QtCore.QObject.connect(c.gridAlphaSlider, QtCore.SIGNAL('valueChanged(int)'), self.updateGrid)

        # QtCore.QObject.connect(self.ctrl.xLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.xLinkComboChanged)
        self.ctrl.xLinkCombo.currentIndexChanged.connect(self.xLinkComboChanged)
        # QtCore.QObject.connect(self.ctrl.yLinkCombo, QtCore.SIGNAL('currentIndexChanged(int)'), self.yLinkComboChanged)
        self.ctrl.yLinkCombo.currentIndexChanged.connect(self.yLinkComboChanged)

        # QtCore.QObject.connect(c.downsampleSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDownsampling)
        c.downsampleSpin.valueChanged.connect(self.updateDownsampling)

        # QtCore.QObject.connect(self.ctrl.avgParamList, QtCore.SIGNAL('itemClicked(QListWidgetItem*)'), self.avgParamListClicked)
        self.ctrl.avgParamList.itemClicked.connect(self.avgParamListClicked)
        # QtCore.QObject.connect(self.ctrl.averageGroup, QtCore.SIGNAL('toggled(bool)'), self.avgToggled)
        self.ctrl.averageGroup.toggled.connect(self.avgToggled)

        # QtCore.QObject.connect(self.ctrl.pointsGroup, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)
        # QtCore.QObject.connect(self.ctrl.autoPointsCheck, QtCore.SIGNAL('toggled(bool)'), self.updatePointMode)

        # QtCore.QObject.connect(self.ctrl.maxTracesCheck, QtCore.SIGNAL('toggled(bool)'), self.updateDecimation)
        self.ctrl.maxTracesCheck.toggled.connect(self.updateDecimation)
        # QtCore.QObject.connect(self.ctrl.maxTracesSpin, QtCore.SIGNAL('valueChanged(int)'), self.updateDecimation)
        self.ctrl.maxTracesSpin.valueChanged.connect(self.updateDecimation)
        # QtCore.QObject.connect(c.xMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.xMouseCheck.toggled.connect(self.mouseCheckChanged)
        # QtCore.QObject.connect(c.yMouseCheck, QtCore.SIGNAL('toggled(bool)'), self.mouseCheckChanged)
        c.yMouseCheck.toggled.connect(self.mouseCheckChanged)

        self.xLinkPlot = None
        self.yLinkPlot = None
        self.linksBlocked = False
        self.manager = None

        # self.showLabel('right', False)
        # self.showLabel('top', False)
        # self.showLabel('title', False)
        # self.showLabel('left', False)
        # self.showLabel('bottom', False)
        self.showScale("right", False)
        self.showScale("top", False)
        self.showScale("left", True)
        self.showScale("bottom", True)

        if name is not None:
            self.registerPlot(name)

        if labels is not None:
            for k in labels:
                if isinstance(labels[k], basestring):
                    labels[k] = (labels[k],)
                self.setLabel(k, *labels[k])

        if len(kargs) > 0:
            self.plot(**kargs)

    # def paint(self, *args):
    # prof = debug.Profiler('PlotItem.paint', disabled=True)
    # QtGui.QGraphicsWidget.paint(self, *args)
    # prof.finish()

    def close(self):
        # print "delete", self
        ## All this crap is needed to avoid PySide trouble.
        ## The problem seems to be whenever scene.clear() leads to deletion of widgets (either through proxies or qgraphicswidgets)
        ## the solution is to manually remove all widgets before scene.clear() is called
        if self.ctrlMenu is None:  ## already shut down
            return
        self.ctrlMenu.setParent(None)
        self.ctrlMenu = None

        self.ctrlBtn.setParent(None)
        self.ctrlBtn = None
        self.autoBtn.setParent(None)
        self.autoBtn = None

        for k in self.scales:
            i = self.scales[k]["item"]
            i.close()

        self.scales = None
        self.scene().removeItem(self.vb)
        self.vb = None
        for i in range(self.layout.count()):
            self.layout.removeAt(i)

        for p in self.proxies:
            try:
                p.setWidget(None)
            except RuntimeError:
                break
            self.scene().removeItem(p)
        self.proxies = []

        self.menuAction.releaseWidget(self.menuAction.defaultWidget())
        self.menuAction.setParent(None)
        self.menuAction = None

        if self.manager is not None:
            self.manager.sigWidgetListChanged.disconnect(self.updatePlotList)
            self.manager.removeWidget(self.name)
        # else:
        # print "no manager"

    def registerPlot(self, name):
        self.name = name
        win = str(self.window())
        # print "register", name, win
        if win not in PlotItem.managers:
            PlotItem.managers[win] = PlotWidgetManager()
        self.manager = PlotItem.managers[win]
        self.manager.addWidget(self, name)
        # QtCore.QObject.connect(self.manager, QtCore.SIGNAL('widgetListChanged'), self.updatePlotList)
        self.manager.sigWidgetListChanged.connect(self.updatePlotList)
        self.updatePlotList()

    def updatePlotList(self):
        """Update the list of all plotWidgets in the "link" combos"""
        # print "update plot list", self
        try:
            for sc in [self.ctrl.xLinkCombo, self.ctrl.yLinkCombo]:
                current = str(sc.currentText())
                sc.clear()
                sc.addItem("")
                if self.manager is not None:
                    for w in self.manager.listWidgets():
                        # print w
                        if w == self.name:
                            continue
                        sc.addItem(w)
        except:
            import gc

            refs = gc.get_referrers(self)
            print "  error during update of", self
            print "  Referrers are:", refs
            raise

    def updateGrid(self, *args):
        g = self.ctrl.gridGroup.isChecked()
        if g:
            g = self.ctrl.gridAlphaSlider.value()
        for k in self.scales:
            self.scales[k]["item"].setGrid(g)

    def viewGeometry(self):
        """return the screen geometry of the viewbox"""
        v = self.scene().views()[0]
        b = self.vb.mapRectToScene(self.vb.boundingRect())
        wr = v.mapFromScene(b).boundingRect()
        pos = v.mapToGlobal(v.pos())
        wr.adjust(pos.x(), pos.y(), pos.x(), pos.y())
        return wr

    def viewRangeChanged(self, vb, range):
        # self.emit(QtCore.SIGNAL('viewChanged'), *args)
        self.sigRangeChanged.emit(self, range)

    def blockLink(self, b):
        self.linksBlocked = b

    def xLinkComboChanged(self):
        self.setXLink(str(self.ctrl.xLinkCombo.currentText()))

    def yLinkComboChanged(self):
        self.setYLink(str(self.ctrl.yLinkCombo.currentText()))

    def setXLink(self, plot=None):
        """Link this plot's X axis to another plot (pass either the PlotItem/PlotWidget or the registered name of the plot)"""
        if isinstance(plot, basestring):
            if self.manager is None:
                return
            if self.xLinkPlot is not None:
                self.manager.unlinkX(self, self.xLinkPlot)
            plot = self.manager.getWidget(plot)
        if not isinstance(plot, PlotItem) and hasattr(plot, "getPlotItem"):
            plot = plot.getPlotItem()
        self.xLinkPlot = plot
        if plot is not None:
            self.setManualXScale()
            self.manager.linkX(self, plot)

    def setYLink(self, plot=None):
        """Link this plot's Y axis to another plot (pass either the PlotItem/PlotWidget or the registered name of the plot)"""
        if isinstance(plot, basestring):
            if self.manager is None:
                return
            if self.yLinkPlot is not None:
                self.manager.unlinkY(self, self.yLinkPlot)
            plot = self.manager.getWidget(plot)
        if not isinstance(plot, PlotItem) and hasattr(plot, "getPlotItem"):
            plot = plot.getPlotItem()
        self.yLinkPlot = plot
        if plot is not None:
            self.setManualYScale()
            self.manager.linkY(self, plot)

    def linkXChanged(self, plot):
        """Called when a linked plot has changed its X scale"""
        # print "update from", plot
        if self.linksBlocked:
            return
        pr = plot.vb.viewRect()
        pg = plot.viewGeometry()
        if pg is None:
            # print "   return early"
            return
        sg = self.viewGeometry()
        upp = float(pr.width()) / pg.width()
        x1 = pr.left() + (sg.x() - pg.x()) * upp
        x2 = x1 + sg.width() * upp
        plot.blockLink(True)
        self.setManualXScale()
        self.setXRange(x1, x2, padding=0)
        plot.blockLink(False)
        self.replot()

    def linkYChanged(self, plot):
        """Called when a linked plot has changed its Y scale"""
        if self.linksBlocked:
            return
        pr = plot.vb.viewRect()
        pg = plot.vb.boundingRect()
        sg = self.vb.boundingRect()
        upp = float(pr.height()) / pg.height()
        y1 = pr.bottom() + (sg.y() - pg.y()) * upp
        y2 = y1 + sg.height() * upp
        plot.blockLink(True)
        self.setManualYScale()
        self.setYRange(y1, y2, padding=0)
        plot.blockLink(False)
        self.replot()

    def avgToggled(self, b):
        if b:
            self.recomputeAverages()
        for k in self.avgCurves:
            self.avgCurves[k][1].setVisible(b)

    def avgParamListClicked(self, item):
        name = str(item.text())
        self.paramList[name] = item.checkState() == QtCore.Qt.Checked
        self.recomputeAverages()

    def recomputeAverages(self):
        if not self.ctrl.averageGroup.isChecked():
            return
        for k in self.avgCurves:
            self.removeItem(self.avgCurves[k][1])
            # Qwt.QwtPlotCurve.detach(self.avgCurves[k][1])
        self.avgCurves = {}
        for c in self.curves:
            self.addAvgCurve(c)
        self.replot()

    def addAvgCurve(self, curve):
        """Add a single curve into the pool of curves averaged together"""

        ## If there are plot parameters, then we need to determine which to average together.
        remKeys = []
        addKeys = []
        if self.ctrl.avgParamList.count() > 0:

            ### First determine the key of the curve to which this new data should be averaged
            for i in range(self.ctrl.avgParamList.count()):
                item = self.ctrl.avgParamList.item(i)
                if item.checkState() == QtCore.Qt.Checked:
                    remKeys.append(str(item.text()))
                else:
                    addKeys.append(str(item.text()))

            if len(remKeys) < 1:  ## In this case, there would be 1 average plot for each data plot; not useful.
                return

        p = curve.meta().copy()
        for k in p:
            if type(k) is tuple:
                p[".".join(k)] = p[k]
                del p[k]
        for rk in remKeys:
            if rk in p:
                del p[rk]
        for ak in addKeys:
            if ak not in p:
                p[ak] = None
        key = tuple(p.items())

        ### Create a new curve if needed
        if key not in self.avgCurves:
            plot = PlotCurveItem()
            plot.setPen(mkPen([0, 200, 0]))
            plot.setShadowPen(mkPen([0, 0, 0, 100], 3))
            plot.setAlpha(1.0, False)
            plot.setZValue(100)
            self.addItem(plot)
            # Qwt.QwtPlotCurve.attach(plot, self)
            self.avgCurves[key] = [0, plot]
        self.avgCurves[key][0] += 1
        (n, plot) = self.avgCurves[key]

        ### Average data together
        (x, y) = curve.getData()
        if plot.yData is not None:
            newData = plot.yData * (n - 1) / float(n) + y * 1.0 / float(n)
            plot.setData(plot.xData, newData)
        else:
            plot.setData(x, y)

    def mouseCheckChanged(self):
        state = [self.ctrl.xMouseCheck.isChecked(), self.ctrl.yMouseCheck.isChecked()]
        self.vb.setMouseEnabled(*state)

    def xRangeChanged(self, _, range):
        if any(np.isnan(range)) or any(np.isinf(range)):
            raise Exception("yRange invalid: %s. Signal came from %s" % (str(range), str(self.sender())))
        self.ctrl.xMinText.setText("%0.5g" % range[0])
        self.ctrl.xMaxText.setText("%0.5g" % range[1])

        ## automatically change unit scale
        maxVal = max(abs(range[0]), abs(range[1]))
        (scale, prefix) = siScale(maxVal)
        # for l in ['top', 'bottom']:
        # if self.getLabel(l).isVisible():
        # self.setLabel(l, unitPrefix=prefix)
        # self.getScale(l).setScale(scale)
        # else:
        # self.setLabel(l, unitPrefix='')
        # self.getScale(l).setScale(1.0)

        # self.emit(QtCore.SIGNAL('xRangeChanged'), self, range)
        self.sigXRangeChanged.emit(self, range)

    def yRangeChanged(self, _, range):
        if any(np.isnan(range)) or any(np.isinf(range)):
            raise Exception("yRange invalid: %s. Signal came from %s" % (str(range), str(self.sender())))
        self.ctrl.yMinText.setText("%0.5g" % range[0])
        self.ctrl.yMaxText.setText("%0.5g" % range[1])

        ## automatically change unit scale
        maxVal = max(abs(range[0]), abs(range[1]))
        (scale, prefix) = siScale(maxVal)
        # for l in ['left', 'right']:
        # if self.getLabel(l).isVisible():
        # self.setLabel(l, unitPrefix=prefix)
        # self.getScale(l).setScale(scale)
        # else:
        # self.setLabel(l, unitPrefix='')
        # self.getScale(l).setScale(1.0)
        # self.emit(QtCore.SIGNAL('yRangeChanged'), self, range)
        self.sigYRangeChanged.emit(self, range)

    def enableAutoScale(self):
        self.ctrl.xAutoRadio.setChecked(True)
        self.ctrl.yAutoRadio.setChecked(True)
        self.autoBtn.hide()
        self.updateXScale()
        self.updateYScale()
        self.replot()

    def updateXScale(self):
        """Set plot to autoscale or not depending on state of radio buttons"""
        if self.ctrl.xManualRadio.isChecked():
            self.setManualXScale()
        else:
            self.setAutoXScale()
        self.replot()

    def updateYScale(self, b=False):
        """Set plot to autoscale or not depending on state of radio buttons"""
        if self.ctrl.yManualRadio.isChecked():
            self.setManualYScale()
        else:
            self.setAutoYScale()
        self.replot()

    def enableManualScale(self, v=[True, True]):
        if v[0]:
            self.autoScale[0] = False
            self.ctrl.xManualRadio.setChecked(True)
            # self.setManualXScale()
        if v[1]:
            self.autoScale[1] = False
            self.ctrl.yManualRadio.setChecked(True)
            # self.setManualYScale()
        self.autoBtn.show()
        # self.replot()

    def setManualXScale(self):
        self.autoScale[0] = False
        x1 = float(self.ctrl.xMinText.text())
        x2 = float(self.ctrl.xMaxText.text())
        self.ctrl.xManualRadio.setChecked(True)
        self.setXRange(x1, x2, padding=0)
        self.autoBtn.show()
        # self.replot()

    def setManualYScale(self):
        self.autoScale[1] = False
        y1 = float(self.ctrl.yMinText.text())
        y2 = float(self.ctrl.yMaxText.text())
        self.ctrl.yManualRadio.setChecked(True)
        self.setYRange(y1, y2, padding=0)
        self.autoBtn.show()
        # self.replot()

    def setAutoXScale(self):
        self.autoScale[0] = True
        self.ctrl.xAutoRadio.setChecked(True)
        # self.replot()

    def setAutoYScale(self):
        self.autoScale[1] = True
        self.ctrl.yAutoRadio.setChecked(True)
        # self.replot()

    def addItem(self, item, *args):
        self.items.append(item)
        self.vb.addItem(item, *args)

    def removeItem(self, item):
        if not item in self.items:
            return
        self.items.remove(item)
        if item in self.dataItems:
            self.dataItems.remove(item)

        if item.scene() is not None:
            self.vb.removeItem(item)
        if item in self.curves:
            self.curves.remove(item)
            self.updateDecimation()
            self.updateParamList()
            # item.connect(item, QtCore.SIGNAL('plotChanged'), self.plotChanged)
            item.sigPlotChanged.connect(self.plotChanged)

    def clear(self):
        for i in self.items[:]:
            self.removeItem(i)
        self.avgCurves = {}

    def clearPlots(self):
        for i in self.curves[:]:
            self.removeItem(i)
        self.avgCurves = {}

    def plot(self, data=None, data2=None, x=None, y=None, clear=False, params=None, pen=None):
        """Add a new plot curve. Data may be specified a few ways:
        plot(yVals)   # x vals will be integers
        plot(xVals, yVals)
        plot(y=yVals, x=xVals)
        """
        if y is not None:
            data = y
        if data2 is not None:
            x = data
            data = data2

        if clear:
            self.clear()
        if params is None:
            params = {}
        if HAVE_METAARRAY and isinstance(data, MetaArray):
            curve = self._plotMetaArray(data, x=x)
        elif isinstance(data, np.ndarray):
            curve = self._plotArray(data, x=x)
        elif isinstance(data, list):
            if x is not None:
                x = np.array(x)
            curve = self._plotArray(np.array(data), x=x)
        elif data is None:
            curve = PlotCurveItem()
        else:
            raise Exception("Not sure how to plot object of type %s" % type(data))

        # print data, curve
        self.addCurve(curve, params)
        if pen is not None:
            curve.setPen(mkPen(pen))

        return curve

    def addDataItem(self, item):
        self.addItem(item)
        self.dataItems.append(item)

    def addCurve(self, c, params=None):
        if params is None:
            params = {}
        c.setMeta(params)
        self.curves.append(c)
        # Qwt.QwtPlotCurve.attach(c, self)
        self.addItem(c)

        ## configure curve for this plot
        (alpha, auto) = self.alphaState()
        c.setAlpha(alpha, auto)
        c.setSpectrumMode(self.ctrl.powerSpectrumGroup.isChecked())
        c.setDownsampling(self.downsampleMode())
        c.setPointMode(self.pointMode())

        ## Hide older plots if needed
        self.updateDecimation()

        ## Add to average if needed
        self.updateParamList()
        if self.ctrl.averageGroup.isChecked():
            self.addAvgCurve(c)

        # c.connect(c, QtCore.SIGNAL('plotChanged'), self.plotChanged)
        c.sigPlotChanged.connect(self.plotChanged)
        self.plotChanged()

    def plotChanged(self, curve=None):
        ## Recompute auto range if needed
        for ax in [0, 1]:
            if self.autoScale[ax]:
                percentScale = [self.ctrl.xAutoPercentSpin.value(), self.ctrl.yAutoPercentSpin.value()][ax] * 0.01
                mn = None
                mx = None
                for c in self.curves + [c[1] for c in self.avgCurves.values()] + self.dataItems:
                    if not c.isVisible():
                        continue
                    cmn, cmx = c.getRange(ax, percentScale)
                    if mn is None or cmn < mn:
                        mn = cmn
                    if mx is None or cmx > mx:
                        mx = cmx
                if mn is None or mx is None or any(np.isnan([mn, mx])) or any(np.isinf([mn, mx])):
                    continue
                if mn == mx:
                    mn -= 1
                    mx += 1
                self.setRange(ax, mn, mx)
                # print "Auto range:", ax, mn, mx

    def replot(self):
        self.plotChanged()
        self.update()

    def updateParamList(self):
        self.ctrl.avgParamList.clear()
        ## Check to see that each parameter for each curve is present in the list
        # print "\nUpdate param list", self
        # print "paramList:", self.paramList
        for c in self.curves:
            # print "  curve:", c
            for p in c.meta().keys():
                # print "    param:", p
                if type(p) is tuple:
                    p = ".".join(p)

                ## If the parameter is not in the list, add it.
                matches = self.ctrl.avgParamList.findItems(p, QtCore.Qt.MatchExactly)
                # print "      matches:", matches
                if len(matches) == 0:
                    i = QtGui.QListWidgetItem(p)
                    if p in self.paramList and self.paramList[p] is True:
                        # print "      set checked"
                        i.setCheckState(QtCore.Qt.Checked)
                    else:
                        # print "      set unchecked"
                        i.setCheckState(QtCore.Qt.Unchecked)
                    self.ctrl.avgParamList.addItem(i)
                else:
                    i = matches[0]

                self.paramList[p] = i.checkState() == QtCore.Qt.Checked
        # print "paramList:", self.paramList

    ## This is bullshit.
    def writeSvg(self, fileName=None):
        if fileName is None:
            fileName = QtGui.QFileDialog.getSaveFileName()
            if isinstance(fileName, tuple):
                raise Exception("Not implemented yet..")
        fileName = str(fileName)
        PlotItem.lastFileDir = os.path.dirname(fileName)

        rect = self.vb.viewRect()
        xRange = rect.left(), rect.right()

        svg = ""
        fh = open(fileName, "w")

        dx = max(rect.right(), 0) - min(rect.left(), 0)
        ymn = min(rect.top(), rect.bottom())
        ymx = max(rect.top(), rect.bottom())
        dy = max(ymx, 0) - min(ymn, 0)
        sx = 1.0
        sy = 1.0
        while dx * sx < 10:
            sx *= 1000
        while dy * sy < 10:
            sy *= 1000
        sy *= -1

        # fh.write('<svg viewBox="%f %f %f %f">\n' % (rect.left()*sx, rect.top()*sx, rect.width()*sy, rect.height()*sy))
        fh.write("<svg>\n")
        fh.write(
            '<path fill="none" stroke="#000000" stroke-opacity="0.5" stroke-width="1" d="M%f,0 L%f,0"/>\n'
            % (rect.left() * sx, rect.right() * sx)
        )
        fh.write(
            '<path fill="none" stroke="#000000" stroke-opacity="0.5" stroke-width="1" d="M0,%f L0,%f"/>\n'
            % (rect.top() * sy, rect.bottom() * sy)
        )

        for item in self.curves:
            if isinstance(item, PlotCurveItem):
                color = colorStr(item.pen.color())
                opacity = item.pen.color().alpha() / 255.0
                color = color[:6]
                x, y = item.getData()
                mask = (x > xRange[0]) * (x < xRange[1])
                mask[:-1] += mask[1:]
                m2 = mask.copy()
                mask[1:] += m2[:-1]
                x = x[mask]
                y = y[mask]

                x *= sx
                y *= sy

                # fh.write('<g fill="none" stroke="#%s" stroke-opacity="1" stroke-width="1">\n' % color)
                fh.write(
                    '<path fill="none" stroke="#%s" stroke-opacity="%f" stroke-width="1" d="M%f,%f '
                    % (color, opacity, x[0], y[0])
                )
                for i in xrange(1, len(x)):
                    fh.write("L%f,%f " % (x[i], y[i]))

                fh.write('"/>')
                # fh.write("</g>")
        for item in self.dataItems:
            if isinstance(item, ScatterPlotItem):

                pRect = item.boundingRect()
                vRect = pRect.intersected(rect)

                for point in item.points():
                    pos = point.pos()
                    if not rect.contains(pos):
                        continue
                    color = colorStr(point.brush.color())
                    opacity = point.brush.color().alpha() / 255.0
                    color = color[:6]
                    x = pos.x() * sx
                    y = pos.y() * sy

                    fh.write(
                        '<circle cx="%f" cy="%f" r="1" fill="#%s" stroke="none" fill-opacity="%f"/>\n'
                        % (x, y, color, opacity)
                    )
                    # fh.write('<path fill="none" stroke="#%s" stroke-opacity="%f" stroke-width="1" d="M%f,%f ' % (color, opacity, x[0], y[0]))
                    # for i in xrange(1, len(x)):
                    # fh.write('L%f,%f ' % (x[i], y[i]))

                    # fh.write('"/>')

        ## get list of curves, scatter plots

        fh.write("</svg>\n")

    # def writeSvg(self, fileName=None):
    # if fileName is None:
    # fileName = QtGui.QFileDialog.getSaveFileName()
    # fileName = str(fileName)
    # PlotItem.lastFileDir = os.path.dirname(fileName)

    # self.svg = QtSvg.QSvgGenerator()
    # self.svg.setFileName(fileName)
    # res = 120.
    # view = self.scene().views()[0]
    # bounds = view.viewport().rect()
    # bounds = QtCore.QRectF(0, 0, bounds.width(), bounds.height())

    # self.svg.setResolution(res)
    # self.svg.setViewBox(bounds)

    # self.svg.setSize(QtCore.QSize(bounds.width(), bounds.height()))

    # painter = QtGui.QPainter(self.svg)
    # view.render(painter, bounds)

    # painter.end()

    ### Workaround to set pen widths correctly
    # import re
    # data = open(fileName).readlines()
    # for i in range(len(data)):
    # line = data[i]
    # m = re.match(r'(<g .*)stroke-width="1"(.*transform="matrix\(([^\)]+)\)".*)', line)
    # if m is not None:
    ##print "Matched group:", line
    # g = m.groups()
    # matrix = map(float, g[2].split(','))
    ##print "matrix:", matrix
    # scale = max(abs(matrix[0]), abs(matrix[3]))
    # if scale == 0 or scale == 1.0:
    # continue
    # data[i] = g[0] + ' stroke-width="%0.2g" ' % (1.0/scale) + g[1] + '\n'
    ##print "old line:", line
    ##print "new line:", data[i]
    # open(fileName, 'w').write(''.join(data))

    def writeImage(self, fileName=None):
        if fileName is None:
            fileName = QtGui.QFileDialog.getSaveFileName()
            if isinstance(fileName, tuple):
                raise Exception("Not implemented yet..")
        fileName = str(fileName)
        PlotItem.lastFileDir = os.path.dirname(fileName)
        self.png = QtGui.QImage(int(self.size().width()), int(self.size().height()), QtGui.QImage.Format_ARGB32)
        painter = QtGui.QPainter(self.png)
        painter.setRenderHints(painter.Antialiasing | painter.TextAntialiasing)
        self.scene().render(painter, QtCore.QRectF(), self.mapRectToScene(self.boundingRect()))
        painter.end()
        self.png.save(fileName)

    def writeCsv(self, fileName=None):
        if fileName is None:
            fileName = QtGui.QFileDialog.getSaveFileName()
        fileName = str(fileName)
        PlotItem.lastFileDir = os.path.dirname(fileName)

        fd = open(fileName, "w")
        data = [c.getData() for c in self.curves]
        i = 0
        while True:
            done = True
            for d in data:
                if i < len(d[0]):
                    fd.write("%g,%g," % (d[0][i], d[1][i]))
                    done = False
                else:
                    fd.write(" , ,")
            fd.write("\n")
            if done:
                break
            i += 1
        fd.close()

    def saveState(self):
        if not HAVE_WIDGETGROUP:
            raise Exception("State save/restore requires WidgetGroup class.")
        state = self.stateGroup.state()
        state["paramList"] = self.paramList.copy()
        # print "\nSAVE %s:\n" % str(self.name), state
        # print "Saving state. averageGroup.isChecked(): %s  state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
        return state

    def restoreState(self, state):
        if not HAVE_WIDGETGROUP:
            raise Exception("State save/restore requires WidgetGroup class.")
        if "paramList" in state:
            self.paramList = state["paramList"].copy()

        self.stateGroup.setState(state)
        self.updateSpectrumMode()
        self.updateDownsampling()
        self.updateAlpha()
        self.updateDecimation()

        self.stateGroup.setState(state)
        self.updateXScale()
        self.updateYScale()
        self.updateParamList()

        # print "\nRESTORE %s:\n" % str(self.name), state
        # print "Restoring state. averageGroup.isChecked(): %s  state: %s" % (str(self.ctrl.averageGroup.isChecked()), str(state['averageGroup']))
        # avg = self.ctrl.averageGroup.isChecked()
        # if avg != state['averageGroup']:
        # print "  WARNING: avgGroup is %s, should be %s" % (str(avg), str(state['averageGroup']))

    def widgetGroupInterface(self):
        return (None, PlotItem.saveState, PlotItem.restoreState)

    def updateSpectrumMode(self, b=None):
        if b is None:
            b = self.ctrl.powerSpectrumGroup.isChecked()
        for c in self.curves:
            c.setSpectrumMode(b)
        self.enableAutoScale()
        self.recomputeAverages()

    def updateDownsampling(self):
        ds = self.downsampleMode()
        for c in self.curves:
            c.setDownsampling(ds)
        self.recomputeAverages()
        # for c in self.avgCurves.values():
        # c[1].setDownsampling(ds)

    def downsampleMode(self):
        if self.ctrl.decimateGroup.isChecked():
            if self.ctrl.manualDecimateRadio.isChecked():
                ds = self.ctrl.downsampleSpin.value()
            else:
                ds = True
        else:
            ds = False
        return ds

    def updateDecimation(self):
        if self.ctrl.maxTracesCheck.isChecked():
            numCurves = self.ctrl.maxTracesSpin.value()
        else:
            numCurves = -1

        curves = self.curves[:]
        split = len(curves) - numCurves
        for i in range(len(curves)):
            if numCurves == -1 or i >= split:
                curves[i].show()
            else:
                if self.ctrl.forgetTracesCheck.isChecked():
                    curves[i].free()
                    self.removeItem(curves[i])
                else:
                    curves[i].hide()

    def updateAlpha(self, *args):
        (alpha, auto) = self.alphaState()
        for c in self.curves:
            c.setAlpha(alpha ** 2, auto)

        # self.replot(autoRange=False)

    def alphaState(self):
        enabled = self.ctrl.alphaGroup.isChecked()
        auto = self.ctrl.autoAlphaCheck.isChecked()
        alpha = float(self.ctrl.alphaSlider.value()) / self.ctrl.alphaSlider.maximum()
        if auto:
            alpha = 1.0  ## should be 1/number of overlapping plots
        if not enabled:
            auto = False
            alpha = 1.0
        return (alpha, auto)

    def pointMode(self):
        if self.ctrl.pointsGroup.isChecked():
            if self.ctrl.autoPointsCheck.isChecked():
                mode = None
            else:
                mode = True
        else:
            mode = False
        return mode

    # def wheelEvent(self, ev):
    # disables panning the whole scene by mousewheel
    # ev.accept()

    def resizeEvent(self, ev):
        if self.ctrlBtn is None:  ## already closed down
            return
        self.ctrlBtn.move(0, self.size().height() - self.ctrlBtn.size().height())
        self.autoBtn.move(self.ctrlBtn.width(), self.size().height() - self.autoBtn.size().height())

    def hoverMoveEvent(self, ev):
        self.mousePos = ev.pos()
        self.mouseScreenPos = ev.screenPos()

    def ctrlBtnClicked(self):
        self.ctrlMenu.popup(self.mouseScreenPos)

    def getLabel(self, key):
        pass

    def _checkScaleKey(self, key):
        if key not in self.scales:
            raise Exception("Scale '%s' not found. Scales are: %s" % (key, str(self.scales.keys())))

    def getScale(self, key):
        self._checkScaleKey(key)
        return self.scales[key]["item"]

    def setLabel(self, key, text=None, units=None, unitPrefix=None, **args):
        self.getScale(key).setLabel(text=text, units=units, unitPrefix=unitPrefix, **args)

    def showLabel(self, key, show=True):
        self.getScale(key).showLabel(show)

    def setTitle(self, title=None, **args):
        if title is None:
            self.titleLabel.setVisible(False)
            self.layout.setRowFixedHeight(0, 0)
            self.titleLabel.setMaximumHeight(0)
        else:
            self.titleLabel.setMaximumHeight(30)
            self.layout.setRowFixedHeight(0, 30)
            self.titleLabel.setVisible(True)
            self.titleLabel.setText(title, **args)

    def showScale(self, key, show=True):
        s = self.getScale(key)
        p = self.scales[key]["pos"]
        if show:
            s.show()
        else:
            s.hide()

    def _plotArray(self, arr, x=None):
        if arr.ndim != 1:
            raise Exception("Array must be 1D to plot (shape is %s)" % arr.shape)
        if x is None:
            x = np.arange(arr.shape[0])
        if x.ndim != 1:
            raise Exception("X array must be 1D to plot (shape is %s)" % x.shape)
        c = PlotCurveItem(arr, x=x)
        return c

    def _plotMetaArray(self, arr, x=None, autoLabel=True):
        inf = arr.infoCopy()
        if arr.ndim != 1:
            raise Exception("can only automatically plot 1 dimensional arrays.")
        ## create curve
        try:
            xv = arr.xvals(0)
            # print 'xvals:', xv
        except:
            if x is None:
                xv = arange(arr.shape[0])
            else:
                xv = x
        c = PlotCurveItem()
        c.setData(x=xv, y=arr.view(np.ndarray))

        if autoLabel:
            name = arr._info[0].get("name", None)
            units = arr._info[0].get("units", None)
            self.setLabel("bottom", text=name, units=units)

            name = arr._info[1].get("name", None)
            units = arr._info[1].get("units", None)
            self.setLabel("left", text=name, units=units)

        return c

    def saveSvgClicked(self):
        self.fileDialog = QtGui.QFileDialog()
        # if PlotItem.lastFileDir is not None:
        # self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        if PlotItem.lastFileDir is not None:
            self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.show()
        # QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeSvg)
        self.fileDialog.fileSelected.connect(self.writeSvg)

    # def svgFileSelected(self, fileName):
    ##PlotWidget.lastFileDir = os.path.split(fileName)[0]
    # self.writeSvg(str(fileName))

    def saveImgClicked(self):
        self.fileDialog = QtGui.QFileDialog()
        # if PlotItem.lastFileDir is not None:
        # self.fileDialog.setDirectory(PlotItem.lastFileDir)
        if PlotItem.lastFileDir is not None:
            self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        self.fileDialog.show()
        # QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeImage)
        self.fileDialog.fileSelected.connect(self.writeImage)

    def saveCsvClicked(self):
        self.fileDialog = QtGui.QFileDialog()
        # if PlotItem.lastFileDir is not None:
        # self.fileDialog.setDirectory(PlotItem.lastFileDir)
        if PlotItem.lastFileDir is not None:
            self.fileDialog.setDirectory(PlotItem.lastFileDir)
        self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
        self.fileDialog.show()
        # QtCore.QObject.connect(self.fileDialog, QtCore.SIGNAL('fileSelected(const QString)'), self.writeCsv)
        self.fileDialog.fileSelected.connect(self.writeCsv)