diff --git a/examples/ScatterPlotSpeedTest.py b/examples/ScatterPlotSpeedTest.py new file mode 100644 index 00000000..f4edf369 --- /dev/null +++ b/examples/ScatterPlotSpeedTest.py @@ -0,0 +1,55 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## Add path to library (just for examples; you do not need this) +import sys, os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) + + +from pyqtgraph.Qt import QtGui, QtCore +import numpy as np +import pyqtgraph as pg +from pyqtgraph.ptime import time +#QtGui.QApplication.setGraphicsSystem('raster') +app = QtGui.QApplication([]) +#mw = QtGui.QMainWindow() +#mw.resize(800,800) + +p = pg.plot() +p.setRange(QtCore.QRectF(0, -10, 5000, 20)) +p.setLabel('bottom', 'Index', units='B') + +#curve.setFillBrush((0, 0, 100, 100)) +#curve.setFillLevel(0) + +#lr = pg.LinearRegionItem([100, 4900]) +#p.addItem(lr) + +data = np.random.normal(size=(50,5000)) +ptr = 0 +lastTime = time() +fps = None +def update(): + global curve, data, ptr, p, lastTime, fps + p.clear() + curve = pg.ScatterPlotItem(x=data[ptr%10], y=data[(ptr+1)%10], pen='w', brush='b', size=10, pxMode=True, identical=True) + p.addItem(curve) + ptr += 1 + now = time() + dt = now - lastTime + lastTime = now + if fps is None: + fps = 1.0/dt + else: + s = np.clip(dt*3., 0, 1) + fps = fps * (1-s) + (1.0/dt) * s + p.setTitle('%0.2f fps' % fps) + app.processEvents() ## force complete redraw for every plot +timer = QtCore.QTimer() +timer.timeout.connect(update) +timer.start(0) + + + +## Start Qt event loop unless running in interactive mode. +if sys.flags.interactive != 1: + app.exec_() diff --git a/functions.py b/functions.py index 9b41da4c..92b52b8f 100644 --- a/functions.py +++ b/functions.py @@ -277,10 +277,10 @@ def mkPen(*args, **kargs): pen.setStyle(style) return pen -def hsvColor(h, s=1.0, v=1.0, a=1.0): - """Generate a QColor from HSVa values.""" +def hsvColor(hue, sat=1.0, val=1.0, alpha=1.0): + """Generate a QColor from HSVa values. (all arguments are float 0.0-1.0)""" c = QtGui.QColor() - c.setHsvF(h, s, v, a) + c.setHsvF(hue, sat, val, alpha) return c diff --git a/graphicsItems/ScatterPlotItem.py b/graphicsItems/ScatterPlotItem.py index f63bd468..0ae9f14c 100644 --- a/graphicsItems/ScatterPlotItem.py +++ b/graphicsItems/ScatterPlotItem.py @@ -6,6 +6,30 @@ import numpy as np import scipy.stats __all__ = ['ScatterPlotItem', 'SpotItem'] + + +## Build all symbol paths +Symbols = {name: QtGui.QPainterPath() for name in ['o', 's', 't', 'd', '+']} + +Symbols['o'].addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1)) +Symbols['s'].addRect(QtCore.QRectF(-0.5, -0.5, 1, 1)) +coords = { + 't': [(-0.5, -0.5), (0, 0.5), (0.5, -0.5)], + 'd': [(0., -0.5), (-0.4, 0.), (0, 0.5), (0.4, 0)], + '+': [ + (-0.5, -0.05), (-0.5, 0.05), (-0.05, 0.05), (-0.05, 0.5), + (0.05, 0.5), (0.05, 0.05), (0.5, 0.05), (0.5, -0.05), + (0.05, -0.05), (0.05, -0.5), (-0.05, -0.5), (-0.05, -0.05) + ], +} +for k, c in coords.iteritems(): + Symbols[k].moveTo(*c[0]) + for x,y in c[1:]: + Symbols[k].lineTo(x, y) + Symbols[k].closeSubpath() + + + class ScatterPlotItem(GraphicsObject): """ Displays a set of x/y points. Instances of this class are created @@ -372,7 +396,13 @@ class ScatterPlotItem(GraphicsObject): self.sigPlotChanged.emit(self) - def generateSpots(self): + def generateSpots(self, clear=True): + if clear: + for spot in self.spots: + self.scene().removeItem(spot) + self.spots = [] + + xmn = ymn = xmx = ymx = None ## apply defaults @@ -585,44 +615,7 @@ class SpotItem(GraphicsObject): self.index = index self.symbol = symbol #s2 = size/2. - self.path = QtGui.QPainterPath() - - if symbol == 'o': - self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1)) - elif symbol == 's': - self.path.addRect(QtCore.QRectF(-0.5, -0.5, 1, 1)) - elif symbol == 't' or symbol == '^': - self.path.moveTo(-0.5, -0.5) - self.path.lineTo(0, 0.5) - self.path.lineTo(0.5, -0.5) - self.path.closeSubpath() - #self.path.connectPath(self.path) - elif symbol == 'd': - self.path.moveTo(0., -0.5) - self.path.lineTo(-0.4, 0.) - self.path.lineTo(0, 0.5) - self.path.lineTo(0.4, 0) - self.path.closeSubpath() - #self.path.connectPath(self.path) - elif symbol == '+': - self.path.moveTo(-0.5, -0.01) - self.path.lineTo(-0.5, 0.01) - self.path.lineTo(-0.01, 0.01) - self.path.lineTo(-0.01, 0.5) - self.path.lineTo(0.01, 0.5) - self.path.lineTo(0.01, 0.01) - self.path.lineTo(0.5, 0.01) - self.path.lineTo(0.5, -0.01) - self.path.lineTo(0.01, -0.01) - self.path.lineTo(0.01, -0.5) - self.path.lineTo(-0.01, -0.5) - self.path.lineTo(-0.01, -0.01) - self.path.closeSubpath() - #self.path.connectPath(self.path) - #elif symbol == 'x': - else: - raise Exception("Unknown spot symbol '%s' (type=%s)" % (str(symbol), str(type(symbol)))) - #self.path.addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1)) + self.path = Symbols[symbol] if pxMode: ## pre-render an image of the spot and display this rather than redrawing every time. diff --git a/graphicsItems/ViewBox/ViewBoxMenu.py b/graphicsItems/ViewBox/ViewBoxMenu.py index 09027029..9d0b1667 100644 --- a/graphicsItems/ViewBox/ViewBoxMenu.py +++ b/graphicsItems/ViewBox/ViewBoxMenu.py @@ -41,6 +41,8 @@ class ViewBoxMenu(QtGui.QMenu): (ui.autoRadio.clicked, 'AutoClicked'), (ui.autoPercentSpin.valueChanged, 'AutoSpinChanged'), (ui.linkCombo.currentIndexChanged, 'LinkComboChanged'), + (ui.autoPanCheck.toggled, 'AutoPanToggled'), + (ui.visibleOnlyCheck.toggled, 'VisibleOnlyToggled') ] for sig, fn in connects: @@ -162,6 +164,11 @@ class ViewBoxMenu(QtGui.QMenu): def xLinkComboChanged(self, ind): self.view.setXLink(str(self.ctrl[0].linkCombo.currentText())) + def xAutoPanToggled(self, b): + pass + + def xVisibleOnlyToggled(self, b): + pass def yMouseToggled(self, b): @@ -189,6 +196,13 @@ class ViewBoxMenu(QtGui.QMenu): def yLinkComboChanged(self, ind): self.view.setYLink(str(self.ctrl[1].linkCombo.currentText())) + def yAutoPanToggled(self, b): + pass + + def yVisibleOnlyToggled(self, b): + pass + + def exportMethod(self): act = self.sender() diff --git a/graphicsItems/ViewBox/axisCtrlTemplate.py b/graphicsItems/ViewBox/axisCtrlTemplate.py index 20e2a8c9..21b6d010 100644 --- a/graphicsItems/ViewBox/axisCtrlTemplate.py +++ b/graphicsItems/ViewBox/axisCtrlTemplate.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'axisCtrlTemplate.ui' # -# Created: Thu Mar 22 13:13:14 2012 -# by: PyQt4 UI code generator 4.8.5 +# Created: Wed Mar 28 23:29:45 2012 +# by: PyQt4 UI code generator 4.8.3 # # WARNING! All changes made in this file will be lost! @@ -17,59 +17,63 @@ except AttributeError: class Ui_Form(object): def setupUi(self, Form): Form.setObjectName(_fromUtf8("Form")) - Form.resize(182, 120) + Form.resize(186, 137) Form.setMaximumSize(QtCore.QSize(200, 16777215)) - Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8)) self.gridLayout = QtGui.QGridLayout(Form) + self.gridLayout.setMargin(0) self.gridLayout.setSpacing(0) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.mouseCheck = QtGui.QCheckBox(Form) - self.mouseCheck.setText(QtGui.QApplication.translate("Form", "Mouse Enabled", None, QtGui.QApplication.UnicodeUTF8)) - self.mouseCheck.setChecked(True) - self.mouseCheck.setObjectName(_fromUtf8("mouseCheck")) - self.gridLayout.addWidget(self.mouseCheck, 0, 1, 1, 2) self.manualRadio = QtGui.QRadioButton(Form) - self.manualRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8)) self.manualRadio.setObjectName(_fromUtf8("manualRadio")) - self.gridLayout.addWidget(self.manualRadio, 1, 0, 1, 1) + self.gridLayout.addWidget(self.manualRadio, 1, 0, 1, 2) self.minText = QtGui.QLineEdit(Form) - self.minText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8)) self.minText.setObjectName(_fromUtf8("minText")) - self.gridLayout.addWidget(self.minText, 1, 1, 1, 1) + self.gridLayout.addWidget(self.minText, 1, 2, 1, 1) self.maxText = QtGui.QLineEdit(Form) - self.maxText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8)) self.maxText.setObjectName(_fromUtf8("maxText")) - self.gridLayout.addWidget(self.maxText, 1, 2, 1, 1) + self.gridLayout.addWidget(self.maxText, 1, 3, 1, 1) self.autoRadio = QtGui.QRadioButton(Form) - self.autoRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8)) self.autoRadio.setChecked(True) self.autoRadio.setObjectName(_fromUtf8("autoRadio")) - self.gridLayout.addWidget(self.autoRadio, 2, 0, 1, 1) + self.gridLayout.addWidget(self.autoRadio, 2, 0, 1, 2) self.autoPercentSpin = QtGui.QSpinBox(Form) self.autoPercentSpin.setEnabled(True) - self.autoPercentSpin.setSuffix(QtGui.QApplication.translate("Form", "%", None, QtGui.QApplication.UnicodeUTF8)) self.autoPercentSpin.setMinimum(1) self.autoPercentSpin.setMaximum(100) self.autoPercentSpin.setSingleStep(1) - self.autoPercentSpin.setProperty("value", 100) + self.autoPercentSpin.setProperty(_fromUtf8("value"), 100) self.autoPercentSpin.setObjectName(_fromUtf8("autoPercentSpin")) - self.gridLayout.addWidget(self.autoPercentSpin, 2, 1, 1, 2) + self.gridLayout.addWidget(self.autoPercentSpin, 2, 2, 1, 2) + self.visibleOnlyCheck = QtGui.QCheckBox(Form) + self.visibleOnlyCheck.setObjectName(_fromUtf8("visibleOnlyCheck")) + self.gridLayout.addWidget(self.visibleOnlyCheck, 3, 1, 1, 3) self.autoPanCheck = QtGui.QCheckBox(Form) - self.autoPanCheck.setText(QtGui.QApplication.translate("Form", "Auto Pan Only", None, QtGui.QApplication.UnicodeUTF8)) self.autoPanCheck.setObjectName(_fromUtf8("autoPanCheck")) - self.gridLayout.addWidget(self.autoPanCheck, 3, 1, 1, 2) + self.gridLayout.addWidget(self.autoPanCheck, 4, 1, 1, 3) + self.label = QtGui.QLabel(Form) + self.label.setObjectName(_fromUtf8("label")) + self.gridLayout.addWidget(self.label, 5, 0, 1, 2) self.linkCombo = QtGui.QComboBox(Form) self.linkCombo.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) self.linkCombo.setObjectName(_fromUtf8("linkCombo")) - self.gridLayout.addWidget(self.linkCombo, 4, 1, 1, 2) - self.label = QtGui.QLabel(Form) - self.label.setText(QtGui.QApplication.translate("Form", "Link Axis:", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 4, 0, 1, 1) + self.gridLayout.addWidget(self.linkCombo, 5, 2, 1, 2) + self.mouseCheck = QtGui.QCheckBox(Form) + self.mouseCheck.setChecked(True) + self.mouseCheck.setObjectName(_fromUtf8("mouseCheck")) + self.gridLayout.addWidget(self.mouseCheck, 0, 0, 1, 4) self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): - pass + Form.setWindowTitle(QtGui.QApplication.translate("Form", "Form", None, QtGui.QApplication.UnicodeUTF8)) + self.manualRadio.setText(QtGui.QApplication.translate("Form", "Manual", None, QtGui.QApplication.UnicodeUTF8)) + self.minText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8)) + self.maxText.setText(QtGui.QApplication.translate("Form", "0", None, QtGui.QApplication.UnicodeUTF8)) + self.autoRadio.setText(QtGui.QApplication.translate("Form", "Auto", None, QtGui.QApplication.UnicodeUTF8)) + self.autoPercentSpin.setSuffix(QtGui.QApplication.translate("Form", "%", None, QtGui.QApplication.UnicodeUTF8)) + self.visibleOnlyCheck.setText(QtGui.QApplication.translate("Form", "Visible Data Only", None, QtGui.QApplication.UnicodeUTF8)) + self.autoPanCheck.setText(QtGui.QApplication.translate("Form", "Auto Pan Only", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("Form", "Link Axis:", None, QtGui.QApplication.UnicodeUTF8)) + self.mouseCheck.setText(QtGui.QApplication.translate("Form", "Mouse Enabled", None, QtGui.QApplication.UnicodeUTF8)) diff --git a/graphicsItems/ViewBox/axisCtrlTemplate.ui b/graphicsItems/ViewBox/axisCtrlTemplate.ui index b463923a..67e2618a 100644 --- a/graphicsItems/ViewBox/axisCtrlTemplate.ui +++ b/graphicsItems/ViewBox/axisCtrlTemplate.ui @@ -6,8 +6,8 @@ 0 0 - 182 - 120 + 186 + 137 @@ -20,41 +20,34 @@ Form + + 0 + 0 - - - - Mouse Enabled - - - true - - - - + Manual - + 0 - + 0 - + Auto @@ -64,7 +57,7 @@ - + true @@ -86,24 +79,41 @@ - + + + + Visible Data Only + + + + Auto Pan Only - + + + + Link Axis: + + + + QComboBox::AdjustToContents - - + + - Link Axis: + Mouse Enabled + + + true diff --git a/widgets/ColorButton.py b/widgets/ColorButton.py index 7e677f73..4a7d4a13 100644 --- a/widgets/ColorButton.py +++ b/widgets/ColorButton.py @@ -5,7 +5,17 @@ import pyqtgraph.functions as functions __all__ = ['ColorButton'] class ColorButton(QtGui.QPushButton): + """ + **Bases:** QtGui.QPushButton + Button displaying a color and allowing the user to select a new color. + + ====================== ============================================================ + **Signals**: + sigColorChanging(self) emitted whenever a new color is picked in the color dialog + sigColorChanged(self) emitted when the selected color is accepted (user clicks OK) + ====================== ============================================================ + """ sigColorChanging = QtCore.Signal(object) ## emitted whenever a new color is picked in the color dialog sigColorChanged = QtCore.Signal(object) ## emitted when the selected color is accepted (user clicks OK) @@ -38,6 +48,7 @@ class ColorButton(QtGui.QPushButton): p.end() def setColor(self, color, finished=True): + """Sets the button's color and emits both sigColorChanged and sigColorChanging.""" self._color = functions.mkColor(color) if finished: self.sigColorChanged.emit(self) diff --git a/widgets/SpinBox.py b/widgets/SpinBox.py index f856cb98..9d1fe5a9 100644 --- a/widgets/SpinBox.py +++ b/widgets/SpinBox.py @@ -158,10 +158,22 @@ class SpinBox(QtGui.QAbstractSpinBox): ## sanity checks: if self.opts['int']: - step = self.opts['step'] - mStep = self.opts['minStep'] - if (int(step) != step) or (self.opts['dec'] and (int(mStep) != mStep)): - raise Exception("Integer SpinBox may only have integer step and minStep.") + if 'step' in opts: + step = opts['step'] + if int(step) != step: + raise Exception('Integer SpinBox must have integer step size.') + else: + self.opts['step'] = int(self.opts['step']) + + if 'minStep' in opts: + step = opts['minStep'] + if int(step) != step: + raise Exception('Integer SpinBox must have integer minStep size.') + else: + ms = int(self.opts.get('minStep', 1)) + if ms < 1: + ms = 1 + self.opts['minStep'] = ms self.updateText()