diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ad152f7f..ea6046a3 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -31,7 +31,7 @@ jobs:
numpy-version: "~=1.19.0"
- python-version: "3.9"
qt-lib: "pyqt"
- qt-version: "PyQt5~=5.15"
+ qt-version: "PyQt6"
numpy-version: "~=1.19.0"
- python-version: "3.9"
qt-lib: "pyside"
diff --git a/README.md b/README.md
index 7a8e745a..d92402d2 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ PyQtGraph
[![Total alerts](https://img.shields.io/lgtm/alerts/g/pyqtgraph/pyqtgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/pyqtgraph/pyqtgraph/alerts/)
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/pyqtgraph/pyqtgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/pyqtgraph/pyqtgraph/context:python)
-A pure-Python graphics library for PyQt5/PySide2/PySide6
+A pure-Python graphics library for PyQt5/PyQt6/PySide2/PySide6
Copyright 2020 Luke Campagnola, University of North Carolina at Chapel Hill
@@ -36,7 +36,7 @@ Currently this means:
* Python 3.7+
* Qt 5.12-6.0
* Required
- * PyQt5, PySide2 or PySide6
+ * PyQt5, PyQt6, PySide2 or PySide6
* `numpy` 1.17+
* Optional
* `scipy` for image processing
@@ -57,8 +57,9 @@ The following table represents the python environments we test in our CI system.
| PySide2-5.12 | :white_check_mark: | :x: | :x: |
| PyQt5-5.12 | :white_check_mark: | :x: | :x: |
| PySide2-5.15 | :x: | :white_check_mark: | :x: |
-| PyQt5-5.15 | :x: | :white_check_mark: | :white_check_mark: |
+| PyQt5-5.15 | :x: | :white_check_mark: | :x: |
| PySide6-6.0 | :x: | :x: | :white_check_mark: |
+| PyQt6-6.0 | :x: | :x: | :white_check_mark: |
Support
-------
diff --git a/examples/ScatterPlotSpeedTestTemplate_pyqt6.py b/examples/ScatterPlotSpeedTestTemplate_pyqt6.py
new file mode 100644
index 00000000..d963d8fc
--- /dev/null
+++ b/examples/ScatterPlotSpeedTestTemplate_pyqt6.py
@@ -0,0 +1,44 @@
+# Form implementation generated from reading ui file 'examples\ScatterPlotSpeedTestTemplate.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 300)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setObjectName("gridLayout")
+ self.sizeSpin = QtWidgets.QSpinBox(Form)
+ self.sizeSpin.setProperty("value", 10)
+ self.sizeSpin.setObjectName("sizeSpin")
+ self.gridLayout.addWidget(self.sizeSpin, 1, 1, 1, 1)
+ self.pixelModeCheck = QtWidgets.QCheckBox(Form)
+ self.pixelModeCheck.setObjectName("pixelModeCheck")
+ self.gridLayout.addWidget(self.pixelModeCheck, 1, 3, 1, 1)
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
+ self.plot = PlotWidget(Form)
+ self.plot.setObjectName("plot")
+ self.gridLayout.addWidget(self.plot, 0, 0, 1, 4)
+ self.randCheck = QtWidgets.QCheckBox(Form)
+ self.randCheck.setObjectName("randCheck")
+ self.gridLayout.addWidget(self.randCheck, 1, 2, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "PyQtGraph"))
+ self.pixelModeCheck.setText(_translate("Form", "pixel mode"))
+ self.label.setText(_translate("Form", "Size"))
+ self.randCheck.setText(_translate("Form", "Randomize"))
+from pyqtgraph import PlotWidget
diff --git a/examples/VideoTemplate_pyqt6.py b/examples/VideoTemplate_pyqt6.py
new file mode 100644
index 00000000..f69ebe0f
--- /dev/null
+++ b/examples/VideoTemplate_pyqt6.py
@@ -0,0 +1,203 @@
+# Form implementation generated from reading ui file 'VideoTemplate.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(695, 798)
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.gridLayout_2 = QtWidgets.QGridLayout(self.centralwidget)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.cudaCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.cudaCheck.setObjectName("cudaCheck")
+ self.gridLayout_2.addWidget(self.cudaCheck, 9, 0, 1, 2)
+ self.downsampleCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.downsampleCheck.setObjectName("downsampleCheck")
+ self.gridLayout_2.addWidget(self.downsampleCheck, 8, 0, 1, 2)
+ self.scaleCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.scaleCheck.setObjectName("scaleCheck")
+ self.gridLayout_2.addWidget(self.scaleCheck, 4, 0, 1, 1)
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setObjectName("gridLayout")
+ self.rawRadio = QtWidgets.QRadioButton(self.centralwidget)
+ self.rawRadio.setObjectName("rawRadio")
+ self.gridLayout.addWidget(self.rawRadio, 3, 0, 1, 1)
+ self.gfxRadio = QtWidgets.QRadioButton(self.centralwidget)
+ self.gfxRadio.setChecked(True)
+ self.gfxRadio.setObjectName("gfxRadio")
+ self.gridLayout.addWidget(self.gfxRadio, 2, 0, 1, 1)
+ self.stack = QtWidgets.QStackedWidget(self.centralwidget)
+ self.stack.setObjectName("stack")
+ self.page = QtWidgets.QWidget()
+ self.page.setObjectName("page")
+ self.gridLayout_3 = QtWidgets.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 = QtWidgets.QWidget()
+ self.page_2.setObjectName("page_2")
+ self.gridLayout_4 = QtWidgets.QGridLayout(self.page_2)
+ self.gridLayout_4.setObjectName("gridLayout_4")
+ self.rawImg = RawImageWidget(self.page_2)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.rawImg.sizePolicy().hasHeightForWidth())
+ self.rawImg.setSizePolicy(sizePolicy)
+ self.rawImg.setObjectName("rawImg")
+ self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1)
+ self.stack.addWidget(self.page_2)
+ self.gridLayout.addWidget(self.stack, 0, 0, 1, 1)
+ self.rawGLRadio = QtWidgets.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.dtypeCombo = QtWidgets.QComboBox(self.centralwidget)
+ self.dtypeCombo.setObjectName("dtypeCombo")
+ self.dtypeCombo.addItem("")
+ self.dtypeCombo.addItem("")
+ self.dtypeCombo.addItem("")
+ self.gridLayout_2.addWidget(self.dtypeCombo, 3, 2, 1, 1)
+ self.label = QtWidgets.QLabel(self.centralwidget)
+ self.label.setObjectName("label")
+ self.gridLayout_2.addWidget(self.label, 3, 0, 1, 1)
+ self.rgbLevelsCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.rgbLevelsCheck.setObjectName("rgbLevelsCheck")
+ self.gridLayout_2.addWidget(self.rgbLevelsCheck, 4, 1, 1, 1)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.minSpin2 = SpinBox(self.centralwidget)
+ self.minSpin2.setEnabled(False)
+ self.minSpin2.setObjectName("minSpin2")
+ self.horizontalLayout_2.addWidget(self.minSpin2)
+ self.label_3 = QtWidgets.QLabel(self.centralwidget)
+ self.label_3.setAlignment(QtCore.Qt.Alignment.AlignCenter)
+ self.label_3.setObjectName("label_3")
+ self.horizontalLayout_2.addWidget(self.label_3)
+ self.maxSpin2 = SpinBox(self.centralwidget)
+ self.maxSpin2.setEnabled(False)
+ self.maxSpin2.setObjectName("maxSpin2")
+ self.horizontalLayout_2.addWidget(self.maxSpin2)
+ self.gridLayout_2.addLayout(self.horizontalLayout_2, 5, 2, 1, 1)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.minSpin1 = SpinBox(self.centralwidget)
+ self.minSpin1.setObjectName("minSpin1")
+ self.horizontalLayout.addWidget(self.minSpin1)
+ self.label_2 = QtWidgets.QLabel(self.centralwidget)
+ self.label_2.setAlignment(QtCore.Qt.Alignment.AlignCenter)
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout.addWidget(self.label_2)
+ self.maxSpin1 = SpinBox(self.centralwidget)
+ self.maxSpin1.setObjectName("maxSpin1")
+ self.horizontalLayout.addWidget(self.maxSpin1)
+ self.gridLayout_2.addLayout(self.horizontalLayout, 4, 2, 1, 1)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.minSpin3 = SpinBox(self.centralwidget)
+ self.minSpin3.setEnabled(False)
+ self.minSpin3.setObjectName("minSpin3")
+ self.horizontalLayout_3.addWidget(self.minSpin3)
+ self.label_4 = QtWidgets.QLabel(self.centralwidget)
+ self.label_4.setAlignment(QtCore.Qt.Alignment.AlignCenter)
+ self.label_4.setObjectName("label_4")
+ self.horizontalLayout_3.addWidget(self.label_4)
+ self.maxSpin3 = SpinBox(self.centralwidget)
+ self.maxSpin3.setEnabled(False)
+ self.maxSpin3.setObjectName("maxSpin3")
+ self.horizontalLayout_3.addWidget(self.maxSpin3)
+ self.gridLayout_2.addLayout(self.horizontalLayout_3, 6, 2, 1, 1)
+ self.lutCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.lutCheck.setObjectName("lutCheck")
+ self.gridLayout_2.addWidget(self.lutCheck, 7, 0, 1, 1)
+ self.alphaCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.alphaCheck.setObjectName("alphaCheck")
+ self.gridLayout_2.addWidget(self.alphaCheck, 7, 1, 1, 1)
+ self.gradient = GradientWidget(self.centralwidget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Preferred)
+ sizePolicy.setHorizontalStretch(0)
+ sizePolicy.setVerticalStretch(0)
+ sizePolicy.setHeightForWidth(self.gradient.sizePolicy().hasHeightForWidth())
+ self.gradient.setSizePolicy(sizePolicy)
+ self.gradient.setObjectName("gradient")
+ self.gridLayout_2.addWidget(self.gradient, 7, 2, 1, 2)
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Policy.Expanding, QtWidgets.QSizePolicy.Policy.Minimum)
+ self.gridLayout_2.addItem(spacerItem, 3, 3, 1, 1)
+ self.fpsLabel = QtWidgets.QLabel(self.centralwidget)
+ font = QtGui.QFont()
+ font.setPointSize(12)
+ self.fpsLabel.setFont(font)
+ self.fpsLabel.setAlignment(QtCore.Qt.Alignment.AlignCenter)
+ self.fpsLabel.setObjectName("fpsLabel")
+ self.gridLayout_2.addWidget(self.fpsLabel, 0, 0, 1, 4)
+ self.rgbCheck = QtWidgets.QCheckBox(self.centralwidget)
+ self.rgbCheck.setObjectName("rgbCheck")
+ self.gridLayout_2.addWidget(self.rgbCheck, 3, 1, 1, 1)
+ self.label_5 = QtWidgets.QLabel(self.centralwidget)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout_2.addWidget(self.label_5, 2, 0, 1, 1)
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.framesSpin = QtWidgets.QSpinBox(self.centralwidget)
+ self.framesSpin.setButtonSymbols(QtWidgets.QAbstractSpinBox.ButtonSymbols.NoButtons)
+ self.framesSpin.setProperty("value", 10)
+ self.framesSpin.setObjectName("framesSpin")
+ self.horizontalLayout_4.addWidget(self.framesSpin)
+ self.widthSpin = QtWidgets.QSpinBox(self.centralwidget)
+ self.widthSpin.setButtonSymbols(QtWidgets.QAbstractSpinBox.ButtonSymbols.PlusMinus)
+ self.widthSpin.setMaximum(10000)
+ self.widthSpin.setProperty("value", 512)
+ self.widthSpin.setObjectName("widthSpin")
+ self.horizontalLayout_4.addWidget(self.widthSpin)
+ self.heightSpin = QtWidgets.QSpinBox(self.centralwidget)
+ self.heightSpin.setButtonSymbols(QtWidgets.QAbstractSpinBox.ButtonSymbols.NoButtons)
+ self.heightSpin.setMaximum(10000)
+ self.heightSpin.setProperty("value", 512)
+ self.heightSpin.setObjectName("heightSpin")
+ self.horizontalLayout_4.addWidget(self.heightSpin)
+ self.gridLayout_2.addLayout(self.horizontalLayout_4, 2, 1, 1, 2)
+ self.sizeLabel = QtWidgets.QLabel(self.centralwidget)
+ self.sizeLabel.setText("")
+ self.sizeLabel.setObjectName("sizeLabel")
+ self.gridLayout_2.addWidget(self.sizeLabel, 2, 3, 1, 1)
+ MainWindow.setCentralWidget(self.centralwidget)
+
+ self.retranslateUi(MainWindow)
+ self.stack.setCurrentIndex(1)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.cudaCheck.setText(_translate("MainWindow", "Use CUDA (GPU) if available"))
+ self.downsampleCheck.setText(_translate("MainWindow", "Auto downsample"))
+ self.scaleCheck.setText(_translate("MainWindow", "Scale Data"))
+ self.rawRadio.setText(_translate("MainWindow", "RawImageWidget"))
+ self.gfxRadio.setText(_translate("MainWindow", "GraphicsView + ImageItem"))
+ self.rawGLRadio.setText(_translate("MainWindow", "RawGLImageWidget"))
+ self.dtypeCombo.setItemText(0, _translate("MainWindow", "uint8"))
+ self.dtypeCombo.setItemText(1, _translate("MainWindow", "uint16"))
+ self.dtypeCombo.setItemText(2, _translate("MainWindow", "float"))
+ self.label.setText(_translate("MainWindow", "Data type"))
+ self.rgbLevelsCheck.setText(_translate("MainWindow", "RGB"))
+ self.label_3.setText(_translate("MainWindow", "<--->"))
+ self.label_2.setText(_translate("MainWindow", "<--->"))
+ self.label_4.setText(_translate("MainWindow", "<--->"))
+ self.lutCheck.setText(_translate("MainWindow", "Use Lookup Table"))
+ self.alphaCheck.setText(_translate("MainWindow", "alpha"))
+ self.fpsLabel.setText(_translate("MainWindow", "FPS"))
+ self.rgbCheck.setText(_translate("MainWindow", "RGB"))
+ self.label_5.setText(_translate("MainWindow", "Image size"))
+from pyqtgraph import GradientWidget, GraphicsView, SpinBox
+from pyqtgraph.widgets.RawImageWidget import RawImageWidget
diff --git a/examples/VideoTemplate_pyside6.py b/examples/VideoTemplate_pyside6.py
index 5195583d..a9d386c8 100644
--- a/examples/VideoTemplate_pyside6.py
+++ b/examples/VideoTemplate_pyside6.py
@@ -27,6 +27,11 @@ class Ui_MainWindow(object):
self.centralwidget.setObjectName(u"centralwidget")
self.gridLayout_2 = QGridLayout(self.centralwidget)
self.gridLayout_2.setObjectName(u"gridLayout_2")
+ self.cudaCheck = QCheckBox(self.centralwidget)
+ self.cudaCheck.setObjectName(u"cudaCheck")
+
+ self.gridLayout_2.addWidget(self.cudaCheck, 9, 0, 1, 2)
+
self.downsampleCheck = QCheckBox(self.centralwidget)
self.downsampleCheck.setObjectName(u"downsampleCheck")
@@ -258,6 +263,7 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
+ self.cudaCheck.setText(QCoreApplication.translate("MainWindow", u"Use CUDA (GPU) if available", None))
self.downsampleCheck.setText(QCoreApplication.translate("MainWindow", u"Auto downsample", None))
self.scaleCheck.setText(QCoreApplication.translate("MainWindow", u"Scale Data", None))
self.rawRadio.setText(QCoreApplication.translate("MainWindow", u"RawImageWidget", None))
diff --git a/examples/designerExample_pyqt6.py b/examples/designerExample_pyqt6.py
new file mode 100644
index 00000000..4145e6a9
--- /dev/null
+++ b/examples/designerExample_pyqt6.py
@@ -0,0 +1,32 @@
+# Form implementation generated from reading ui file 'examples\designerExample.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(400, 300)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setObjectName("gridLayout")
+ self.plotBtn = QtWidgets.QPushButton(Form)
+ self.plotBtn.setObjectName("plotBtn")
+ self.gridLayout.addWidget(self.plotBtn, 0, 0, 1, 1)
+ self.plot = PlotWidget(Form)
+ self.plot.setObjectName("plot")
+ self.gridLayout.addWidget(self.plot, 1, 0, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "PyQtGraph"))
+ self.plotBtn.setText(_translate("Form", "Plot!"))
+from pyqtgraph import PlotWidget
diff --git a/examples/exampleLoaderTemplate_pyqt6.py b/examples/exampleLoaderTemplate_pyqt6.py
new file mode 100644
index 00000000..dbfa5e08
--- /dev/null
+++ b/examples/exampleLoaderTemplate_pyqt6.py
@@ -0,0 +1,93 @@
+# Form implementation generated from reading ui file 'examples\exampleLoaderTemplate.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(846, 552)
+ self.gridLayout_2 = QtWidgets.QGridLayout(Form)
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.splitter = QtWidgets.QSplitter(Form)
+ self.splitter.setOrientation(QtCore.Qt.Orientations.Horizontal)
+ self.splitter.setObjectName("splitter")
+ self.widget = QtWidgets.QWidget(self.splitter)
+ self.widget.setObjectName("widget")
+ self.gridLayout = QtWidgets.QGridLayout(self.widget)
+ self.gridLayout.setContentsMargins(0, 0, 0, 0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.exampleTree = QtWidgets.QTreeWidget(self.widget)
+ self.exampleTree.setObjectName("exampleTree")
+ self.exampleTree.headerItem().setText(0, "1")
+ self.exampleTree.header().setVisible(False)
+ self.gridLayout.addWidget(self.exampleTree, 0, 0, 1, 2)
+ self.graphicsSystemCombo = QtWidgets.QComboBox(self.widget)
+ self.graphicsSystemCombo.setObjectName("graphicsSystemCombo")
+ self.graphicsSystemCombo.addItem("")
+ self.graphicsSystemCombo.addItem("")
+ self.graphicsSystemCombo.addItem("")
+ self.graphicsSystemCombo.addItem("")
+ self.gridLayout.addWidget(self.graphicsSystemCombo, 2, 1, 1, 1)
+ self.qtLibCombo = QtWidgets.QComboBox(self.widget)
+ self.qtLibCombo.setObjectName("qtLibCombo")
+ self.qtLibCombo.addItem("")
+ self.qtLibCombo.addItem("")
+ self.qtLibCombo.addItem("")
+ self.qtLibCombo.addItem("")
+ self.qtLibCombo.addItem("")
+ self.gridLayout.addWidget(self.qtLibCombo, 1, 1, 1, 1)
+ self.label_2 = QtWidgets.QLabel(self.widget)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
+ self.label = QtWidgets.QLabel(self.widget)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 1, 0, 1, 1)
+ self.loadBtn = QtWidgets.QPushButton(self.widget)
+ self.loadBtn.setObjectName("loadBtn")
+ self.gridLayout.addWidget(self.loadBtn, 3, 1, 1, 1)
+ self.widget1 = QtWidgets.QWidget(self.splitter)
+ self.widget1.setObjectName("widget1")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.widget1)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.loadedFileLabel = QtWidgets.QLabel(self.widget1)
+ font = QtGui.QFont()
+ font.setBold(True)
+ self.loadedFileLabel.setFont(font)
+ self.loadedFileLabel.setText("")
+ self.loadedFileLabel.setAlignment(QtCore.Qt.Alignment.AlignCenter)
+ self.loadedFileLabel.setObjectName("loadedFileLabel")
+ self.verticalLayout.addWidget(self.loadedFileLabel)
+ self.codeView = QtWidgets.QPlainTextEdit(self.widget1)
+ font = QtGui.QFont()
+ font.setFamily("Courier New")
+ self.codeView.setFont(font)
+ self.codeView.setObjectName("codeView")
+ self.verticalLayout.addWidget(self.codeView)
+ self.gridLayout_2.addWidget(self.splitter, 0, 0, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "PyQtGraph"))
+ self.graphicsSystemCombo.setItemText(0, _translate("Form", "default"))
+ self.graphicsSystemCombo.setItemText(1, _translate("Form", "native"))
+ self.graphicsSystemCombo.setItemText(2, _translate("Form", "raster"))
+ self.graphicsSystemCombo.setItemText(3, _translate("Form", "opengl"))
+ self.qtLibCombo.setItemText(0, _translate("Form", "default"))
+ self.qtLibCombo.setItemText(1, _translate("Form", "PyQt4"))
+ self.qtLibCombo.setItemText(2, _translate("Form", "PySide"))
+ self.qtLibCombo.setItemText(3, _translate("Form", "PyQt5"))
+ self.qtLibCombo.setItemText(4, _translate("Form", "PySide2"))
+ self.label_2.setText(_translate("Form", "Graphics System:"))
+ self.label.setText(_translate("Form", "Qt Library:"))
+ self.loadBtn.setText(_translate("Form", "Run Example"))
diff --git a/examples/test_examples.py b/examples/test_examples.py
index b5baf76f..f3ffcebd 100644
--- a/examples/test_examples.py
+++ b/examples/test_examples.py
@@ -35,10 +35,10 @@ def buildFileList(examples, files=None):
path = os.path.abspath(os.path.dirname(__file__))
files = sorted(set(buildFileList(examples)))
frontends = {
- Qt.PYQT4: False,
Qt.PYQT5: False,
- Qt.PYSIDE: False,
- Qt.PYSIDE2: False
+ Qt.PYQT6: False,
+ Qt.PYSIDE2: False,
+ Qt.PYSIDE6: False,
}
# sort out which of the front ends are available
for frontend in frontends.keys():
@@ -66,13 +66,6 @@ conditionalExamples = {
False,
reason="Test is being problematic on CI machines"
),
- "optics_demos.py": exceptionCondition(
- not frontends[Qt.PYSIDE],
- reason=(
- "Test fails due to PySide bug: ",
- "https://bugreports.qt.io/browse/PYSIDE-671"
- )
- ),
'GLVolumeItem.py': exceptionCondition(
not(platform.system() == "Darwin" and
tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and
diff --git a/pyqtgraph/GraphicsScene/GraphicsScene.py b/pyqtgraph/GraphicsScene/GraphicsScene.py
index 6569d57b..71631613 100644
--- a/pyqtgraph/GraphicsScene/GraphicsScene.py
+++ b/pyqtgraph/GraphicsScene/GraphicsScene.py
@@ -3,7 +3,7 @@ import time
import weakref
import warnings
-from ..Qt import QtCore, QtGui, isQObjectAlive
+from ..Qt import QtCore, QtGui, QT_LIB, isQObjectAlive
from ..Point import Point
from .. import functions as fn
from .. import ptime as ptime
@@ -14,15 +14,9 @@ from .. import getConfigOption
getMillis = lambda: int(round(time.time() * 1000))
-if hasattr(QtCore, 'PYQT_VERSION'):
- try:
- try:
- from PyQt5 import sip
- except ImportError:
- import sip
- HAVE_SIP = True
- except ImportError:
- HAVE_SIP = False
+if QT_LIB.startswith('PyQt'):
+ from ..Qt import sip
+ HAVE_SIP = True
else:
HAVE_SIP = False
@@ -158,7 +152,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
self._moveDistance = d
def mousePressEvent(self, ev):
- QtGui.QGraphicsScene.mousePressEvent(self, ev)
+ super().mousePressEvent(ev)
if self.mouseGrabberItem() is None: ## nobody claimed press; we are free to generate drag/click events
if self.lastHoverEvent is not None:
# If the mouse has moved since the last hover event, send a new one.
@@ -171,7 +165,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
## set focus on the topmost focusable item under this click
items = self.items(ev.scenePos())
for i in items:
- if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0:
+ if i.isEnabled() and i.isVisible() and (i.flags() & i.ItemIsFocusable):
i.setFocus(QtCore.Qt.MouseFocusReason)
break
@@ -197,35 +191,35 @@ class GraphicsScene(QtGui.QGraphicsScene):
self.sigMouseMoved.emit(ev.scenePos())
# First allow QGraphicsScene to eliver hoverEvent/Move/Exit Events
- QtGui.QGraphicsScene.mouseMoveEvent(self, ev)
+ super().mouseMoveEvent(ev)
# Next Deliver our own Hover Events
self.sendHoverEvents(ev)
- if int(ev.buttons()) != 0:
+ if ev.buttons():
# button is pressed' send mouseMoveEvents and mouseDragEvents
- QtGui.QGraphicsScene.mouseMoveEvent(self, ev)
+ super().mouseMoveEvent(ev)
if self.mouseGrabberItem() is None:
now = ptime.time()
init = False
## keep track of which buttons are involved in dragging
for btn in [QtCore.Qt.LeftButton, QtCore.Qt.MiddleButton, QtCore.Qt.RightButton]:
- if int(ev.buttons() & btn) == 0:
+ if not (ev.buttons() & btn):
continue
- if int(btn) not in self.dragButtons: ## see if we've dragged far enough yet
- cev = [e for e in self.clickEvents if int(e.button()) == int(btn)]
+ if btn not in self.dragButtons: ## see if we've dragged far enough yet
+ cev = [e for e in self.clickEvents if e.button() == btn]
if cev:
cev = cev[0]
dist = Point(ev.scenePos() - cev.scenePos()).length()
if dist == 0 or (dist < self._moveDistance and now - cev.time() < self.minDragTime):
continue
init = init or (len(self.dragButtons) == 0) ## If this is the first button to be dragged, then init=True
- self.dragButtons.append(int(btn))
+ self.dragButtons.append(btn)
## if we have dragged buttons, deliver a drag event
if len(self.dragButtons) > 0:
if self.sendDragEvent(ev, init=init):
ev.accept()
else:
- QtGui.QGraphicsScene.mouseMoveEvent(self, ev)
+ super().mouseMoveEvent(ev)
# if you do not accept event (which is ignored) then cursor will disappear
ev.accept()
@@ -241,24 +235,24 @@ class GraphicsScene(QtGui.QGraphicsScene):
ev.accept()
self.dragButtons.remove(ev.button())
else:
- cev = [e for e in self.clickEvents if int(e.button()) == int(ev.button())]
+ cev = [e for e in self.clickEvents if e.button() == ev.button()]
if cev:
if self.sendClickEvent(cev[0]):
#print "sent click event"
ev.accept()
self.clickEvents.remove(cev[0])
- if int(ev.buttons()) == 0:
+ if not ev.buttons():
self.dragItem = None
self.dragButtons = []
self.clickEvents = []
self.lastDrag = None
- QtGui.QGraphicsScene.mouseReleaseEvent(self, ev)
+ super().mouseReleaseEvent(ev)
self.sendHoverEvents(ev) ## let items prepare for next click/drag
def mouseDoubleClickEvent(self, ev):
- QtGui.QGraphicsScene.mouseDoubleClickEvent(self, ev)
+ super().mouseDoubleClickEvent(ev)
if self.mouseGrabberItem() is None: ## nobody claimed press; we are free to generate drag/click events
self.clickEvents.append(MouseClickEvent(ev, double=True))
@@ -270,7 +264,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
items = []
event = HoverEvent(None, acceptable)
else:
- acceptable = int(ev.buttons()) == 0 ## if we are in mid-drag, do not allow items to accept the hover event.
+ acceptable = not ev.buttons() ## if we are in mid-drag, do not allow items to accept the hover event.
event = HoverEvent(ev, acceptable)
items = self.itemsNearEvent(event, hoverable=True)
self.sigMouseHover.emit(items)
@@ -312,7 +306,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
# item to continue receiving events until the drag is over
# - event is not a mouse event (QEvent.Leave sometimes appears here)
if (ev.type() == ev.GraphicsSceneMousePress or
- (ev.type() == ev.GraphicsSceneMouseMove and int(ev.buttons()) == 0)):
+ (ev.type() == ev.GraphicsSceneMouseMove and not ev.buttons())):
self.lastHoverEvent = event ## save this so we can ask about accepted events later.
def sendDragEvent(self, ev, init=False, final=False):
@@ -350,7 +344,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
if event.isAccepted():
#print " --> accepted"
self.dragItem = item
- if int(item.flags() & item.ItemIsFocusable) > 0:
+ if item.flags() & item.ItemIsFocusable:
item.setFocus(QtCore.Qt.MouseFocusReason)
break
elif self.dragItem is not None:
@@ -395,7 +389,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
debug.printExc("Error sending click event:")
if ev.isAccepted():
- if int(item.flags() & item.ItemIsFocusable) > 0:
+ if item.flags() & item.ItemIsFocusable:
item.setFocus(QtCore.Qt.MouseFocusReason)
break
self.sigMouseClicked.emit(ev)
diff --git a/pyqtgraph/GraphicsScene/exportDialog.py b/pyqtgraph/GraphicsScene/exportDialog.py
index 56bd90c0..60cb6cd1 100644
--- a/pyqtgraph/GraphicsScene/exportDialog.py
+++ b/pyqtgraph/GraphicsScene/exportDialog.py
@@ -140,4 +140,4 @@ class ExportDialog(QtGui.QWidget):
def closeEvent(self, event):
self.close()
- QtGui.QWidget.closeEvent(self, event)
+ super().closeEvent(event)
diff --git a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py
new file mode 100644
index 00000000..55cbe3a8
--- /dev/null
+++ b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py
@@ -0,0 +1,63 @@
+# Form implementation generated from reading ui file 'pyqtgraph\GraphicsScene\exportDialogTemplate.ui'
+#
+# Created by: PyQt6 UI code generator 6.0.0
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic6 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt6 import QtCore, QtGui, QtWidgets
+
+
+class Ui_Form(object):
+ def setupUi(self, Form):
+ Form.setObjectName("Form")
+ Form.resize(241, 367)
+ self.gridLayout = QtWidgets.QGridLayout(Form)
+ self.gridLayout.setSpacing(0)
+ self.gridLayout.setObjectName("gridLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 3)
+ self.itemTree = QtWidgets.QTreeWidget(Form)
+ self.itemTree.setObjectName("itemTree")
+ self.itemTree.headerItem().setText(0, "1")
+ self.itemTree.header().setVisible(False)
+ self.gridLayout.addWidget(self.itemTree, 1, 0, 1, 3)
+ self.label_2 = QtWidgets.QLabel(Form)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 2, 0, 1, 3)
+ self.formatList = QtWidgets.QListWidget(Form)
+ self.formatList.setObjectName("formatList")
+ self.gridLayout.addWidget(self.formatList, 3, 0, 1, 3)
+ self.exportBtn = QtWidgets.QPushButton(Form)
+ self.exportBtn.setObjectName("exportBtn")
+ self.gridLayout.addWidget(self.exportBtn, 6, 1, 1, 1)
+ self.closeBtn = QtWidgets.QPushButton(Form)
+ self.closeBtn.setObjectName("closeBtn")
+ self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1)
+ self.paramTree = ParameterTree(Form)
+ self.paramTree.setObjectName("paramTree")
+ self.paramTree.headerItem().setText(0, "1")
+ self.paramTree.header().setVisible(False)
+ self.gridLayout.addWidget(self.paramTree, 5, 0, 1, 3)
+ self.label_3 = QtWidgets.QLabel(Form)
+ self.label_3.setObjectName("label_3")
+ self.gridLayout.addWidget(self.label_3, 4, 0, 1, 3)
+ self.copyBtn = QtWidgets.QPushButton(Form)
+ self.copyBtn.setObjectName("copyBtn")
+ self.gridLayout.addWidget(self.copyBtn, 6, 0, 1, 1)
+
+ self.retranslateUi(Form)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Export"))
+ self.label.setText(_translate("Form", "Item to export:"))
+ self.label_2.setText(_translate("Form", "Export format"))
+ self.exportBtn.setText(_translate("Form", "Export"))
+ self.closeBtn.setText(_translate("Form", "Close"))
+ self.label_3.setText(_translate("Form", "Export options"))
+ self.copyBtn.setText(_translate("Form", "Copy"))
+from ..parametertree import ParameterTree
diff --git a/pyqtgraph/GraphicsScene/mouseEvents.py b/pyqtgraph/GraphicsScene/mouseEvents.py
index 36b53919..b8c210af 100644
--- a/pyqtgraph/GraphicsScene/mouseEvents.py
+++ b/pyqtgraph/GraphicsScene/mouseEvents.py
@@ -19,8 +19,8 @@ class MouseDragEvent(object):
self._buttonDownScenePos = {}
self._buttonDownScreenPos = {}
for btn in [QtCore.Qt.LeftButton, QtCore.Qt.MiddleButton, QtCore.Qt.RightButton]:
- self._buttonDownScenePos[int(btn)] = moveEvent.buttonDownScenePos(btn)
- self._buttonDownScreenPos[int(btn)] = moveEvent.buttonDownScreenPos(btn)
+ self._buttonDownScenePos[btn] = moveEvent.buttonDownScenePos(btn)
+ self._buttonDownScreenPos[btn] = moveEvent.buttonDownScreenPos(btn)
self._scenePos = moveEvent.scenePos()
self._screenPos = moveEvent.screenPos()
if lastEvent is None:
@@ -61,7 +61,7 @@ class MouseDragEvent(object):
"""
if btn is None:
btn = self.button()
- return Point(self._buttonDownScenePos[int(btn)])
+ return Point(self._buttonDownScenePos[btn])
def buttonDownScreenPos(self, btn=None):
"""
@@ -70,7 +70,7 @@ class MouseDragEvent(object):
"""
if btn is None:
btn = self.button()
- return Point(self._buttonDownScreenPos[int(btn)])
+ return Point(self._buttonDownScreenPos[btn])
def lastScenePos(self):
"""
@@ -119,7 +119,7 @@ class MouseDragEvent(object):
"""
if btn is None:
btn = self.button()
- return Point(self.currentItem.mapFromScene(self._buttonDownScenePos[int(btn)]))
+ return Point(self.currentItem.mapFromScene(self._buttonDownScenePos[btn]))
def isStart(self):
"""Returns True if this event is the first since a drag was initiated."""
@@ -137,7 +137,7 @@ class MouseDragEvent(object):
else:
lp = self.lastPos()
p = self.pos()
- return " Links this axis with another view. When linked, both views will display the same data range. Percent of data to be visible when auto-scaling. It may be useful to decrease this value for data with spiky noise. Automatically resize this axis whenever the displayed data is changed. Set the range for this axis manually. This disables automatic scaling. Minimum value to display for this axis. Maximum value to display for this axis. Inverts the display of this axis. (+y points downward instead of upward)
Enables mouse interaction (panning, scaling) for this axis.
")) + self.mouseCheck.setText(_translate("Form", "Mouse Enabled")) + self.visibleOnlyCheck.setToolTip(_translate("Form", "When checked, the axis will only auto-scale to data that is visible along the orthogonal axis.
")) + self.visibleOnlyCheck.setText(_translate("Form", "Visible Data Only")) + self.autoPanCheck.setToolTip(_translate("Form", "When checked, the axis will automatically pan to center on the current data, but the scale along this axis will not change.
")) + self.autoPanCheck.setText(_translate("Form", "Auto Pan Only")) diff --git a/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py b/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py index 5d9a2184..689e1963 100644 --- a/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py +++ b/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py @@ -63,8 +63,8 @@ def test_mouseInteraction(): plt.setYRange(-10, 10) # test horizontal drag - pos = plt.plotItem.vb.mapViewToScene(pg.Point(0,5)).toPoint() - pos2 = pos - QtCore.QPoint(200, 200) + pos = plt.plotItem.vb.mapViewToScene(pg.Point(0,5)) + pos2 = pos - QtCore.QPointF(200, 200) mouseMove(plt, pos) assert vline.mouseHovering is True and hline.mouseHovering is False mouseDrag(plt, pos, pos2, QtCore.Qt.LeftButton) @@ -72,17 +72,17 @@ def test_mouseInteraction(): assert abs(vline.value() - plt.plotItem.vb.mapSceneToView(pos2).x()) <= px # test missed drag - pos = plt.plotItem.vb.mapViewToScene(pg.Point(5,0)).toPoint() - pos = pos + QtCore.QPoint(0, 6) - pos2 = pos + QtCore.QPoint(-20, -20) + pos = plt.plotItem.vb.mapViewToScene(pg.Point(5,0)) + pos = pos + QtCore.QPointF(0, 6) + pos2 = pos + QtCore.QPointF(-20, -20) mouseMove(plt, pos) assert vline.mouseHovering is False and hline.mouseHovering is False mouseDrag(plt, pos, pos2, QtCore.Qt.LeftButton) assert hline.value() == 0 # test vertical drag - pos = plt.plotItem.vb.mapViewToScene(pg.Point(5,0)).toPoint() - pos2 = pos - QtCore.QPoint(50, 50) + pos = plt.plotItem.vb.mapViewToScene(pg.Point(5,0)) + pos2 = pos - QtCore.QPointF(50, 50) mouseMove(plt, pos) assert vline.mouseHovering is False and hline.mouseHovering is True mouseDrag(plt, pos, pos2, QtCore.Qt.LeftButton) @@ -90,8 +90,8 @@ def test_mouseInteraction(): assert abs(hline.value() - plt.plotItem.vb.mapSceneToView(pos2).y()) <= px # test non-interactive line - pos = plt.plotItem.vb.mapViewToScene(pg.Point(5,-1)).toPoint() - pos2 = pos - QtCore.QPoint(50, 50) + pos = plt.plotItem.vb.mapViewToScene(pg.Point(5,-1)) + pos2 = pos - QtCore.QPointF(50, 50) mouseMove(plt, pos) assert hline2.mouseHovering == False mouseDrag(plt, pos, pos2, QtCore.Qt.LeftButton) diff --git a/pyqtgraph/imageview/ImageView.py b/pyqtgraph/imageview/ImageView.py index 7b52880e..ca29d1f9 100644 --- a/pyqtgraph/imageview/ImageView.py +++ b/pyqtgraph/imageview/ImageView.py @@ -450,7 +450,7 @@ class ImageView(QtGui.QWidget): self.keysPressed[ev.key()] = 1 self.evalKeyState() else: - QtGui.QWidget.keyPressEvent(self, ev) + super().keyPressEvent(ev) def keyReleaseEvent(self, ev): if ev.key() in [QtCore.Qt.Key_Space, QtCore.Qt.Key_Home, QtCore.Qt.Key_End]: @@ -465,7 +465,7 @@ class ImageView(QtGui.QWidget): self.keysPressed = {} self.evalKeyState() else: - QtGui.QWidget.keyReleaseEvent(self, ev) + super().keyReleaseEvent(ev) def evalKeyState(self): if len(self.keysPressed) == 1: diff --git a/pyqtgraph/imageview/ImageViewTemplate_pyqt6.py b/pyqtgraph/imageview/ImageViewTemplate_pyqt6.py new file mode 100644 index 00000000..c27aa232 --- /dev/null +++ b/pyqtgraph/imageview/ImageViewTemplate_pyqt6.py @@ -0,0 +1,151 @@ +# Form implementation generated from reading ui file 'pyqtgraph\imageview\ImageViewTemplate.ui' +# +# Created by: PyQt6 UI code generator 6.0.0 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt6 import QtCore, QtGui, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(726, 588) + self.gridLayout_3 = QtWidgets.QGridLayout(Form) + self.gridLayout_3.setContentsMargins(0, 0, 0, 0) + self.gridLayout_3.setSpacing(0) + self.gridLayout_3.setObjectName("gridLayout_3") + self.splitter = QtWidgets.QSplitter(Form) + self.splitter.setOrientation(QtCore.Qt.Orientations.Vertical) + self.splitter.setObjectName("splitter") + self.layoutWidget = QtWidgets.QWidget(self.splitter) + self.layoutWidget.setObjectName("layoutWidget") + self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setSpacing(0) + self.gridLayout.setObjectName("gridLayout") + self.graphicsView = GraphicsView(self.layoutWidget) + self.graphicsView.setObjectName("graphicsView") + self.gridLayout.addWidget(self.graphicsView, 0, 0, 2, 1) + self.histogram = HistogramLUTWidget(self.layoutWidget) + self.histogram.setObjectName("histogram") + self.gridLayout.addWidget(self.histogram, 0, 1, 1, 2) + self.roiBtn = QtWidgets.QPushButton(self.layoutWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(1) + sizePolicy.setHeightForWidth(self.roiBtn.sizePolicy().hasHeightForWidth()) + self.roiBtn.setSizePolicy(sizePolicy) + self.roiBtn.setCheckable(True) + self.roiBtn.setObjectName("roiBtn") + self.gridLayout.addWidget(self.roiBtn, 1, 1, 1, 1) + self.menuBtn = QtWidgets.QPushButton(self.layoutWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(1) + sizePolicy.setHeightForWidth(self.menuBtn.sizePolicy().hasHeightForWidth()) + self.menuBtn.setSizePolicy(sizePolicy) + self.menuBtn.setObjectName("menuBtn") + self.gridLayout.addWidget(self.menuBtn, 1, 2, 1, 1) + self.roiPlot = PlotWidget(self.splitter) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Policy.Preferred, QtWidgets.QSizePolicy.Policy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.roiPlot.sizePolicy().hasHeightForWidth()) + self.roiPlot.setSizePolicy(sizePolicy) + self.roiPlot.setMinimumSize(QtCore.QSize(0, 40)) + self.roiPlot.setObjectName("roiPlot") + self.gridLayout_3.addWidget(self.splitter, 0, 0, 1, 1) + self.normGroup = QtWidgets.QGroupBox(Form) + self.normGroup.setObjectName("normGroup") + self.gridLayout_2 = QtWidgets.QGridLayout(self.normGroup) + self.gridLayout_2.setContentsMargins(0, 0, 0, 0) + self.gridLayout_2.setSpacing(0) + self.gridLayout_2.setObjectName("gridLayout_2") + self.normSubtractRadio = QtWidgets.QRadioButton(self.normGroup) + self.normSubtractRadio.setObjectName("normSubtractRadio") + self.gridLayout_2.addWidget(self.normSubtractRadio, 0, 2, 1, 1) + self.normDivideRadio = QtWidgets.QRadioButton(self.normGroup) + self.normDivideRadio.setChecked(False) + self.normDivideRadio.setObjectName("normDivideRadio") + self.gridLayout_2.addWidget(self.normDivideRadio, 0, 1, 1, 1) + self.label_5 = QtWidgets.QLabel(self.normGroup) + font = QtGui.QFont() + font.setBold(True) + self.label_5.setFont(font) + self.label_5.setObjectName("label_5") + self.gridLayout_2.addWidget(self.label_5, 0, 0, 1, 1) + self.label_3 = QtWidgets.QLabel(self.normGroup) + font = QtGui.QFont() + font.setBold(True) + self.label_3.setFont(font) + self.label_3.setObjectName("label_3") + self.gridLayout_2.addWidget(self.label_3, 1, 0, 1, 1) + self.label_4 = QtWidgets.QLabel(self.normGroup) + font = QtGui.QFont() + font.setBold(True) + self.label_4.setFont(font) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 2, 0, 1, 1) + self.normROICheck = QtWidgets.QCheckBox(self.normGroup) + self.normROICheck.setObjectName("normROICheck") + self.gridLayout_2.addWidget(self.normROICheck, 1, 1, 1, 1) + self.normXBlurSpin = QtWidgets.QDoubleSpinBox(self.normGroup) + self.normXBlurSpin.setObjectName("normXBlurSpin") + self.gridLayout_2.addWidget(self.normXBlurSpin, 2, 2, 1, 1) + self.label_8 = QtWidgets.QLabel(self.normGroup) + self.label_8.setAlignment(QtCore.Qt.Alignment.AlignRight|QtCore.Qt.Alignment.AlignTrailing|QtCore.Qt.Alignment.AlignVCenter) + self.label_8.setObjectName("label_8") + self.gridLayout_2.addWidget(self.label_8, 2, 1, 1, 1) + self.label_9 = QtWidgets.QLabel(self.normGroup) + self.label_9.setAlignment(QtCore.Qt.Alignment.AlignRight|QtCore.Qt.Alignment.AlignTrailing|QtCore.Qt.Alignment.AlignVCenter) + self.label_9.setObjectName("label_9") + self.gridLayout_2.addWidget(self.label_9, 2, 3, 1, 1) + self.normYBlurSpin = QtWidgets.QDoubleSpinBox(self.normGroup) + self.normYBlurSpin.setObjectName("normYBlurSpin") + self.gridLayout_2.addWidget(self.normYBlurSpin, 2, 4, 1, 1) + self.label_10 = QtWidgets.QLabel(self.normGroup) + self.label_10.setAlignment(QtCore.Qt.Alignment.AlignRight|QtCore.Qt.Alignment.AlignTrailing|QtCore.Qt.Alignment.AlignVCenter) + self.label_10.setObjectName("label_10") + self.gridLayout_2.addWidget(self.label_10, 2, 5, 1, 1) + self.normOffRadio = QtWidgets.QRadioButton(self.normGroup) + self.normOffRadio.setChecked(True) + self.normOffRadio.setObjectName("normOffRadio") + self.gridLayout_2.addWidget(self.normOffRadio, 0, 3, 1, 1) + self.normTimeRangeCheck = QtWidgets.QCheckBox(self.normGroup) + self.normTimeRangeCheck.setObjectName("normTimeRangeCheck") + self.gridLayout_2.addWidget(self.normTimeRangeCheck, 1, 3, 1, 1) + self.normFrameCheck = QtWidgets.QCheckBox(self.normGroup) + self.normFrameCheck.setObjectName("normFrameCheck") + self.gridLayout_2.addWidget(self.normFrameCheck, 1, 2, 1, 1) + self.normTBlurSpin = QtWidgets.QDoubleSpinBox(self.normGroup) + self.normTBlurSpin.setObjectName("normTBlurSpin") + self.gridLayout_2.addWidget(self.normTBlurSpin, 2, 6, 1, 1) + self.gridLayout_3.addWidget(self.normGroup, 1, 0, 1, 1) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "PyQtGraph")) + self.roiBtn.setText(_translate("Form", "ROI")) + self.menuBtn.setText(_translate("Form", "Menu")) + self.normGroup.setTitle(_translate("Form", "Normalization")) + self.normSubtractRadio.setText(_translate("Form", "Subtract")) + self.normDivideRadio.setText(_translate("Form", "Divide")) + self.label_5.setText(_translate("Form", "Operation:")) + self.label_3.setText(_translate("Form", "Mean:")) + self.label_4.setText(_translate("Form", "Blur:")) + self.normROICheck.setText(_translate("Form", "ROI")) + self.label_8.setText(_translate("Form", "X")) + self.label_9.setText(_translate("Form", "Y")) + self.label_10.setText(_translate("Form", "T")) + self.normOffRadio.setText(_translate("Form", "Off")) + self.normTimeRangeCheck.setText(_translate("Form", "Time range")) + self.normFrameCheck.setText(_translate("Form", "Frame")) +from ..widgets.GraphicsView import GraphicsView +from ..widgets.HistogramLUTWidget import HistogramLUTWidget +from ..widgets.PlotWidget import PlotWidget diff --git a/pyqtgraph/opengl/GLViewWidget.py b/pyqtgraph/opengl/GLViewWidget.py index 2c9bff01..1426a424 100644 --- a/pyqtgraph/opengl/GLViewWidget.py +++ b/pyqtgraph/opengl/GLViewWidget.py @@ -401,11 +401,12 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): return xDist / self.width() def mousePressEvent(self, ev): - self.mousePos = ev.pos() + self.mousePos = ev.localPos() def mouseMoveEvent(self, ev): - diff = ev.pos() - self.mousePos - self.mousePos = ev.pos() + lpos = ev.localPos() + diff = lpos - self.mousePos + self.mousePos = lpos if ev.buttons() == QtCore.Qt.LeftButton: if (ev.modifiers() & QtCore.Qt.ControlModifier): diff --git a/pyqtgraph/parametertree/ParameterTree.py b/pyqtgraph/parametertree/ParameterTree.py index de6ab126..8c89d37e 100644 --- a/pyqtgraph/parametertree/ParameterTree.py +++ b/pyqtgraph/parametertree/ParameterTree.py @@ -157,8 +157,8 @@ class ParameterTree(TreeWidget): self.lastSel = sel[0] if hasattr(sel[0], 'selected'): sel[0].selected(True) - return TreeWidget.selectionChanged(self, *args) + return super().selectionChanged(*args) def wheelEvent(self, ev): self.clearSelection() - return TreeWidget.wheelEvent(self, ev) + return super().wheelEvent(ev) diff --git a/pyqtgraph/tests/image_testing.py b/pyqtgraph/tests/image_testing.py index 7cb993f9..1245bb02 100644 --- a/pyqtgraph/tests/image_testing.py +++ b/pyqtgraph/tests/image_testing.py @@ -257,7 +257,7 @@ def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50., assert im1.dtype == im2.dtype if pxCount == -1: - if QT_LIB in {'PyQt5', 'PySide2', 'PySide6'}: + if QT_LIB in {'PyQt5', 'PySide2', 'PySide6', 'PyQt6'}: # Qt5 generates slightly different results; relax the tolerance # until test images are updated. pxCount = int(im1.shape[0] * im1.shape[1] * 0.01) diff --git a/pyqtgraph/tests/ui_testing.py b/pyqtgraph/tests/ui_testing.py index 4bcb7606..747aadf9 100644 --- a/pyqtgraph/tests/ui_testing.py +++ b/pyqtgraph/tests/ui_testing.py @@ -1,5 +1,5 @@ import time -from ..Qt import QtCore, QtGui, QtTest, QT_LIB +from ..Qt import QtCore, QtGui, QtTest def resizeWindow(win, w, h, timeout=2.0): @@ -32,8 +32,6 @@ def mousePress(widget, pos, button, modifier=None): widget = widget.viewport() if modifier is None: modifier = QtCore.Qt.NoModifier - if QT_LIB != 'PyQt5' and isinstance(pos, QtCore.QPointF): - pos = pos.toPoint() event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, pos, button, QtCore.Qt.NoButton, modifier) QtGui.QApplication.sendEvent(widget, event) @@ -43,8 +41,6 @@ def mouseRelease(widget, pos, button, modifier=None): widget = widget.viewport() if modifier is None: modifier = QtCore.Qt.NoModifier - if QT_LIB != 'PyQt5' and isinstance(pos, QtCore.QPointF): - pos = pos.toPoint() event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonRelease, pos, button, QtCore.Qt.NoButton, modifier) QtGui.QApplication.sendEvent(widget, event) @@ -56,8 +52,6 @@ def mouseMove(widget, pos, buttons=None, modifier=None): modifier = QtCore.Qt.NoModifier if buttons is None: buttons = QtCore.Qt.NoButton - if QT_LIB != 'PyQt5' and isinstance(pos, QtCore.QPointF): - pos = pos.toPoint() event = QtGui.QMouseEvent(QtCore.QEvent.MouseMove, pos, QtCore.Qt.NoButton, buttons, modifier) QtGui.QApplication.sendEvent(widget, event) diff --git a/pyqtgraph/widgets/ColorButton.py b/pyqtgraph/widgets/ColorButton.py index 43dd16f6..11a5ac14 100644 --- a/pyqtgraph/widgets/ColorButton.py +++ b/pyqtgraph/widgets/ColorButton.py @@ -35,7 +35,7 @@ class ColorButton(QtGui.QPushButton): self.setMinimumWidth(15) def paintEvent(self, ev): - QtGui.QPushButton.paintEvent(self, ev) + super().paintEvent(ev) p = QtGui.QPainter(self) rect = self.rect().adjusted(6, 6, -6, -6) ## draw white base, then texture for indicating transparency, then actual color diff --git a/pyqtgraph/widgets/GradientWidget.py b/pyqtgraph/widgets/GradientWidget.py index 77881b30..c5396d75 100644 --- a/pyqtgraph/widgets/GradientWidget.py +++ b/pyqtgraph/widgets/GradientWidget.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from ..Qt import QtGui, QtCore +from ..Qt import QtGui, QtCore, QtWidgets, QT_LIB from .GraphicsView import GraphicsView from ..graphicsItems.GradientEditorItem import GradientEditorItem import weakref @@ -40,7 +40,18 @@ class GradientWidget(GraphicsView): self.setOrientation(orientation) self.setCacheMode(self.CacheNone) self.setRenderHints(QtGui.QPainter.Antialiasing | QtGui.QPainter.TextAntialiasing) - self.setFrameStyle(QtGui.QFrame.NoFrame | QtGui.QFrame.Plain) + + if QT_LIB == 'PyQt6': + # PyQt6 doesn't allow or-ing of different enum types + # so we need to take its value property + NoFrame = QtWidgets.QFrame.Shape.NoFrame.value + Plain = QtWidgets.QFrame.Shadow.Plain.value + else: + NoFrame = QtWidgets.QFrame.NoFrame + Plain = QtWidgets.QFrame.Plain + frame_style = NoFrame | Plain + + self.setFrameStyle(frame_style) #self.setBackgroundRole(QtGui.QPalette.NoRole) #self.setBackgroundBrush(QtGui.QBrush(QtCore.Qt.NoBrush)) #self.setAutoFillBackground(False) diff --git a/pyqtgraph/widgets/GraphicsView.py b/pyqtgraph/widgets/GraphicsView.py index accaf2a9..4490d4de 100644 --- a/pyqtgraph/widgets/GraphicsView.py +++ b/pyqtgraph/widgets/GraphicsView.py @@ -25,6 +25,7 @@ from .. import getConfigOption __all__ = ['GraphicsView'] + class GraphicsView(QtGui.QGraphicsView): """Re-implementation of QGraphicsView that removes scrollbars and allows unambiguous control of the viewed coordinate range. Also automatically creates a GraphicsScene and a central QGraphicsWidget @@ -157,11 +158,11 @@ class GraphicsView(QtGui.QGraphicsView): def paintEvent(self, ev): self.scene().prepareForPaint() - return QtGui.QGraphicsView.paintEvent(self, ev) + return super().paintEvent(ev) def render(self, *args, **kwds): self.scene().prepareForPaint() - return QtGui.QGraphicsView.render(self, *args, **kwds) + return super().render(*args, **kwds) def close(self): @@ -328,10 +329,9 @@ class GraphicsView(QtGui.QGraphicsView): GraphicsView.setRange(self, r1, padding=[0, padding], propagate=False) def wheelEvent(self, ev): - QtGui.QGraphicsView.wheelEvent(self, ev) + super().wheelEvent(ev) if not self.mouseEnabled: return - delta = 0 if QT_LIB in ['PyQt4', 'PySide']: delta = ev.delta() @@ -352,20 +352,21 @@ class GraphicsView(QtGui.QGraphicsView): self.scene().leaveEvent(ev) ## inform scene when mouse leaves def mousePressEvent(self, ev): - QtGui.QGraphicsView.mousePressEvent(self, ev) + super().mousePressEvent(ev) if not self.mouseEnabled: return - self.lastMousePos = Point(ev.pos()) - self.mousePressPos = ev.pos() + lpos = ev.localPos() + self.lastMousePos = lpos + self.mousePressPos = lpos self.clickAccepted = ev.isAccepted() if not self.clickAccepted: self.scene().clearSelection() return ## Everything below disabled for now.. def mouseReleaseEvent(self, ev): - QtGui.QGraphicsView.mouseReleaseEvent(self, ev) + super().mouseReleaseEvent(ev) if not self.mouseEnabled: return self.sigMouseReleased.emit(ev) @@ -373,15 +374,16 @@ class GraphicsView(QtGui.QGraphicsView): return ## Everything below disabled for now.. def mouseMoveEvent(self, ev): + lpos = ev.localPos() if self.lastMousePos is None: - self.lastMousePos = Point(ev.pos()) - delta = Point(ev.pos() - self.lastMousePos.toQPoint()) - self.lastMousePos = Point(ev.pos()) + self.lastMousePos = lpos + delta = Point(lpos - self.lastMousePos) + self.lastMousePos = lpos - QtGui.QGraphicsView.mouseMoveEvent(self, ev) + super().mouseMoveEvent(ev) if not self.mouseEnabled: return - self.sigSceneMouseMoved.emit(self.mapToScene(ev.pos())) + self.sigSceneMouseMoved.emit(self.mapToScene(lpos)) if self.clickAccepted: ## Ignore event if an item in the scene has already claimed it. return diff --git a/pyqtgraph/widgets/JoystickButton.py b/pyqtgraph/widgets/JoystickButton.py index 6f73c8dc..b668d82d 100644 --- a/pyqtgraph/widgets/JoystickButton.py +++ b/pyqtgraph/widgets/JoystickButton.py @@ -18,11 +18,11 @@ class JoystickButton(QtGui.QPushButton): def mousePressEvent(self, ev): self.setChecked(True) - self.pressPos = ev.pos() + self.pressPos = ev.localPos() ev.accept() def mouseMoveEvent(self, ev): - dif = ev.pos()-self.pressPos + dif = ev.localPos()-self.pressPos self.setState(dif.x(), -dif.y()) def mouseReleaseEvent(self, ev): @@ -64,14 +64,14 @@ class JoystickButton(QtGui.QPushButton): self.sigStateChanged.emit(self, self.state) def paintEvent(self, ev): - QtGui.QPushButton.paintEvent(self, ev) + super().paintEvent(ev) p = QtGui.QPainter(self) p.setBrush(QtGui.QBrush(QtGui.QColor(0,0,0))) p.drawEllipse(self.spotPos.x()-3,self.spotPos.y()-3,6,6) def resizeEvent(self, ev): self.setState(*self.state) - QtGui.QPushButton.resizeEvent(self, ev) + super().resizeEvent(ev) diff --git a/pyqtgraph/widgets/PathButton.py b/pyqtgraph/widgets/PathButton.py index ee2e0bca..06fe131f 100644 --- a/pyqtgraph/widgets/PathButton.py +++ b/pyqtgraph/widgets/PathButton.py @@ -32,7 +32,7 @@ class PathButton(QtGui.QPushButton): self.update() def paintEvent(self, ev): - QtGui.QPushButton.paintEvent(self, ev) + super().paintEvent(ev) margin = self.margin geom = QtCore.QRectF(0, 0, self.width(), self.height()).adjusted(margin, margin, -margin, -margin) rect = self.path.boundingRect() diff --git a/pyqtgraph/widgets/ProgressDialog.py b/pyqtgraph/widgets/ProgressDialog.py index 7d2ef8a4..989a8d61 100644 --- a/pyqtgraph/widgets/ProgressDialog.py +++ b/pyqtgraph/widgets/ProgressDialog.py @@ -195,7 +195,7 @@ class ProgressDialog(QtGui.QProgressDialog): if self._nestingReady: # don't let progress dialog manage widgets anymore. return - return QtGui.QProgressDialog.resizeEvent(self, ev) + return super().resizeEvent(ev) ## wrap all other functions to make sure they aren't being called from non-gui threads diff --git a/pyqtgraph/widgets/RemoteGraphicsView.py b/pyqtgraph/widgets/RemoteGraphicsView.py index 877425d1..620f4085 100644 --- a/pyqtgraph/widgets/RemoteGraphicsView.py +++ b/pyqtgraph/widgets/RemoteGraphicsView.py @@ -1,9 +1,6 @@ from ..Qt import QtGui, QtCore, QT_LIB -if QT_LIB in ['PyQt4', 'PyQt5']: - try: - from PyQt5 import sip - except ImportError: - import sip +if QT_LIB.startswith('PyQt'): + from ..Qt import sip from .. import multiprocess as mp from .GraphicsView import GraphicsView from .. import CONFIG_OPTIONS @@ -12,55 +9,6 @@ import mmap, tempfile, ctypes, atexit, sys, random __all__ = ['RemoteGraphicsView'] -class SerializableWheelEvent: - """ - Contains all information of a QWheelEvent, is serializable and can generate QWheelEvents. - - Methods have the functionality of their QWheelEvent equivalent. - """ - def __init__(self, _pos, _globalPos, _delta, _buttons, _modifiers, _orientation): - self._pos = _pos - self._globalPos = _globalPos - self._delta = _delta - self._buttons = _buttons - self._modifiers = _modifiers - self._orientation_vertical = _orientation == QtCore.Qt.Vertical - - def pos(self): - return self._pos - - def globalPos(self): - return self._globalPos - - def delta(self): - return self._delta - - def orientation(self): - if self._orientation_vertical: - return QtCore.Qt.Vertical - else: - return QtCore.Qt.Horizontal - - def angleDelta(self): - if self._orientation_vertical: - return QtCore.QPoint(0, self._delta) - else: - return QtCore.QPoint(self._delta, 0) - - def buttons(self): - return QtCore.Qt.MouseButtons(self._buttons) - - def modifiers(self): - return QtCore.Qt.KeyboardModifiers(self._modifiers) - - def toQWheelEvent(self): - """ - Generate QWheelEvent from SerializableWheelEvent. - """ - if QT_LIB in ['PyQt4', 'PySide']: - return QtGui.QWheelEvent(self.pos(), self.globalPos(), self.delta(), self.buttons(), self.modifiers(), self.orientation()) - else: - return QtGui.QWheelEvent(self.pos(), self.globalPos(), QtCore.QPoint(), self.angleDelta(), self.delta(), self.orientation(), self.buttons(), self.modifiers()) class RemoteGraphicsView(QtGui.QWidget): """ @@ -114,7 +62,7 @@ class RemoteGraphicsView(QtGui.QWidget): setattr(self, method, getattr(self._view, method)) def resizeEvent(self, ev): - ret = QtGui.QWidget.resizeEvent(self, ev) + ret = super().resizeEvent(ev) self._view.resize(self.size(), _callSync='off') return ret @@ -148,52 +96,60 @@ class RemoteGraphicsView(QtGui.QWidget): p = QtGui.QPainter(self) p.drawImage(self.rect(), self._img, QtCore.QRect(0, 0, self._img.width(), self._img.height())) p.end() - + + def serialize_mouse_common(self, ev): + if QT_LIB == 'PyQt6': + # PyQt6 can pickle MouseButtons and KeyboardModifiers but cannot cast to int + btns = ev.buttons() + mods = ev.modifiers() + else: + # PyQt5, PySide2, PySide6 cannot pickle MouseButtons and KeyboardModifiers + btns = int(ev.buttons()) + mods = int(ev.modifiers()) + return (btns, mods) + + def serialize_mouse_event(self, ev): + # lpos, gpos = ev.localPos(), ev.screenPos() + # RemoteGraphicsView Renderer assumes to be at (0, 0) + gpos = lpos = ev.localPos() + btns, mods = self.serialize_mouse_common(ev) + return (ev.type(), lpos, gpos, ev.button(), btns, mods) + + def serialize_wheel_event(self, ev): + # lpos, gpos = ev.position(), globalPosition() + # RemoteGraphicsView Renderer assumes to be at (0, 0) + gpos = lpos = ev.position() + btns, mods = self.serialize_mouse_common(ev) + return (lpos, gpos, ev.pixelDelta(), ev.angleDelta(), btns, mods, ev.phase(), ev.inverted()) + def mousePressEvent(self, ev): - self._view.mousePressEvent(int(ev.type()), ev.pos(), ev.pos(), int(ev.button()), int(ev.buttons()), int(ev.modifiers()), _callSync='off') + self._view.mousePressEvent(self.serialize_mouse_event(ev), _callSync='off') ev.accept() - return QtGui.QWidget.mousePressEvent(self, ev) + return super().mousePressEvent(ev) def mouseReleaseEvent(self, ev): - self._view.mouseReleaseEvent(int(ev.type()), ev.pos(), ev.pos(), int(ev.button()), int(ev.buttons()), int(ev.modifiers()), _callSync='off') + self._view.mouseReleaseEvent(self.serialize_mouse_event(ev), _callSync='off') ev.accept() - return QtGui.QWidget.mouseReleaseEvent(self, ev) + return super().mouseReleaseEvent(ev) def mouseMoveEvent(self, ev): - self._view.mouseMoveEvent(int(ev.type()), ev.pos(), ev.pos(), int(ev.button()), int(ev.buttons()), int(ev.modifiers()), _callSync='off') + self._view.mouseMoveEvent(self.serialize_mouse_event(ev), _callSync='off') ev.accept() - return QtGui.QWidget.mouseMoveEvent(self, ev) + return super().mouseMoveEvent(ev) def wheelEvent(self, ev): - delta = 0 - orientation = QtCore.Qt.Horizontal - if QT_LIB in ['PyQt4', 'PySide']: - delta = ev.delta() - orientation = ev.orientation() - else: - delta = ev.angleDelta().x() - if delta == 0: - orientation = QtCore.Qt.Vertical - delta = ev.angleDelta().y() - - serializableEvent = SerializableWheelEvent(ev.pos(), ev.pos(), delta, int(ev.buttons()), int(ev.modifiers()), orientation) - self._view.wheelEvent(serializableEvent, _callSync='off') + self._view.wheelEvent(self.serialize_wheel_event(ev), _callSync='off') ev.accept() - return QtGui.QWidget.wheelEvent(self, ev) - - def keyEvent(self, ev): - if self._view.keyEvent(int(ev.type()), int(ev.modifiers()), text, autorep, count): - ev.accept() - return QtGui.QWidget.keyEvent(self, ev) - + return super().wheelEvent(ev) + def enterEvent(self, ev): lws = ev.localPos(), ev.windowPos(), ev.screenPos() self._view.enterEvent(lws, _callSync='off') - return QtGui.QWidget.enterEvent(self, ev) + return super().enterEvent(ev) def leaveEvent(self, ev): - self._view.leaveEvent(int(ev.type()), _callSync='off') - return QtGui.QWidget.leaveEvent(self, ev) + self._view.leaveEvent(ev.type(), _callSync='off') + return super().leaveEvent(ev) def remoteProcess(self): """Return the remote process handle. (see multiprocess.remoteproxy.RemoteEventHandler)""" @@ -243,11 +199,11 @@ class Renderer(GraphicsView): def update(self): self.img = None - return GraphicsView.update(self) + return super().update() def resize(self, size): oldSize = self.size() - GraphicsView.resize(self, size) + super().resize(size) self.resizeEvent(QtGui.QResizeEvent(size, oldSize)) self.update() @@ -275,62 +231,60 @@ class Renderer(GraphicsView): self.shm.resize(size) ## render the scene directly to shared memory + ctypes_obj = ctypes.c_char.from_buffer(self.shm, 0) if QT_LIB.startswith('PySide'): - ch = ctypes.c_char.from_buffer(self.shm, 0) - self.img = QtGui.QImage(ch, self.width(), self.height(), QtGui.QImage.Format_ARGB32) + # PySide2, PySide6 + img_ptr = ctypes_obj else: - address = ctypes.addressof(ctypes.c_char.from_buffer(self.shm, 0)) + # PyQt5, PyQt6 + img_ptr = sip.voidptr(ctypes.addressof(ctypes_obj)) + + if QT_LIB == 'PyQt6': + img_ptr.setsize(size) + + self.img = QtGui.QImage(img_ptr, self.width(), self.height(), QtGui.QImage.Format_ARGB32) - # different versions of pyqt have different requirements here.. - try: - self.img = QtGui.QImage(sip.voidptr(address), self.width(), self.height(), QtGui.QImage.Format_ARGB32) - except TypeError: - try: - self.img = QtGui.QImage(memoryview(buffer(self.shm)), self.width(), self.height(), QtGui.QImage.Format_ARGB32) - except TypeError: - # Works on PyQt 4.9.6 - self.img = QtGui.QImage(address, self.width(), self.height(), QtGui.QImage.Format_ARGB32) self.img.fill(0xffffffff) p = QtGui.QPainter(self.img) self.render(p, self.viewRect(), self.rect()) p.end() self.sceneRendered.emit((self.width(), self.height(), self.shm.size(), self.shmFileName())) - def mousePressEvent(self, typ, pos, gpos, btn, btns, mods): - typ = QtCore.QEvent.Type(typ) - btn = QtCore.Qt.MouseButton(btn) + def deserialize_mouse_event(self, mouse_event): + typ, pos, gpos, btn, btns, mods = mouse_event + typ = QtCore.QEvent.Type(typ) # this line needed by PyQt5 only btns = QtCore.Qt.MouseButtons(btns) mods = QtCore.Qt.KeyboardModifiers(mods) - return GraphicsView.mousePressEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods)) + return QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods) - def mouseMoveEvent(self, typ, pos, gpos, btn, btns, mods): - typ = QtCore.QEvent.Type(typ) - btn = QtCore.Qt.MouseButton(btn) + def deserialize_wheel_event(self, wheel_event): + pos, gpos, pixelDelta, angleDelta, btns, mods, scrollPhase, inverted = wheel_event btns = QtCore.Qt.MouseButtons(btns) mods = QtCore.Qt.KeyboardModifiers(mods) - return GraphicsView.mouseMoveEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods)) + return QtGui.QWheelEvent(pos, gpos, pixelDelta, angleDelta, btns, mods, scrollPhase, inverted) - def mouseReleaseEvent(self, typ, pos, gpos, btn, btns, mods): - typ = QtCore.QEvent.Type(typ) - btn = QtCore.Qt.MouseButton(btn) - btns = QtCore.Qt.MouseButtons(btns) - mods = QtCore.Qt.KeyboardModifiers(mods) - return GraphicsView.mouseReleaseEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods)) + def mousePressEvent(self, mouse_event): + ev = self.deserialize_mouse_event(mouse_event) + return super().mousePressEvent(ev) + + def mouseMoveEvent(self, mouse_event): + ev = self.deserialize_mouse_event(mouse_event) + return super().mouseMoveEvent(ev) + + def mouseReleaseEvent(self, mouse_event): + ev = self.deserialize_mouse_event(mouse_event) + return super().mouseReleaseEvent(ev) - def wheelEvent(self, ev): - return GraphicsView.wheelEvent(self, ev.toQWheelEvent()) + def wheelEvent(self, wheel_event): + ev = self.deserialize_wheel_event(wheel_event) + return super().wheelEvent(ev) - def keyEvent(self, typ, mods, text, autorep, count): - typ = QtCore.QEvent.Type(typ) - mods = QtCore.Qt.KeyboardModifiers(mods) - GraphicsView.keyEvent(self, QtGui.QKeyEvent(typ, mods, text, autorep, count)) - return ev.accepted() - def enterEvent(self, lws): ev = QtGui.QEnterEvent(*lws) - return GraphicsView.enterEvent(self, ev) + return super().enterEvent(ev) def leaveEvent(self, typ): - ev = QtCore.QEvent(QtCore.QEvent.Type(typ)) - return GraphicsView.leaveEvent(self, ev) + typ = QtCore.QEvent.Type(typ) # this line needed by PyQt5 only + ev = QtCore.QEvent(typ) + return super().leaveEvent(ev) diff --git a/pyqtgraph/widgets/SpinBox.py b/pyqtgraph/widgets/SpinBox.py index bc83bb28..10c40c8e 100644 --- a/pyqtgraph/widgets/SpinBox.py +++ b/pyqtgraph/widgets/SpinBox.py @@ -114,7 +114,7 @@ class SpinBox(QtGui.QAbstractSpinBox): self.editingFinished.connect(self.editingFinishedEvent) def event(self, ev): - ret = QtGui.QAbstractSpinBox.event(self, ev) + ret = super().event(ev) if ev.type() == QtCore.QEvent.KeyPress and ev.key() == QtCore.Qt.Key_Return: ret = True ## For some reason, spinbox pretends to ignore return key press return ret @@ -596,7 +596,7 @@ class SpinBox(QtGui.QAbstractSpinBox): def paintEvent(self, ev): self._updateHeight() - QtGui.QAbstractSpinBox.paintEvent(self, ev) + super().paintEvent(ev) class ErrorBox(QtGui.QWidget): diff --git a/pyqtgraph/widgets/TableWidget.py b/pyqtgraph/widgets/TableWidget.py index c0e6ea90..3fc526ab 100644 --- a/pyqtgraph/widgets/TableWidget.py +++ b/pyqtgraph/widgets/TableWidget.py @@ -366,7 +366,7 @@ class TableWidget(QtGui.QTableWidget): ev.accept() self.copySel() else: - QtGui.QTableWidget.keyPressEvent(self, ev) + super().keyPressEvent(ev) def handleItemChanged(self, item): item.itemChanged() diff --git a/pyqtgraph/widgets/TreeWidget.py b/pyqtgraph/widgets/TreeWidget.py index 8c55ae2f..2f8ce544 100644 --- a/pyqtgraph/widgets/TreeWidget.py +++ b/pyqtgraph/widgets/TreeWidget.py @@ -144,7 +144,7 @@ class TreeWidget(QtGui.QTreeWidget): return items def dropEvent(self, ev): - QtGui.QTreeWidget.dropEvent(self, ev) + super().dropEvent(ev) self.updateDropFlags() def updateDropFlags(self): diff --git a/pyqtgraph/widgets/ValueLabel.py b/pyqtgraph/widgets/ValueLabel.py index b24fb16c..0714689f 100644 --- a/pyqtgraph/widgets/ValueLabel.py +++ b/pyqtgraph/widgets/ValueLabel.py @@ -58,7 +58,7 @@ class ValueLabel(QtGui.QLabel): def paintEvent(self, ev): self.setText(self.generateText()) - return QtGui.QLabel.paintEvent(self, ev) + return super().paintEvent(ev) def generateText(self): if len(self.values) == 0: