diff --git a/examples/VideoSpeedTest.py b/examples/VideoSpeedTest.py index dd392189..d7a4e1e0 100644 --- a/examples/VideoSpeedTest.py +++ b/examples/VideoSpeedTest.py @@ -130,8 +130,13 @@ def update(): if ui.rawRadio.isChecked(): ui.rawImg.setImage(data[ptr%data.shape[0]], lut=useLut, levels=useScale) + ui.stack.setCurrentIndex(1) + elif ui.rawGLRadio.isChecked(): + ui.rawGLImg.setImage(data[ptr%data.shape[0]], lut=useLut, levels=useScale) + ui.stack.setCurrentIndex(2) else: img.setImage(data[ptr%data.shape[0]], autoLevels=False, levels=useScale, lut=useLut) + ui.stack.setCurrentIndex(0) #img.setImage(data[ptr%data.shape[0]], autoRange=False) ptr += 1 diff --git a/examples/VideoTemplate.ui b/examples/VideoTemplate.ui index 3dddb928..d73b0dc9 100644 --- a/examples/VideoTemplate.ui +++ b/examples/VideoTemplate.ui @@ -6,8 +6,8 @@ 0 0 - 985 - 674 + 695 + 798 @@ -17,33 +17,62 @@ - - - - - 0 - 0 - - - - - - - - + - RawImageWidget (unscaled; faster) + RawImageWidget true - + - GraphicsView + ImageItem (scaled; slower) + GraphicsView + ImageItem + + + + + + + 2 + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + RawGLImageWidget @@ -250,6 +279,12 @@ QDoubleSpinBox
pyqtgraph
+ + RawImageGLWidget + QWidget +
pyqtgraph.widgets.RawImageWidget
+ 1 +
diff --git a/examples/VideoTemplate_pyqt.py b/examples/VideoTemplate_pyqt.py index c3430e2d..f61a5e46 100644 --- a/examples/VideoTemplate_pyqt.py +++ b/examples/VideoTemplate_pyqt.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file './examples/VideoTemplate.ui' +# Form implementation generated from reading ui file './VideoTemplate.ui' # -# Created: Sun Nov 4 18:24:20 2012 -# by: PyQt4 UI code generator 4.9.1 +# Created: Tue Jul 9 23:38:17 2013 +# by: PyQt4 UI code generator 4.9.3 # # WARNING! All changes made in this file will be lost! @@ -17,31 +17,55 @@ except AttributeError: class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(985, 674) + MainWindow.resize(695, 798) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) self.gridLayout_2 = QtGui.QGridLayout(self.centralwidget) self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2")) self.gridLayout = QtGui.QGridLayout() self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.rawImg = RawImageWidget(self.centralwidget) + self.rawRadio = QtGui.QRadioButton(self.centralwidget) + self.rawRadio.setChecked(True) + self.rawRadio.setObjectName(_fromUtf8("rawRadio")) + self.gridLayout.addWidget(self.rawRadio, 3, 0, 1, 1) + self.gfxRadio = QtGui.QRadioButton(self.centralwidget) + self.gfxRadio.setObjectName(_fromUtf8("gfxRadio")) + self.gridLayout.addWidget(self.gfxRadio, 2, 0, 1, 1) + self.stack = QtGui.QStackedWidget(self.centralwidget) + self.stack.setObjectName(_fromUtf8("stack")) + self.page = QtGui.QWidget() + self.page.setObjectName(_fromUtf8("page")) + self.gridLayout_3 = QtGui.QGridLayout(self.page) + self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) + self.graphicsView = GraphicsView(self.page) + self.graphicsView.setObjectName(_fromUtf8("graphicsView")) + self.gridLayout_3.addWidget(self.graphicsView, 0, 0, 1, 1) + self.stack.addWidget(self.page) + self.page_2 = QtGui.QWidget() + self.page_2.setObjectName(_fromUtf8("page_2")) + self.gridLayout_4 = QtGui.QGridLayout(self.page_2) + self.gridLayout_4.setObjectName(_fromUtf8("gridLayout_4")) + self.rawImg = RawImageWidget(self.page_2) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.rawImg.sizePolicy().hasHeightForWidth()) self.rawImg.setSizePolicy(sizePolicy) self.rawImg.setObjectName(_fromUtf8("rawImg")) - self.gridLayout.addWidget(self.rawImg, 0, 0, 1, 1) - self.graphicsView = GraphicsView(self.centralwidget) - self.graphicsView.setObjectName(_fromUtf8("graphicsView")) - self.gridLayout.addWidget(self.graphicsView, 0, 1, 1, 1) - self.rawRadio = QtGui.QRadioButton(self.centralwidget) - self.rawRadio.setChecked(True) - self.rawRadio.setObjectName(_fromUtf8("rawRadio")) - self.gridLayout.addWidget(self.rawRadio, 1, 0, 1, 1) - self.gfxRadio = QtGui.QRadioButton(self.centralwidget) - self.gfxRadio.setObjectName(_fromUtf8("gfxRadio")) - self.gridLayout.addWidget(self.gfxRadio, 1, 1, 1, 1) + self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1) + self.stack.addWidget(self.page_2) + self.page_3 = QtGui.QWidget() + self.page_3.setObjectName(_fromUtf8("page_3")) + self.gridLayout_5 = QtGui.QGridLayout(self.page_3) + self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) + self.rawGLImg = RawImageGLWidget(self.page_3) + self.rawGLImg.setObjectName(_fromUtf8("rawGLImg")) + self.gridLayout_5.addWidget(self.rawGLImg, 0, 0, 1, 1) + self.stack.addWidget(self.page_3) + self.gridLayout.addWidget(self.stack, 0, 0, 1, 1) + self.rawGLRadio = QtGui.QRadioButton(self.centralwidget) + self.rawGLRadio.setObjectName(_fromUtf8("rawGLRadio")) + self.gridLayout.addWidget(self.rawGLRadio, 4, 0, 1, 1) self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 4) self.label = QtGui.QLabel(self.centralwidget) self.label.setObjectName(_fromUtf8("label")) @@ -130,12 +154,14 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) + self.stack.setCurrentIndex(2) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) - self.rawRadio.setText(QtGui.QApplication.translate("MainWindow", "RawImageWidget (unscaled; faster)", None, QtGui.QApplication.UnicodeUTF8)) - self.gfxRadio.setText(QtGui.QApplication.translate("MainWindow", "GraphicsView + ImageItem (scaled; slower)", None, QtGui.QApplication.UnicodeUTF8)) + self.rawRadio.setText(QtGui.QApplication.translate("MainWindow", "RawImageWidget", None, QtGui.QApplication.UnicodeUTF8)) + self.gfxRadio.setText(QtGui.QApplication.translate("MainWindow", "GraphicsView + ImageItem", None, QtGui.QApplication.UnicodeUTF8)) + self.rawGLRadio.setText(QtGui.QApplication.translate("MainWindow", "RawGLImageWidget", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("MainWindow", "Data type", None, QtGui.QApplication.UnicodeUTF8)) self.dtypeCombo.setItemText(0, QtGui.QApplication.translate("MainWindow", "uint8", None, QtGui.QApplication.UnicodeUTF8)) self.dtypeCombo.setItemText(1, QtGui.QApplication.translate("MainWindow", "uint16", None, QtGui.QApplication.UnicodeUTF8)) @@ -150,4 +176,5 @@ class Ui_MainWindow(object): self.fpsLabel.setText(QtGui.QApplication.translate("MainWindow", "FPS", None, QtGui.QApplication.UnicodeUTF8)) self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8)) -from pyqtgraph import SpinBox, GradientWidget, GraphicsView, RawImageWidget +from pyqtgraph.widgets.RawImageWidget import RawImageGLWidget +from pyqtgraph import GradientWidget, SpinBox, GraphicsView, RawImageWidget diff --git a/examples/VideoTemplate_pyside.py b/examples/VideoTemplate_pyside.py index d19e0f23..d0db5eff 100644 --- a/examples/VideoTemplate_pyside.py +++ b/examples/VideoTemplate_pyside.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file './examples/VideoTemplate.ui' +# Form implementation generated from reading ui file './VideoTemplate.ui' # -# Created: Sun Nov 4 18:24:21 2012 -# by: pyside-uic 0.2.13 running on PySide 1.1.0 +# Created: Tue Jul 9 23:38:19 2013 +# by: pyside-uic 0.2.13 running on PySide 1.1.2 # # WARNING! All changes made in this file will be lost! @@ -12,31 +12,55 @@ from PySide import QtCore, QtGui class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(985, 674) + MainWindow.resize(695, 798) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.gridLayout_2 = QtGui.QGridLayout(self.centralwidget) self.gridLayout_2.setObjectName("gridLayout_2") self.gridLayout = QtGui.QGridLayout() self.gridLayout.setObjectName("gridLayout") - self.rawImg = RawImageWidget(self.centralwidget) + self.rawRadio = QtGui.QRadioButton(self.centralwidget) + self.rawRadio.setChecked(True) + self.rawRadio.setObjectName("rawRadio") + self.gridLayout.addWidget(self.rawRadio, 3, 0, 1, 1) + self.gfxRadio = QtGui.QRadioButton(self.centralwidget) + self.gfxRadio.setObjectName("gfxRadio") + self.gridLayout.addWidget(self.gfxRadio, 2, 0, 1, 1) + self.stack = QtGui.QStackedWidget(self.centralwidget) + self.stack.setObjectName("stack") + self.page = QtGui.QWidget() + self.page.setObjectName("page") + self.gridLayout_3 = QtGui.QGridLayout(self.page) + self.gridLayout_3.setObjectName("gridLayout_3") + self.graphicsView = GraphicsView(self.page) + self.graphicsView.setObjectName("graphicsView") + self.gridLayout_3.addWidget(self.graphicsView, 0, 0, 1, 1) + self.stack.addWidget(self.page) + self.page_2 = QtGui.QWidget() + self.page_2.setObjectName("page_2") + self.gridLayout_4 = QtGui.QGridLayout(self.page_2) + self.gridLayout_4.setObjectName("gridLayout_4") + self.rawImg = RawImageWidget(self.page_2) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.rawImg.sizePolicy().hasHeightForWidth()) self.rawImg.setSizePolicy(sizePolicy) self.rawImg.setObjectName("rawImg") - self.gridLayout.addWidget(self.rawImg, 0, 0, 1, 1) - self.graphicsView = GraphicsView(self.centralwidget) - self.graphicsView.setObjectName("graphicsView") - self.gridLayout.addWidget(self.graphicsView, 0, 1, 1, 1) - self.rawRadio = QtGui.QRadioButton(self.centralwidget) - self.rawRadio.setChecked(True) - self.rawRadio.setObjectName("rawRadio") - self.gridLayout.addWidget(self.rawRadio, 1, 0, 1, 1) - self.gfxRadio = QtGui.QRadioButton(self.centralwidget) - self.gfxRadio.setObjectName("gfxRadio") - self.gridLayout.addWidget(self.gfxRadio, 1, 1, 1, 1) + self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1) + self.stack.addWidget(self.page_2) + self.page_3 = QtGui.QWidget() + self.page_3.setObjectName("page_3") + self.gridLayout_5 = QtGui.QGridLayout(self.page_3) + self.gridLayout_5.setObjectName("gridLayout_5") + self.rawGLImg = RawImageGLWidget(self.page_3) + self.rawGLImg.setObjectName("rawGLImg") + self.gridLayout_5.addWidget(self.rawGLImg, 0, 0, 1, 1) + self.stack.addWidget(self.page_3) + self.gridLayout.addWidget(self.stack, 0, 0, 1, 1) + self.rawGLRadio = QtGui.QRadioButton(self.centralwidget) + self.rawGLRadio.setObjectName("rawGLRadio") + self.gridLayout.addWidget(self.rawGLRadio, 4, 0, 1, 1) self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 4) self.label = QtGui.QLabel(self.centralwidget) self.label.setObjectName("label") @@ -125,12 +149,14 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) + self.stack.setCurrentIndex(2) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) - self.rawRadio.setText(QtGui.QApplication.translate("MainWindow", "RawImageWidget (unscaled; faster)", None, QtGui.QApplication.UnicodeUTF8)) - self.gfxRadio.setText(QtGui.QApplication.translate("MainWindow", "GraphicsView + ImageItem (scaled; slower)", None, QtGui.QApplication.UnicodeUTF8)) + self.rawRadio.setText(QtGui.QApplication.translate("MainWindow", "RawImageWidget", None, QtGui.QApplication.UnicodeUTF8)) + self.gfxRadio.setText(QtGui.QApplication.translate("MainWindow", "GraphicsView + ImageItem", None, QtGui.QApplication.UnicodeUTF8)) + self.rawGLRadio.setText(QtGui.QApplication.translate("MainWindow", "RawGLImageWidget", None, QtGui.QApplication.UnicodeUTF8)) self.label.setText(QtGui.QApplication.translate("MainWindow", "Data type", None, QtGui.QApplication.UnicodeUTF8)) self.dtypeCombo.setItemText(0, QtGui.QApplication.translate("MainWindow", "uint8", None, QtGui.QApplication.UnicodeUTF8)) self.dtypeCombo.setItemText(1, QtGui.QApplication.translate("MainWindow", "uint16", None, QtGui.QApplication.UnicodeUTF8)) @@ -145,4 +171,5 @@ class Ui_MainWindow(object): self.fpsLabel.setText(QtGui.QApplication.translate("MainWindow", "FPS", None, QtGui.QApplication.UnicodeUTF8)) self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8)) -from pyqtgraph import SpinBox, GradientWidget, GraphicsView, RawImageWidget +from pyqtgraph.widgets.RawImageWidget import RawImageGLWidget +from pyqtgraph import GradientWidget, SpinBox, GraphicsView, RawImageWidget diff --git a/examples/parametertree.py b/examples/parametertree.py index 4c5d7275..c600d1be 100644 --- a/examples/parametertree.py +++ b/examples/parametertree.py @@ -139,14 +139,19 @@ p.param('Save/Restore functionality', 'Restore State').sigActivated.connect(rest ## Create two ParameterTree widgets, both accessing the same data t = ParameterTree() t.setParameters(p, showTop=False) -t.show() t.setWindowTitle('pyqtgraph example: Parameter Tree') -t.resize(400,800) t2 = ParameterTree() t2.setParameters(p, showTop=False) -t2.show() -t2.resize(400,800) - + +win = QtGui.QWidget() +layout = QtGui.QGridLayout() +win.setLayout(layout) +layout.addWidget(QtGui.QLabel("These are two views of the same data. They should always display the same values."), 0, 0, 1, 2) +layout.addWidget(t, 1, 0, 1, 1) +layout.addWidget(t2, 1, 1, 1, 1) +win.show() +win.resize(800,800) + ## test save/restore s = p.saveState() p.restoreState(s) diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py index d83e0ec0..b1a05835 100644 --- a/pyqtgraph/__init__.py +++ b/pyqtgraph/__init__.py @@ -255,7 +255,7 @@ def exit(): ## close file handles os.closerange(3, 4096) ## just guessing on the maximum descriptor count.. - os._exit(os.EX_OK) + os._exit(0) diff --git a/pyqtgraph/graphicsItems/ROI.py b/pyqtgraph/graphicsItems/ROI.py index a5e25a2f..033aab42 100644 --- a/pyqtgraph/graphicsItems/ROI.py +++ b/pyqtgraph/graphicsItems/ROI.py @@ -49,7 +49,13 @@ class ROI(GraphicsObject): sigRegionChanged Emitted any time the position of the ROI changes, including while it is being dragged by the user. sigHoverEvent Emitted when the mouse hovers over the ROI. - sigClicked Emitted when the user clicks on the ROI + sigClicked Emitted when the user clicks on the ROI. + Note that clicking is disabled by default to prevent + stealing clicks from objects behind the ROI. To + enable clicking, call + roi.setAcceptedMouseButtons(QtCore.Qt.LeftButton). + See QtGui.QGraphicsItem documentation for more + details. sigRemoveRequested Emitted when the user selects 'remove' from the ROI's context menu (if available). ----------------------- ---------------------------------------------------- diff --git a/pyqtgraph/parametertree/parameterTypes.py b/pyqtgraph/parametertree/parameterTypes.py index 28e1e618..51f0be64 100644 --- a/pyqtgraph/parametertree/parameterTypes.py +++ b/pyqtgraph/parametertree/parameterTypes.py @@ -619,9 +619,15 @@ class TextParameterItem(WidgetParameterItem): self.addChild(self.subItem) def treeWidgetChanged(self): + ## TODO: fix so that superclass method can be called + ## (WidgetParameter should just natively support this style) + #WidgetParameterItem.treeWidgetChanged(self) self.treeWidget().setFirstItemColumnSpanned(self.subItem, True) self.treeWidget().setItemWidget(self.subItem, 0, self.textBox) - self.setExpanded(True) + + # for now, these are copied from ParameterItem.treeWidgetChanged + self.setHidden(not self.param.opts.get('visible', True)) + self.setExpanded(self.param.opts.get('expanded', True)) def makeWidget(self): self.textBox = QtGui.QTextEdit() diff --git a/pyqtgraph/widgets/RawImageWidget.py b/pyqtgraph/widgets/RawImageWidget.py index ea5c98a0..a780f463 100644 --- a/pyqtgraph/widgets/RawImageWidget.py +++ b/pyqtgraph/widgets/RawImageWidget.py @@ -11,8 +11,8 @@ import numpy as np class RawImageWidget(QtGui.QWidget): """ Widget optimized for very fast video display. - Generally using an ImageItem inside GraphicsView is fast enough, - but if you need even more performance, this widget is about as fast as it gets (but only in unscaled mode). + Generally using an ImageItem inside GraphicsView is fast enough. + On some systems this may provide faster video. See the VideoSpeedTest example for benchmarking. """ def __init__(self, parent=None, scaled=False): """ @@ -59,26 +59,82 @@ class RawImageWidget(QtGui.QWidget): p.end() if HAVE_OPENGL: + from OpenGL.GL import * class RawImageGLWidget(QtOpenGL.QGLWidget): """ Similar to RawImageWidget, but uses a GL widget to do all drawing. - Generally this will be about as fast as using GraphicsView + ImageItem, - but performance may vary on some platforms. + Perfomance varies between platforms; see examples/VideoSpeedTest for benchmarking. """ def __init__(self, parent=None, scaled=False): QtOpenGL.QGLWidget.__init__(self, parent=None) self.scaled = scaled self.image = None + self.uploaded = False + self.smooth = False + self.opts = None - def setImage(self, img): - self.image = fn.makeQImage(img) + def setImage(self, img, *args, **kargs): + """ + img must be ndarray of shape (x,y), (x,y,3), or (x,y,4). + Extra arguments are sent to functions.makeARGB + """ + self.opts = (img, args, kargs) + self.image = None + self.uploaded = False self.update() - def paintEvent(self, ev): + def initializeGL(self): + self.texture = glGenTextures(1) + + def uploadTexture(self): + glEnable(GL_TEXTURE_2D) + glBindTexture(GL_TEXTURE_2D, self.texture) + if self.smooth: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) + else: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER) + #glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER) + shape = self.image.shape + + ### Test texture dimensions first + #glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA, shape[0], shape[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, None) + #if glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH) == 0: + #raise Exception("OpenGL failed to create 2D texture (%dx%d); too large for this hardware." % shape[:2]) + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, shape[0], shape[1], 0, GL_RGBA, GL_UNSIGNED_BYTE, self.image.transpose((1,0,2))) + glDisable(GL_TEXTURE_2D) + + def paintGL(self): if self.image is None: - return - p = QtGui.QPainter(self) - p.drawImage(self.rect(), self.image) - p.end() + if self.opts is None: + return + img, args, kwds = self.opts + kwds['useRGBA'] = True + self.image, alpha = fn.makeARGB(img, *args, **kwds) + + if not self.uploaded: + self.uploadTexture() + + glViewport(0, 0, self.width(), self.height()) + glEnable(GL_TEXTURE_2D) + glBindTexture(GL_TEXTURE_2D, self.texture) + glColor4f(1,1,1,1) + + glBegin(GL_QUADS) + glTexCoord2f(0,0) + glVertex3f(-1,-1,0) + glTexCoord2f(1,0) + glVertex3f(1, -1, 0) + glTexCoord2f(1,1) + glVertex3f(1, 1, 0) + glTexCoord2f(0,1) + glVertex3f(-1, 1, 0) + glEnd() + glDisable(GL_TEXTURE_3D) +