commit
4a3fb81535
14
.travis.yml
14
.travis.yml
@ -17,10 +17,10 @@ env:
|
|||||||
# Enable python 2 and python 3 builds
|
# Enable python 2 and python 3 builds
|
||||||
# Note that the 2.6 build doesn't get flake8, and runs old versions of
|
# Note that the 2.6 build doesn't get flake8, and runs old versions of
|
||||||
# Pyglet and GLFW to make sure we deal with those correctly
|
# Pyglet and GLFW to make sure we deal with those correctly
|
||||||
- PYTHON=2.6 QT=pyqt TEST=standard
|
- PYTHON=2.6 QT=pyqt4 TEST=standard
|
||||||
- PYTHON=2.7 QT=pyqt TEST=extra
|
- PYTHON=2.7 QT=pyqt4 TEST=extra
|
||||||
- PYTHON=2.7 QT=pyside TEST=standard
|
- PYTHON=2.7 QT=pyside TEST=standard
|
||||||
- PYTHON=3.4 QT=pyqt TEST=standard
|
- PYTHON=3.4 QT=pyqt5 TEST=standard
|
||||||
# - PYTHON=3.4 QT=pyside TEST=standard # pyside isn't available for 3.4 with conda
|
# - PYTHON=3.4 QT=pyside TEST=standard # pyside isn't available for 3.4 with conda
|
||||||
#- PYTHON=3.2 QT=pyqt5 TEST=standard
|
#- PYTHON=3.2 QT=pyqt5 TEST=standard
|
||||||
|
|
||||||
@ -56,9 +56,12 @@ install:
|
|||||||
- echo ${TEST}
|
- echo ${TEST}
|
||||||
- echo ${PYTHON}
|
- echo ${PYTHON}
|
||||||
|
|
||||||
- if [ "${QT}" == "pyqt" ]; then
|
- if [ "${QT}" == "pyqt5" ]; then
|
||||||
conda install pyqt --yes;
|
conda install pyqt --yes;
|
||||||
fi;
|
fi;
|
||||||
|
- if [ "${QT}" == "pyqt4" ]; then
|
||||||
|
conda install pyqt=4 --yes;
|
||||||
|
fi;
|
||||||
- if [ "${QT}" == "pyside" ]; then
|
- if [ "${QT}" == "pyside" ]; then
|
||||||
conda install pyside --yes;
|
conda install pyside --yes;
|
||||||
fi;
|
fi;
|
||||||
@ -135,6 +138,9 @@ script:
|
|||||||
|
|
||||||
- source activate test_env
|
- source activate test_env
|
||||||
|
|
||||||
|
# Check system info
|
||||||
|
- python -c "import pyqtgraph as pg; pg.systemInfo()"
|
||||||
|
|
||||||
# Run unit tests
|
# Run unit tests
|
||||||
- start_test "unit tests";
|
- start_test "unit tests";
|
||||||
PYTHONPATH=. py.test --cov pyqtgraph -sv;
|
PYTHONPATH=. py.test --cov pyqtgraph -sv;
|
||||||
|
58
CHANGELOG
58
CHANGELOG
@ -1,31 +1,55 @@
|
|||||||
pyqtgraph-0.9.11 [unreleased]
|
pyqtgraph-0.10.0 [unreleased]
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
- Fixed git version string generation on python3
|
|
||||||
- Fixed setting default values for out-of-bound points in pg.interpolateArray
|
|
||||||
- Fixed plot downsampling bug on python 3
|
|
||||||
- DockArea:
|
|
||||||
- Fixed adding Docks to DockArea after all Docks have been removed
|
|
||||||
- Fixed DockArea save/restoreState when area is empty
|
|
||||||
- Properly remove select box when export dialog is closed using window decorations
|
|
||||||
- Remove all modifications to builtins
|
|
||||||
- Fix SpinBox decimals
|
|
||||||
|
|
||||||
API / behavior changes:
|
|
||||||
- Change the defaut color kwarg to None in TextItem.setText() to avoid changing
|
|
||||||
the color everytime the text is changed.
|
|
||||||
|
|
||||||
New Features:
|
New Features:
|
||||||
- Preliminary PyQt5 support
|
- PyQt5 support
|
||||||
|
- Options for interpreting image data as either row-major or col-major
|
||||||
|
- InfiniteLine and LinearRegionItem can have attached labels
|
||||||
- DockArea:
|
- DockArea:
|
||||||
- Dock titles can be changed after creation
|
- Dock titles can be changed after creation
|
||||||
- Added Dock.sigClosed
|
- Added Dock.sigClosed
|
||||||
- Added TextItem.setColor()
|
- Added TextItem.setColor()
|
||||||
|
- FillBetweenItem supports finite-connected curves (those that exclude nan/inf)
|
||||||
|
|
||||||
|
API / behavior changes:
|
||||||
|
- Improved ImageItem performance for some data types by scaling LUT instead of image
|
||||||
|
- Change the defaut color kwarg to None in TextItem.setText() to avoid changing
|
||||||
|
the color every time the text is changed.
|
||||||
|
- FFT plots skip first sample if x-axis uses log scaling
|
||||||
|
- Multiprocessing system adds bytes and unicode to the default list of no-proxy data types
|
||||||
|
- Version number scheme changed to be PEP440-compliant (only affects installations from non-
|
||||||
|
release git commits)
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Fix for numpy API change that caused casting errors for inplace operations
|
||||||
|
- Fixed git version string generation on python3
|
||||||
|
- Fixed setting default values for out-of-bound points in pg.interpolateArray
|
||||||
|
- Fixed plot downsampling bug on python 3
|
||||||
|
- Fixed invalid slice in ImageItem.getHistogram
|
||||||
|
- DockArea:
|
||||||
|
- Fixed adding Docks to DockArea after all Docks have been removed
|
||||||
|
- Fixed DockArea save/restoreState when area is empty
|
||||||
|
- Properly remove select box when export dialog is closed using window decorations
|
||||||
|
- Remove all modifications to python builtins
|
||||||
|
- Better Python 2.6 compatibility
|
||||||
|
- Fix SpinBox decimals
|
||||||
|
- Fixed numerous issues with ImageItem automatic downsampling
|
||||||
|
- Fixed PlotItem average curves using incorrect stepMode
|
||||||
|
- Fixed TableWidget eating key events
|
||||||
|
- Prevent redundant updating of flowchart nodes with multiple inputs
|
||||||
|
- Ignore wheel events in GraphicsView if mouse interaction is disabled
|
||||||
|
- Correctly pass calls to QWidget.close() up the inheritance chain
|
||||||
|
- ColorMap forces color inputs to be sorted
|
||||||
|
- Fixed memory mapping for RemoteGraphicsView in OSX
|
||||||
|
- Fixed QPropertyAnimation str/bytes handling
|
||||||
|
- Fixed __version__ string update when using `setup.py install` with newer setuptools
|
||||||
|
|
||||||
Maintenance:
|
Maintenance:
|
||||||
|
- Image comparison system for unit testing plus tests for several graphics items
|
||||||
|
- Travis CI and coveralls/codecov support
|
||||||
- Add examples to unit tests
|
- Add examples to unit tests
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pyqtgraph-0.9.10
|
pyqtgraph-0.9.10
|
||||||
|
|
||||||
Fixed installation issues with more recent pip versions.
|
Fixed installation issues with more recent pip versions.
|
||||||
|
@ -39,6 +39,7 @@ Contributors
|
|||||||
* Martin Fitzpatrick
|
* Martin Fitzpatrick
|
||||||
* Daniel Lidstrom
|
* Daniel Lidstrom
|
||||||
* Eric Dill
|
* Eric Dill
|
||||||
|
* Vincent LeSaux
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
------------
|
------------
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
RawImageWidget
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. autoclass:: pyqtgraph.RawImageWidget
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automethod:: pyqtgraph.RawImageWidget.__init__
|
|
||||||
|
|
@ -25,14 +25,22 @@ else:
|
|||||||
|
|
||||||
#QtGui.QApplication.setGraphicsSystem('raster')
|
#QtGui.QApplication.setGraphicsSystem('raster')
|
||||||
app = QtGui.QApplication([])
|
app = QtGui.QApplication([])
|
||||||
#mw = QtGui.QMainWindow()
|
|
||||||
#mw.resize(800,800)
|
|
||||||
|
|
||||||
win = QtGui.QMainWindow()
|
win = QtGui.QMainWindow()
|
||||||
win.setWindowTitle('pyqtgraph example: VideoSpeedTest')
|
win.setWindowTitle('pyqtgraph example: VideoSpeedTest')
|
||||||
ui = VideoTemplate.Ui_MainWindow()
|
ui = VideoTemplate.Ui_MainWindow()
|
||||||
ui.setupUi(win)
|
ui.setupUi(win)
|
||||||
win.show()
|
win.show()
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pyqtgraph.widgets.RawImageWidget import RawImageGLWidget
|
||||||
|
except ImportError:
|
||||||
|
ui.rawGLRadio.setEnabled(False)
|
||||||
|
ui.rawGLRadio.setText(ui.rawGLRadio.text() + " (OpenGL not available)")
|
||||||
|
else:
|
||||||
|
ui.rawGLImg = RawImageGLWidget()
|
||||||
|
ui.stack.addWidget(ui.rawGLImg)
|
||||||
|
|
||||||
ui.maxSpin1.setOpts(value=255, step=1)
|
ui.maxSpin1.setOpts(value=255, step=1)
|
||||||
ui.minSpin1.setOpts(value=0, step=1)
|
ui.minSpin1.setOpts(value=0, step=1)
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<widget class="QStackedWidget" name="stack">
|
<widget class="QStackedWidget" name="stack">
|
||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>2</number>
|
<number>1</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="page">
|
<widget class="QWidget" name="page">
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
@ -74,13 +74,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="page_3">
|
|
||||||
<layout class="QGridLayout" name="gridLayout_5">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="RawImageGLWidget" name="rawGLImg" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0">
|
<item row="4" column="0">
|
||||||
@ -340,12 +333,6 @@
|
|||||||
<extends>QDoubleSpinBox</extends>
|
<extends>QDoubleSpinBox</extends>
|
||||||
<header>pyqtgraph</header>
|
<header>pyqtgraph</header>
|
||||||
</customwidget>
|
</customwidget>
|
||||||
<customwidget>
|
|
||||||
<class>RawImageGLWidget</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>pyqtgraph.widgets.RawImageWidget</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
</customwidgets>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file './examples/VideoTemplate.ui'
|
# Form implementation generated from reading ui file 'examples/VideoTemplate.ui'
|
||||||
#
|
#
|
||||||
# Created: Mon Feb 17 20:39:30 2014
|
# Created by: PyQt4 UI code generator 4.11.4
|
||||||
# by: PyQt4 UI code generator 4.10.3
|
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
@ -69,14 +68,6 @@ class Ui_MainWindow(object):
|
|||||||
self.rawImg.setObjectName(_fromUtf8("rawImg"))
|
self.rawImg.setObjectName(_fromUtf8("rawImg"))
|
||||||
self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1)
|
||||||
self.stack.addWidget(self.page_2)
|
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.gridLayout.addWidget(self.stack, 0, 0, 1, 1)
|
||||||
self.rawGLRadio = QtGui.QRadioButton(self.centralwidget)
|
self.rawGLRadio = QtGui.QRadioButton(self.centralwidget)
|
||||||
self.rawGLRadio.setObjectName(_fromUtf8("rawGLRadio"))
|
self.rawGLRadio.setObjectName(_fromUtf8("rawGLRadio"))
|
||||||
@ -193,7 +184,7 @@ class Ui_MainWindow(object):
|
|||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
self.stack.setCurrentIndex(2)
|
self.stack.setCurrentIndex(1)
|
||||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
def retranslateUi(self, MainWindow):
|
def retranslateUi(self, MainWindow):
|
||||||
@ -217,5 +208,5 @@ class Ui_MainWindow(object):
|
|||||||
self.rgbCheck.setText(_translate("MainWindow", "RGB", None))
|
self.rgbCheck.setText(_translate("MainWindow", "RGB", None))
|
||||||
self.label_5.setText(_translate("MainWindow", "Image size", None))
|
self.label_5.setText(_translate("MainWindow", "Image size", None))
|
||||||
|
|
||||||
from pyqtgraph.widgets.RawImageWidget import RawImageGLWidget, RawImageWidget
|
from pyqtgraph import GradientWidget, GraphicsView, SpinBox
|
||||||
from pyqtgraph import GradientWidget, SpinBox, GraphicsView
|
from pyqtgraph.widgets.RawImageWidget import RawImageWidget
|
||||||
|
199
examples/VideoTemplate_pyqt5.py
Normal file
199
examples/VideoTemplate_pyqt5.py
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Form implementation generated from reading ui file 'examples/VideoTemplate.ui'
|
||||||
|
#
|
||||||
|
# Created by: PyQt5 UI code generator 5.5.1
|
||||||
|
#
|
||||||
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
|
from PyQt5 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.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.Expanding, QtWidgets.QSizePolicy.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.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.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.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.Expanding, QtWidgets.QSizePolicy.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.Expanding, QtWidgets.QSizePolicy.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.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.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.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.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.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
|
@ -1,9 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Form implementation generated from reading ui file './examples/VideoTemplate.ui'
|
# Form implementation generated from reading ui file 'examples/VideoTemplate.ui'
|
||||||
#
|
#
|
||||||
# Created: Mon Feb 17 20:39:30 2014
|
# Created: Wed Oct 26 09:21:01 2016
|
||||||
# by: pyside-uic 0.2.14 running on PySide 1.1.2
|
# by: pyside-uic 0.2.15 running on PySide 1.2.2
|
||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
@ -55,14 +55,6 @@ class Ui_MainWindow(object):
|
|||||||
self.rawImg.setObjectName("rawImg")
|
self.rawImg.setObjectName("rawImg")
|
||||||
self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1)
|
self.gridLayout_4.addWidget(self.rawImg, 0, 0, 1, 1)
|
||||||
self.stack.addWidget(self.page_2)
|
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.gridLayout.addWidget(self.stack, 0, 0, 1, 1)
|
||||||
self.rawGLRadio = QtGui.QRadioButton(self.centralwidget)
|
self.rawGLRadio = QtGui.QRadioButton(self.centralwidget)
|
||||||
self.rawGLRadio.setObjectName("rawGLRadio")
|
self.rawGLRadio.setObjectName("rawGLRadio")
|
||||||
@ -179,7 +171,7 @@ class Ui_MainWindow(object):
|
|||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
self.stack.setCurrentIndex(2)
|
self.stack.setCurrentIndex(1)
|
||||||
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
QtCore.QMetaObject.connectSlotsByName(MainWindow)
|
||||||
|
|
||||||
def retranslateUi(self, MainWindow):
|
def retranslateUi(self, MainWindow):
|
||||||
@ -203,5 +195,5 @@ class Ui_MainWindow(object):
|
|||||||
self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
self.rgbCheck.setText(QtGui.QApplication.translate("MainWindow", "RGB", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
self.label_5.setText(QtGui.QApplication.translate("MainWindow", "Image size", None, QtGui.QApplication.UnicodeUTF8))
|
self.label_5.setText(QtGui.QApplication.translate("MainWindow", "Image size", None, QtGui.QApplication.UnicodeUTF8))
|
||||||
|
|
||||||
from pyqtgraph.widgets.RawImageWidget import RawImageGLWidget, RawImageWidget
|
from pyqtgraph.widgets.RawImageWidget import RawImageWidget
|
||||||
from pyqtgraph import GradientWidget, SpinBox, GraphicsView
|
from pyqtgraph import SpinBox, GradientWidget, GraphicsView
|
||||||
|
@ -89,7 +89,7 @@ def wlPen(wl):
|
|||||||
return pen
|
return pen
|
||||||
|
|
||||||
|
|
||||||
class ParamObj:
|
class ParamObj(object):
|
||||||
# Just a helper for tracking parameters and responding to changes
|
# Just a helper for tracking parameters and responding to changes
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__params = {}
|
self.__params = {}
|
||||||
@ -109,7 +109,8 @@ class ParamObj:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return self.getParam(item)
|
# bug in pyside 1.2.2 causes getitem to be called inside QGraphicsObject.parentItem:
|
||||||
|
return self.getParam(item) # PySide bug: https://bugreports.qt.io/browse/PYSIDE-441
|
||||||
|
|
||||||
def getParam(self, param):
|
def getParam(self, param):
|
||||||
return self.__params[param]
|
return self.__params[param]
|
||||||
|
@ -46,7 +46,6 @@ def update():
|
|||||||
global curvePoint, index
|
global curvePoint, index
|
||||||
index = (index + 1) % len(x)
|
index = (index + 1) % len(x)
|
||||||
curvePoint.setPos(float(index)/(len(x)-1))
|
curvePoint.setPos(float(index)/(len(x)-1))
|
||||||
#text2.viewRangeChanged()
|
|
||||||
text2.setText('[%0.1f, %0.1f]' % (x[index], y[index]))
|
text2.setText('[%0.1f, %0.1f]' % (x[index], y[index]))
|
||||||
|
|
||||||
timer = QtCore.QTimer()
|
timer = QtCore.QTimer()
|
||||||
|
@ -83,8 +83,9 @@ class Tracer(object):
|
|||||||
funcname = cls.__name__ + "." + funcname
|
funcname = cls.__name__ + "." + funcname
|
||||||
return "%s: %s %s: %s" % (callline, filename, lineno, funcname)
|
return "%s: %s %s: %s" % (callline, filename, lineno, funcname)
|
||||||
|
|
||||||
|
|
||||||
def warnOnException(func):
|
def warnOnException(func):
|
||||||
"""Decorator which catches/ignores exceptions and prints a stack trace."""
|
"""Decorator that catches/ignores exceptions and prints a stack trace."""
|
||||||
def w(*args, **kwds):
|
def w(*args, **kwds):
|
||||||
try:
|
try:
|
||||||
func(*args, **kwds)
|
func(*args, **kwds)
|
||||||
@ -92,11 +93,9 @@ def warnOnException(func):
|
|||||||
printExc('Ignored exception:')
|
printExc('Ignored exception:')
|
||||||
return w
|
return w
|
||||||
|
|
||||||
|
|
||||||
def getExc(indent=4, prefix='| ', skip=1):
|
def getExc(indent=4, prefix='| ', skip=1):
|
||||||
lines = (traceback.format_stack()[:-skip]
|
lines = formatException(*sys.exc_info(), skip=skip)
|
||||||
+ [" ---- exception caught ---->\n"]
|
|
||||||
+ traceback.format_tb(sys.exc_info()[2])
|
|
||||||
+ traceback.format_exception_only(*sys.exc_info()[:2]))
|
|
||||||
lines2 = []
|
lines2 = []
|
||||||
for l in lines:
|
for l in lines:
|
||||||
lines2.extend(l.strip('\n').split('\n'))
|
lines2.extend(l.strip('\n').split('\n'))
|
||||||
@ -113,6 +112,7 @@ def printExc(msg='', indent=4, prefix='|'):
|
|||||||
print(exc)
|
print(exc)
|
||||||
print(" "*indent + prefix + '='*30 + '<<')
|
print(" "*indent + prefix + '='*30 + '<<')
|
||||||
|
|
||||||
|
|
||||||
def printTrace(msg='', indent=4, prefix='|'):
|
def printTrace(msg='', indent=4, prefix='|'):
|
||||||
"""Print an error message followed by an indented stack trace"""
|
"""Print an error message followed by an indented stack trace"""
|
||||||
trace = backtrace(1)
|
trace = backtrace(1)
|
||||||
@ -128,6 +128,29 @@ def backtrace(skip=0):
|
|||||||
return ''.join(traceback.format_stack()[:-(skip+1)])
|
return ''.join(traceback.format_stack()[:-(skip+1)])
|
||||||
|
|
||||||
|
|
||||||
|
def formatException(exctype, value, tb, skip=0):
|
||||||
|
"""Return a list of formatted exception strings.
|
||||||
|
|
||||||
|
Similar to traceback.format_exception, but displays the entire stack trace
|
||||||
|
rather than just the portion downstream of the point where the exception is
|
||||||
|
caught. In particular, unhandled exceptions that occur during Qt signal
|
||||||
|
handling do not usually show the portion of the stack that emitted the
|
||||||
|
signal.
|
||||||
|
"""
|
||||||
|
lines = traceback.format_exception(exctype, value, tb)
|
||||||
|
lines = [lines[0]] + traceback.format_stack()[:-(skip+1)] + [' --- exception caught here ---\n'] + lines[1:]
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
def printException(exctype, value, traceback):
|
||||||
|
"""Print an exception with its full traceback.
|
||||||
|
|
||||||
|
Set `sys.excepthook = printException` to ensure that exceptions caught
|
||||||
|
inside Qt signal handlers are printed with their full stack trace.
|
||||||
|
"""
|
||||||
|
print(''.join(formatException(exctype, value, traceback, skip=1)))
|
||||||
|
|
||||||
|
|
||||||
def listObjs(regex='Q', typ=None):
|
def listObjs(regex='Q', typ=None):
|
||||||
"""List all objects managed by python gc with class name matching regex.
|
"""List all objects managed by python gc with class name matching regex.
|
||||||
Finds 'Q...' classes by default."""
|
Finds 'Q...' classes by default."""
|
||||||
|
@ -164,8 +164,15 @@ class Gaussian(CtrlNode):
|
|||||||
import scipy.ndimage
|
import scipy.ndimage
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise Exception("GaussianFilter node requires the package scipy.ndimage.")
|
raise Exception("GaussianFilter node requires the package scipy.ndimage.")
|
||||||
return pgfn.gaussianFilter(data, self.ctrls['sigma'].value())
|
|
||||||
|
|
||||||
|
if hasattr(data, 'implements') and data.implements('MetaArray'):
|
||||||
|
info = data.infoCopy()
|
||||||
|
filt = pgfn.gaussianFilter(data.asarray(), self.ctrls['sigma'].value())
|
||||||
|
if 'values' in info[0]:
|
||||||
|
info[0]['values'] = info[0]['values'][:filt.shape[0]]
|
||||||
|
return metaarray.MetaArray(filt, info=info)
|
||||||
|
else:
|
||||||
|
return pgfn.gaussianFilter(data, self.ctrls['sigma'].value())
|
||||||
|
|
||||||
class Derivative(CtrlNode):
|
class Derivative(CtrlNode):
|
||||||
"""Returns the pointwise derivative of the input"""
|
"""Returns the pointwise derivative of the input"""
|
||||||
|
@ -48,6 +48,7 @@ class TextItem(GraphicsObject):
|
|||||||
self.textItem = QtGui.QGraphicsTextItem()
|
self.textItem = QtGui.QGraphicsTextItem()
|
||||||
self.textItem.setParentItem(self)
|
self.textItem.setParentItem(self)
|
||||||
self._lastTransform = None
|
self._lastTransform = None
|
||||||
|
self._lastScene = None
|
||||||
self._bounds = QtCore.QRectF()
|
self._bounds = QtCore.QRectF()
|
||||||
if html is None:
|
if html is None:
|
||||||
self.setColor(color)
|
self.setColor(color)
|
||||||
@ -149,9 +150,18 @@ class TextItem(GraphicsObject):
|
|||||||
self.updateTransform()
|
self.updateTransform()
|
||||||
|
|
||||||
def paint(self, p, *args):
|
def paint(self, p, *args):
|
||||||
# this is not ideal because it causes another update to be scheduled.
|
# this is not ideal because it requires the transform to be updated at every draw.
|
||||||
# ideally, we would have a sceneTransformChanged event to react to..
|
# ideally, we would have a sceneTransformChanged event to react to..
|
||||||
|
s = self.scene()
|
||||||
|
ls = self._lastScene
|
||||||
|
if s is not ls:
|
||||||
|
if ls is not None:
|
||||||
|
ls.sigPrepareForPaint.disconnect(self.updateTransform)
|
||||||
|
self._lastScene = s
|
||||||
|
if s is not None:
|
||||||
|
s.sigPrepareForPaint.connect(self.updateTransform)
|
||||||
self.updateTransform()
|
self.updateTransform()
|
||||||
|
p.setTransform(self.sceneTransform())
|
||||||
|
|
||||||
if self.border.style() != QtCore.Qt.NoPen or self.fill.style() != QtCore.Qt.NoBrush:
|
if self.border.style() != QtCore.Qt.NoPen or self.fill.style() != QtCore.Qt.NoBrush:
|
||||||
p.setPen(self.border)
|
p.setPen(self.border)
|
||||||
@ -191,5 +201,3 @@ class TextItem(GraphicsObject):
|
|||||||
self._lastTransform = pt
|
self._lastTransform = pt
|
||||||
|
|
||||||
self.updateTextPos()
|
self.updateTextPos()
|
||||||
|
|
||||||
|
|
||||||
|
@ -312,7 +312,8 @@ class Parameter(QtCore.QObject):
|
|||||||
If blockSignals is True, no signals will be emitted until the tree has been completely restored.
|
If blockSignals is True, no signals will be emitted until the tree has been completely restored.
|
||||||
This prevents signal handlers from responding to a partially-rebuilt network.
|
This prevents signal handlers from responding to a partially-rebuilt network.
|
||||||
"""
|
"""
|
||||||
childState = state.get('children', [])
|
state = state.copy()
|
||||||
|
childState = state.pop('children', [])
|
||||||
|
|
||||||
## list of children may be stored either as list or dict.
|
## list of children may be stored either as list or dict.
|
||||||
if isinstance(childState, dict):
|
if isinstance(childState, dict):
|
||||||
|
@ -285,8 +285,6 @@ class WidgetParameterItem(ParameterItem):
|
|||||||
self.updateDisplayLabel()
|
self.updateDisplayLabel()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class EventProxy(QtCore.QObject):
|
class EventProxy(QtCore.QObject):
|
||||||
def __init__(self, qobj, callback):
|
def __init__(self, qobj, callback):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
@ -297,8 +295,6 @@ class EventProxy(QtCore.QObject):
|
|||||||
return self.callback(obj, ev)
|
return self.callback(obj, ev)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleParameter(Parameter):
|
class SimpleParameter(Parameter):
|
||||||
itemClass = WidgetParameterItem
|
itemClass = WidgetParameterItem
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ if sys.version[0] >= '3':
|
|||||||
else:
|
else:
|
||||||
import httplib
|
import httplib
|
||||||
import urllib
|
import urllib
|
||||||
from ..Qt import QtGui, QtCore, QtTest
|
from ..Qt import QtGui, QtCore, QtTest, QT_LIB
|
||||||
from .. import functions as fn
|
from .. import functions as fn
|
||||||
from .. import GraphicsLayoutWidget
|
from .. import GraphicsLayoutWidget
|
||||||
from .. import ImageItem, TextItem
|
from .. import ImageItem, TextItem
|
||||||
@ -212,7 +212,7 @@ def assertImageApproved(image, standardFile, message=None, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
||||||
pxCount=0, maxPxDiff=None, avgPxDiff=None,
|
pxCount=-1, maxPxDiff=None, avgPxDiff=None,
|
||||||
imgDiff=None):
|
imgDiff=None):
|
||||||
"""Check that two images match.
|
"""Check that two images match.
|
||||||
|
|
||||||
@ -234,7 +234,8 @@ def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
|||||||
pxThreshold : float
|
pxThreshold : float
|
||||||
Minimum value difference at which two pixels are considered different
|
Minimum value difference at which two pixels are considered different
|
||||||
pxCount : int or None
|
pxCount : int or None
|
||||||
Maximum number of pixels that may differ
|
Maximum number of pixels that may differ. Default is 0 for Qt4 and
|
||||||
|
1% of image size for Qt5.
|
||||||
maxPxDiff : float or None
|
maxPxDiff : float or None
|
||||||
Maximum allowed difference between pixels
|
Maximum allowed difference between pixels
|
||||||
avgPxDiff : float or None
|
avgPxDiff : float or None
|
||||||
@ -247,6 +248,14 @@ def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
|||||||
assert im1.shape[2] == 4
|
assert im1.shape[2] == 4
|
||||||
assert im1.dtype == im2.dtype
|
assert im1.dtype == im2.dtype
|
||||||
|
|
||||||
|
if pxCount == -1:
|
||||||
|
if QT_LIB == 'PyQt5':
|
||||||
|
# Qt5 generates slightly different results; relax the tolerance
|
||||||
|
# until test images are updated.
|
||||||
|
pxCount = int(im1.shape[0] * im1.shape[1] * 0.01)
|
||||||
|
else:
|
||||||
|
pxCount = 0
|
||||||
|
|
||||||
diff = im1.astype(float) - im2.astype(float)
|
diff = im1.astype(float) - im2.astype(float)
|
||||||
if imgDiff is not None:
|
if imgDiff is not None:
|
||||||
assert np.abs(diff).sum() <= imgDiff
|
assert np.abs(diff).sum() <= imgDiff
|
||||||
|
@ -6,7 +6,10 @@ if not USE_PYQT5:
|
|||||||
matplotlib.rcParams['backend.qt4']='PySide'
|
matplotlib.rcParams['backend.qt4']='PySide'
|
||||||
|
|
||||||
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
|
try:
|
||||||
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
|
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
|
||||||
|
except ImportError:
|
||||||
|
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
|
||||||
else:
|
else:
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
|
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
|
||||||
|
@ -3,7 +3,9 @@ try:
|
|||||||
from ..Qt import QtOpenGL
|
from ..Qt import QtOpenGL
|
||||||
from OpenGL.GL import *
|
from OpenGL.GL import *
|
||||||
HAVE_OPENGL = True
|
HAVE_OPENGL = True
|
||||||
except ImportError:
|
except Exception:
|
||||||
|
# Would prefer `except ImportError` here, but some versions of pyopengl generate
|
||||||
|
# AttributeError upon import
|
||||||
HAVE_OPENGL = False
|
HAVE_OPENGL = False
|
||||||
|
|
||||||
from .. import functions as fn
|
from .. import functions as fn
|
||||||
@ -59,6 +61,7 @@ class RawImageWidget(QtGui.QWidget):
|
|||||||
#p.drawPixmap(self.rect(), self.pixmap)
|
#p.drawPixmap(self.rect(), self.pixmap)
|
||||||
p.end()
|
p.end()
|
||||||
|
|
||||||
|
|
||||||
if HAVE_OPENGL:
|
if HAVE_OPENGL:
|
||||||
class RawImageGLWidget(QtOpenGL.QGLWidget):
|
class RawImageGLWidget(QtOpenGL.QGLWidget):
|
||||||
"""
|
"""
|
||||||
|
70
setup.py
70
setup.py
@ -42,10 +42,22 @@ try:
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
from setuptools.command import install
|
from setuptools.command import install
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
sys.stderr.write("Warning: could not import setuptools; falling back to distutils.\n")
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
from distutils.command import install
|
from distutils.command import install
|
||||||
|
|
||||||
|
|
||||||
|
# Work around mbcs bug in distutils.
|
||||||
|
# http://bugs.python.org/issue10945
|
||||||
|
import codecs
|
||||||
|
try:
|
||||||
|
codecs.lookup('mbcs')
|
||||||
|
except LookupError:
|
||||||
|
ascii = codecs.lookup('ascii')
|
||||||
|
func = lambda name, enc=ascii: {True: enc}.get(name=='mbcs')
|
||||||
|
codecs.register(func)
|
||||||
|
|
||||||
|
|
||||||
path = os.path.split(__file__)[0]
|
path = os.path.split(__file__)[0]
|
||||||
sys.path.insert(0, os.path.join(path, 'tools'))
|
sys.path.insert(0, os.path.join(path, 'tools'))
|
||||||
import setupHelpers as helpers
|
import setupHelpers as helpers
|
||||||
@ -62,11 +74,9 @@ version, forcedVersion, gitVersion, initVersion = helpers.getVersionStrings(pkg=
|
|||||||
class Build(build.build):
|
class Build(build.build):
|
||||||
"""
|
"""
|
||||||
* Clear build path before building
|
* Clear build path before building
|
||||||
* Set version string in __init__ after building
|
|
||||||
"""
|
"""
|
||||||
def run(self):
|
def run(self):
|
||||||
global path, version, initVersion, forcedVersion
|
global path
|
||||||
global buildVersion
|
|
||||||
|
|
||||||
## Make sure build directory is clean
|
## Make sure build directory is clean
|
||||||
buildPath = os.path.join(path, self.build_lib)
|
buildPath = os.path.join(path, self.build_lib)
|
||||||
@ -75,41 +85,47 @@ class Build(build.build):
|
|||||||
|
|
||||||
ret = build.build.run(self)
|
ret = build.build.run(self)
|
||||||
|
|
||||||
# If the version in __init__ is different from the automatically-generated
|
|
||||||
# version string, then we will update __init__ in the build directory
|
|
||||||
if initVersion == version:
|
|
||||||
return ret
|
|
||||||
|
|
||||||
try:
|
|
||||||
initfile = os.path.join(buildPath, 'pyqtgraph', '__init__.py')
|
|
||||||
data = open(initfile, 'r').read()
|
|
||||||
open(initfile, 'w').write(re.sub(r"__version__ = .*", "__version__ = '%s'" % version, data))
|
|
||||||
buildVersion = version
|
|
||||||
except:
|
|
||||||
if forcedVersion:
|
|
||||||
raise
|
|
||||||
buildVersion = initVersion
|
|
||||||
sys.stderr.write("Warning: Error occurred while setting version string in build path. "
|
|
||||||
"Installation will use the original version string "
|
|
||||||
"%s instead.\n" % (initVersion)
|
|
||||||
)
|
|
||||||
sys.excepthook(*sys.exc_info())
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
class Install(install.install):
|
class Install(install.install):
|
||||||
"""
|
"""
|
||||||
* Check for previously-installed version before installing
|
* Check for previously-installed version before installing
|
||||||
|
* Set version string in __init__ after building. This helps to ensure that we
|
||||||
|
know when an installation came from a non-release code base.
|
||||||
"""
|
"""
|
||||||
def run(self):
|
def run(self):
|
||||||
|
global path, version, initVersion, forcedVersion, installVersion
|
||||||
|
|
||||||
name = self.config_vars['dist_name']
|
name = self.config_vars['dist_name']
|
||||||
path = self.install_libbase
|
path = os.path.join(self.install_libbase, 'pyqtgraph')
|
||||||
if os.path.exists(path) and name in os.listdir(path):
|
if os.path.exists(path):
|
||||||
raise Exception("It appears another version of %s is already "
|
raise Exception("It appears another version of %s is already "
|
||||||
"installed at %s; remove this before installing."
|
"installed at %s; remove this before installing."
|
||||||
% (name, path))
|
% (name, path))
|
||||||
print("Installing to %s" % path)
|
print("Installing to %s" % path)
|
||||||
return install.install.run(self)
|
rval = install.install.run(self)
|
||||||
|
|
||||||
|
|
||||||
|
# If the version in __init__ is different from the automatically-generated
|
||||||
|
# version string, then we will update __init__ in the install directory
|
||||||
|
if initVersion == version:
|
||||||
|
return rval
|
||||||
|
|
||||||
|
try:
|
||||||
|
initfile = os.path.join(path, '__init__.py')
|
||||||
|
data = open(initfile, 'r').read()
|
||||||
|
open(initfile, 'w').write(re.sub(r"__version__ = .*", "__version__ = '%s'" % version, data))
|
||||||
|
installVersion = version
|
||||||
|
except:
|
||||||
|
sys.stderr.write("Warning: Error occurred while setting version string in build path. "
|
||||||
|
"Installation will use the original version string "
|
||||||
|
"%s instead.\n" % (initVersion)
|
||||||
|
)
|
||||||
|
if forcedVersion:
|
||||||
|
raise
|
||||||
|
installVersion = initVersion
|
||||||
|
sys.excepthook(*sys.exc_info())
|
||||||
|
|
||||||
|
return rval
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
|
252
tools/pg-release.py
Normal file
252
tools/pg-release.py
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
import os, sys, argparse, random
|
||||||
|
from shell import shell, ssh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
description="Build release packages for pyqtgraph."
|
||||||
|
|
||||||
|
epilog = """
|
||||||
|
Package build is done in several steps:
|
||||||
|
|
||||||
|
* Attempt to clone branch release-x.y.z from source-repo
|
||||||
|
* Merge release branch into master
|
||||||
|
* Write new version numbers into the source
|
||||||
|
* Roll over unreleased CHANGELOG entries
|
||||||
|
* Commit and tag new release
|
||||||
|
* Build HTML documentation
|
||||||
|
* Build source package
|
||||||
|
* Build deb packages (if running on Linux)
|
||||||
|
* Build Windows exe installers
|
||||||
|
|
||||||
|
Release packages may be published by using the --publish flag:
|
||||||
|
|
||||||
|
* Uploads release files to website
|
||||||
|
* Pushes tagged git commit to github
|
||||||
|
* Uploads source package to pypi
|
||||||
|
|
||||||
|
Building source packages requires:
|
||||||
|
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* python-sphinx
|
||||||
|
|
||||||
|
Building deb packages requires several dependencies:
|
||||||
|
|
||||||
|
* build-essential
|
||||||
|
* python-all, python3-all
|
||||||
|
* python-stdeb, python3-stdeb
|
||||||
|
|
||||||
|
Note: building windows .exe files should be possible on any OS. However,
|
||||||
|
Debian/Ubuntu systems do not include the necessary wininst*.exe files; these
|
||||||
|
must be manually copied from the Python source to the distutils/command
|
||||||
|
submodule path (/usr/lib/pythonX.X/distutils/command). Additionally, it may be
|
||||||
|
necessary to rename (or copy / link) wininst-9.0-amd64.exe to
|
||||||
|
wininst-6.0-amd64.exe.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
build_dir = os.path.join(path, 'release-build')
|
||||||
|
pkg_dir = os.path.join(path, 'release-packages')
|
||||||
|
|
||||||
|
ap = argparse.ArgumentParser(description=description, epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument('version', help='The x.y.z version to generate release packages for. '
|
||||||
|
'There must be a corresponding pyqtgraph-x.y.z branch in the source repository.')
|
||||||
|
ap.add_argument('--publish', metavar='', help='Publish previously built package files (must be stored in pkg-dir/version) and tagged release commit (from build-dir).', action='store_const', const=True, default=False)
|
||||||
|
ap.add_argument('--source-repo', metavar='', help='Repository from which release and master branches will be cloned. Default is the repo containing this script.', default=path)
|
||||||
|
ap.add_argument('--build-dir', metavar='', help='Directory where packages will be staged and built. Default is source_root/release-build.', default=build_dir)
|
||||||
|
ap.add_argument('--pkg-dir', metavar='', help='Directory where packages will be stored. Default is source_root/release-packages.', default=pkg_dir)
|
||||||
|
ap.add_argument('--skip-pip-test', metavar='', help='Skip testing pip install.', action='store_const', const=True, default=False)
|
||||||
|
ap.add_argument('--no-deb', metavar='', help='Skip building Debian packages.', action='store_const', const=True, default=False)
|
||||||
|
ap.add_argument('--no-exe', metavar='', help='Skip building Windows exe installers.', action='store_const', const=True, default=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def build(args):
|
||||||
|
if os.path.exists(args.build_dir):
|
||||||
|
sys.stderr.write("Please remove the build directory %s before proceeding, or specify a different path with --build-dir.\n" % args.build_dir)
|
||||||
|
sys.exit(-1)
|
||||||
|
if os.path.exists(args.pkg_dir):
|
||||||
|
sys.stderr.write("Please remove the package directory %s before proceeding, or specify a different path with --pkg-dir.\n" % args.pkg_dir)
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
# Clone source repository and tag the release branch
|
||||||
|
shell('''
|
||||||
|
# Clone and merge release branch into previous master
|
||||||
|
mkdir -p {build_dir}
|
||||||
|
cd {build_dir}
|
||||||
|
rm -rf pyqtgraph
|
||||||
|
git clone --depth 1 -b master {source_repo} pyqtgraph
|
||||||
|
cd pyqtgraph
|
||||||
|
git checkout -b release-{version}
|
||||||
|
git pull {source_repo} release-{version}
|
||||||
|
git checkout master
|
||||||
|
git merge --no-ff --no-commit release-{version}
|
||||||
|
|
||||||
|
# Write new version number into the source
|
||||||
|
sed -i "s/__version__ = .*/__version__ = '{version}'/" pyqtgraph/__init__.py
|
||||||
|
sed -i "s/version = .*/version = '{version}'/" doc/source/conf.py
|
||||||
|
sed -i "s/release = .*/release = '{version}'/" doc/source/conf.py
|
||||||
|
|
||||||
|
# make sure changelog mentions unreleased changes
|
||||||
|
grep "pyqtgraph-{version}.*unreleased.*" CHANGELOG
|
||||||
|
sed -i "s/pyqtgraph-{version}.*unreleased.*/pyqtgraph-{version}/" CHANGELOG
|
||||||
|
|
||||||
|
# Commit and tag new release
|
||||||
|
git commit -a -m "PyQtGraph release {version}"
|
||||||
|
git tag pyqtgraph-{version}
|
||||||
|
|
||||||
|
# Build HTML documentation
|
||||||
|
cd doc
|
||||||
|
make clean
|
||||||
|
make html
|
||||||
|
cd ..
|
||||||
|
find ./ -name "*.pyc" -delete
|
||||||
|
|
||||||
|
# package source distribution
|
||||||
|
python setup.py sdist
|
||||||
|
|
||||||
|
mkdir -p {pkg_dir}
|
||||||
|
cp dist/*.tar.gz {pkg_dir}
|
||||||
|
|
||||||
|
# source package build complete.
|
||||||
|
'''.format(**args.__dict__))
|
||||||
|
|
||||||
|
|
||||||
|
if args.skip_pip_test:
|
||||||
|
args.pip_test = 'skipped'
|
||||||
|
else:
|
||||||
|
shell('''
|
||||||
|
# test pip install source distribution
|
||||||
|
rm -rf release-{version}-virtenv
|
||||||
|
virtualenv --system-site-packages release-{version}-virtenv
|
||||||
|
. release-{version}-virtenv/bin/activate
|
||||||
|
echo "PATH: $PATH"
|
||||||
|
echo "ENV: $VIRTUAL_ENV"
|
||||||
|
pip install --no-index --no-deps dist/pyqtgraph-{version}.tar.gz
|
||||||
|
deactivate
|
||||||
|
|
||||||
|
# pip install test passed
|
||||||
|
'''.format(**args.__dict__))
|
||||||
|
args.pip_test = 'passed'
|
||||||
|
|
||||||
|
|
||||||
|
if 'linux' in sys.platform and not args.no_deb:
|
||||||
|
shell('''
|
||||||
|
# build deb packages
|
||||||
|
cd {build_dir}/pyqtgraph
|
||||||
|
python setup.py --command-packages=stdeb.command sdist_dsc
|
||||||
|
cd deb_dist/pyqtgraph-{version}
|
||||||
|
sed -i "s/^Depends:.*/Depends: python (>= 2.6), python-qt4 | python-pyside, python-numpy/" debian/control
|
||||||
|
dpkg-buildpackage
|
||||||
|
cd ../../
|
||||||
|
mv deb_dist {pkg_dir}/pyqtgraph-{version}-deb
|
||||||
|
|
||||||
|
# deb package build complete.
|
||||||
|
'''.format(**args.__dict__))
|
||||||
|
args.deb_status = 'built'
|
||||||
|
else:
|
||||||
|
args.deb_status = 'skipped'
|
||||||
|
|
||||||
|
|
||||||
|
if not args.no_exe:
|
||||||
|
shell("""
|
||||||
|
# Build windows executables
|
||||||
|
cd {build_dir}/pyqtgraph
|
||||||
|
python setup.py build bdist_wininst --plat-name=win32
|
||||||
|
python setup.py build bdist_wininst --plat-name=win-amd64
|
||||||
|
cp dist/*.exe {pkg_dir}
|
||||||
|
""".format(**args.__dict__))
|
||||||
|
args.exe_status = 'built'
|
||||||
|
else:
|
||||||
|
args.exe_status = 'skipped'
|
||||||
|
|
||||||
|
|
||||||
|
print(unindent("""
|
||||||
|
|
||||||
|
======== Build complete. =========
|
||||||
|
|
||||||
|
* Source package: built
|
||||||
|
* Pip install test: {pip_test}
|
||||||
|
* Debian packages: {deb_status}
|
||||||
|
* Windows installers: {exe_status}
|
||||||
|
* Package files in {pkg_dir}
|
||||||
|
|
||||||
|
Next steps to publish:
|
||||||
|
|
||||||
|
* Test all packages
|
||||||
|
* Run script again with --publish
|
||||||
|
|
||||||
|
""").format(**args.__dict__))
|
||||||
|
|
||||||
|
|
||||||
|
def publish(args):
|
||||||
|
|
||||||
|
|
||||||
|
if not os.path.isfile(os.path.expanduser('~/.pypirc')):
|
||||||
|
print(unindent("""
|
||||||
|
Missing ~/.pypirc file. Should look like:
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
[distutils]
|
||||||
|
index-servers =
|
||||||
|
pypi
|
||||||
|
|
||||||
|
[pypi]
|
||||||
|
username:your_username
|
||||||
|
password:your_password
|
||||||
|
|
||||||
|
"""))
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
### Upload everything to server
|
||||||
|
shell("""
|
||||||
|
# Uploading documentation..
|
||||||
|
cd {build_dir}/pyqtgraph
|
||||||
|
rsync -rv doc/build/* pyqtgraph.org:/www/code/pyqtgraph/pyqtgraph/documentation/build/
|
||||||
|
|
||||||
|
# Uploading release packages to website
|
||||||
|
rsync -v {pkg_dir}/{version} pyqtgraph.org:/www/code/pyqtgraph/downloads/
|
||||||
|
|
||||||
|
# Push to github
|
||||||
|
git push --tags https://github.com/pyqtgraph/pyqtgraph master:master
|
||||||
|
|
||||||
|
# Upload to pypi..
|
||||||
|
python setup.py sdist upload
|
||||||
|
|
||||||
|
""".format(**args.__dict__))
|
||||||
|
|
||||||
|
print(unindent("""
|
||||||
|
|
||||||
|
======== Upload complete. =========
|
||||||
|
|
||||||
|
Next steps to publish:
|
||||||
|
- update website
|
||||||
|
- mailing list announcement
|
||||||
|
- new conda recipe (http://conda.pydata.org/docs/build.html)
|
||||||
|
- contact deb maintainer (gianfranco costamagna)
|
||||||
|
- other package maintainers?
|
||||||
|
|
||||||
|
""").format(**args.__dict__))
|
||||||
|
|
||||||
|
|
||||||
|
def unindent(msg):
|
||||||
|
ind = 1e6
|
||||||
|
lines = msg.split('\n')
|
||||||
|
for line in lines:
|
||||||
|
if len(line.strip()) == 0:
|
||||||
|
continue
|
||||||
|
ind = min(ind, len(line) - len(line.lstrip()))
|
||||||
|
return '\n'.join([line[ind:] for line in lines])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = ap.parse_args()
|
||||||
|
args.build_dir = os.path.abspath(args.build_dir)
|
||||||
|
args.pkg_dir = os.path.join(os.path.abspath(args.pkg_dir), args.version)
|
||||||
|
|
||||||
|
if args.publish:
|
||||||
|
publish(args)
|
||||||
|
else:
|
||||||
|
build(args)
|
@ -1,30 +1,53 @@
|
|||||||
import os, sys
|
"""
|
||||||
## Search the package tree for all .ui files, compile each to
|
Script for compiling Qt Designer .ui files to .py
|
||||||
## a .py for pyqt and pyside
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
import os, sys, subprocess, tempfile
|
||||||
|
|
||||||
pyqtuic = 'pyuic4'
|
pyqtuic = 'pyuic4'
|
||||||
pysideuic = 'pyside-uic'
|
pysideuic = 'pyside-uic'
|
||||||
pyqt5uic = 'pyuic5'
|
pyqt5uic = 'pyuic5'
|
||||||
|
|
||||||
for path, sd, files in os.walk('.'):
|
usage = """Compile .ui files to .py for all supported pyqt/pyside versions.
|
||||||
|
|
||||||
|
Usage: python rebuildUi.py [.ui files|search paths]
|
||||||
|
|
||||||
|
May specify a list of .ui files and/or directories to search recursively for .ui files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if len(args) == 0:
|
||||||
|
print(usage)
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
uifiles = []
|
||||||
|
for arg in args:
|
||||||
|
if os.path.isfile(arg) and arg.endswith('.ui'):
|
||||||
|
uifiles.append(arg)
|
||||||
|
elif os.path.isdir(arg):
|
||||||
|
# recursively search for ui files in this directory
|
||||||
|
for path, sd, files in os.walk(arg):
|
||||||
for f in files:
|
for f in files:
|
||||||
base, ext = os.path.splitext(f)
|
if not f.endswith('.ui'):
|
||||||
if ext != '.ui':
|
|
||||||
continue
|
continue
|
||||||
ui = os.path.join(path, f)
|
uifiles.append(os.path.join(path, f))
|
||||||
|
else:
|
||||||
py = os.path.join(path, base + '_pyqt.py')
|
print('Argument "%s" is not a directory or .ui file.' % arg)
|
||||||
if not os.path.exists(py) or os.stat(ui).st_mtime > os.stat(py).st_mtime:
|
sys.exit(-1)
|
||||||
os.system('%s %s > %s' % (pyqtuic, ui, py))
|
|
||||||
print(py)
|
|
||||||
|
|
||||||
py = os.path.join(path, base + '_pyside.py')
|
|
||||||
if not os.path.exists(py) or os.stat(ui).st_mtime > os.stat(py).st_mtime:
|
|
||||||
os.system('%s %s > %s' % (pysideuic, ui, py))
|
|
||||||
print(py)
|
|
||||||
|
|
||||||
py = os.path.join(path, base + '_pyqt5.py')
|
|
||||||
if not os.path.exists(py) or os.stat(ui).st_mtime > os.stat(py).st_mtime:
|
|
||||||
os.system('%s %s > %s' % (pyqt5uic, ui, py))
|
|
||||||
print(py)
|
|
||||||
|
|
||||||
|
# rebuild all requested ui files
|
||||||
|
for ui in uifiles:
|
||||||
|
base, _ = os.path.splitext(ui)
|
||||||
|
for compiler, ext in [(pyqtuic, '_pyqt.py'), (pysideuic, '_pyside.py'), (pyqt5uic, '_pyqt5.py')]:
|
||||||
|
py = base + ext
|
||||||
|
if os.path.exists(py) and os.stat(ui).st_mtime <= os.stat(py).st_mtime:
|
||||||
|
print("Skipping %s; already compiled." % py)
|
||||||
|
else:
|
||||||
|
cmd = '%s %s > %s' % (compiler, ui, py)
|
||||||
|
print(cmd)
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, shell=True)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
os.remove(py)
|
||||||
|
34
tools/release_instructions.md
Normal file
34
tools/release_instructions.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
PyQtGraph Release Procedure
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
1. Create a release-x.x.x branch
|
||||||
|
|
||||||
|
2. Run pyqtgraph/tools/pg-release.py script (this has only been tested on linux)
|
||||||
|
- creates clone of master
|
||||||
|
- merges release branch into master
|
||||||
|
- updates version numbers in code
|
||||||
|
- creates pyqtgraph-x.x.x tag
|
||||||
|
- creates release commit
|
||||||
|
- builds documentation
|
||||||
|
- builds source package
|
||||||
|
- tests pip install
|
||||||
|
- builds windows .exe installers (note: it may be necessary to manually
|
||||||
|
copy wininst*.exe files from the python source packages)
|
||||||
|
- builds deb package (note: official debian packages are built elsewhere;
|
||||||
|
these locally-built deb packages may be phased out)
|
||||||
|
|
||||||
|
3. test build files
|
||||||
|
- test setup.py, pip on OSX
|
||||||
|
- test setup.py, pip, 32/64 exe on windows
|
||||||
|
- test setup.py, pip, deb on linux (py2, py3)
|
||||||
|
|
||||||
|
4. Run pg-release.py script again with --publish flag
|
||||||
|
- website upload
|
||||||
|
- github push + release
|
||||||
|
- pip upload
|
||||||
|
|
||||||
|
5. publish
|
||||||
|
- update website
|
||||||
|
- mailing list announcement
|
||||||
|
- new conda recipe (http://conda.pydata.org/docs/build.html)
|
||||||
|
- contact various package maintainers
|
@ -1,26 +0,0 @@
|
|||||||
import re, os, sys
|
|
||||||
|
|
||||||
version = sys.argv[1]
|
|
||||||
|
|
||||||
replace = [
|
|
||||||
("pyqtgraph/__init__.py", r"__version__ = .*", "__version__ = '%s'" % version),
|
|
||||||
#("setup.py", r" version=.*,", " version='%s'," % version), # setup.py automatically detects version
|
|
||||||
("doc/source/conf.py", r"version = .*", "version = '%s'" % version),
|
|
||||||
("doc/source/conf.py", r"release = .*", "release = '%s'" % version),
|
|
||||||
#("tools/debian/control", r"^Version: .*", "Version: %s" % version)
|
|
||||||
]
|
|
||||||
|
|
||||||
path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
|
|
||||||
|
|
||||||
for filename, search, sub in replace:
|
|
||||||
filename = os.path.join(path, filename)
|
|
||||||
data = open(filename, 'r').read()
|
|
||||||
if re.search(search, data) is None:
|
|
||||||
print('Error: Search expression "%s" not found in file %s.' % (search, filename))
|
|
||||||
os._exit(1)
|
|
||||||
open(filename, 'w').write(re.sub(search, sub, data))
|
|
||||||
|
|
||||||
print("Updated version strings to %s" % version)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -358,18 +358,33 @@ def getGitVersion(tagPrefix):
|
|||||||
if not os.path.isdir(os.path.join(path, '.git')):
|
if not os.path.isdir(os.path.join(path, '.git')):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
gitVersion = check_output(['git', 'describe', '--tags']).strip().decode('utf-8')
|
v = check_output(['git', 'describe', '--tags', '--dirty', '--match=%s*'%tagPrefix]).strip().decode('utf-8')
|
||||||
|
|
||||||
# any uncommitted modifications?
|
# chop off prefix
|
||||||
|
assert v.startswith(tagPrefix)
|
||||||
|
v = v[len(tagPrefix):]
|
||||||
|
|
||||||
|
# split up version parts
|
||||||
|
parts = v.split('-')
|
||||||
|
|
||||||
|
# has working tree been modified?
|
||||||
modified = False
|
modified = False
|
||||||
status = check_output(['git', 'status', '--porcelain'], universal_newlines=True).strip().split('\n')
|
if parts[-1] == 'dirty':
|
||||||
for line in status:
|
|
||||||
if line != '' and line[:2] != '??':
|
|
||||||
modified = True
|
modified = True
|
||||||
break
|
parts = parts[:-1]
|
||||||
|
|
||||||
|
# have commits been added on top of last tagged version?
|
||||||
|
# (git describe adds -NNN-gXXXXXXX if this is the case)
|
||||||
|
local = None
|
||||||
|
if len(parts) > 2 and re.match(r'\d+', parts[-2]) and re.match(r'g[0-9a-f]{7}', parts[-1]):
|
||||||
|
local = parts[-1]
|
||||||
|
parts = parts[:-2]
|
||||||
|
|
||||||
|
gitVersion = '-'.join(parts)
|
||||||
|
if local is not None:
|
||||||
|
gitVersion += '+' + local
|
||||||
if modified:
|
if modified:
|
||||||
gitVersion = gitVersion + '+'
|
gitVersion += 'm'
|
||||||
|
|
||||||
return gitVersion
|
return gitVersion
|
||||||
|
|
||||||
@ -393,11 +408,11 @@ def getVersionStrings(pkg):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
## Determine current version string from __init__.py
|
## Determine current version string from __init__.py
|
||||||
initVersion = getInitVersion(pkgroot='pyqtgraph')
|
initVersion = getInitVersion(pkgroot=pkg)
|
||||||
|
|
||||||
## If this is a git checkout, try to generate a more descriptive version string
|
## If this is a git checkout, try to generate a more descriptive version string
|
||||||
try:
|
try:
|
||||||
gitVersion = getGitVersion(tagPrefix='pyqtgraph-')
|
gitVersion = getGitVersion(tagPrefix=pkg+'-')
|
||||||
except:
|
except:
|
||||||
gitVersion = None
|
gitVersion = None
|
||||||
sys.stderr.write("This appears to be a git checkout, but an error occurred "
|
sys.stderr.write("This appears to be a git checkout, but an error occurred "
|
||||||
|
38
tools/shell.py
Normal file
38
tools/shell.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import os, sys
|
||||||
|
import subprocess as sp
|
||||||
|
|
||||||
|
|
||||||
|
def shell(cmd):
|
||||||
|
"""Run each line of a shell script; raise an exception if any line returns
|
||||||
|
a nonzero value.
|
||||||
|
"""
|
||||||
|
pin, pout = os.pipe()
|
||||||
|
proc = sp.Popen('/bin/bash', stdin=sp.PIPE)
|
||||||
|
for line in cmd.split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith('#'):
|
||||||
|
print('\033[33m> ' + line + '\033[0m')
|
||||||
|
else:
|
||||||
|
print('\033[32m> ' + line + '\033[0m')
|
||||||
|
if line.startswith('cd '):
|
||||||
|
os.chdir(line[3:])
|
||||||
|
proc.stdin.write((line + '\n').encode('utf-8'))
|
||||||
|
proc.stdin.write(('echo $? 1>&%d\n' % pout).encode('utf-8'))
|
||||||
|
ret = ""
|
||||||
|
while not ret.endswith('\n'):
|
||||||
|
ret += os.read(pin, 1)
|
||||||
|
ret = int(ret.strip())
|
||||||
|
if ret != 0:
|
||||||
|
print("\033[31mLast command returned %d; bailing out.\033[0m" % ret)
|
||||||
|
sys.exit(-1)
|
||||||
|
proc.stdin.close()
|
||||||
|
proc.wait()
|
||||||
|
|
||||||
|
|
||||||
|
def ssh(host, cmd):
|
||||||
|
"""Run commands on a remote host by ssh.
|
||||||
|
"""
|
||||||
|
proc = sp.Popen(['ssh', host], stdin=sp.PIPE)
|
||||||
|
proc.stdin.write(cmd)
|
||||||
|
proc.wait()
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user