Move and Update test-data repo into pyqtgraph repo
To reduce complexity, and make it easier to add more images and tests, the images in the `test-data` repository should be merged with the main repository. Furthermore, we can remove a lot of the subprocess work in the image_testing.py file, as we no longer need to have it interact with git. The images are not the same. Images were regenerated with Qt6, and now have proper big and little endian handling thanks to @pijyoi Second commit is a slightly modified variant of 2e135ab282d6007b34a3854921be54d0e9efb241 authored by @pijyoi it is to convert qimages to RGBA8888 for testing. Image files were regenerated images for the big/little handling Fixed issue with bogus test from test_NonUniformImage and generated a new image
7
.github/workflows/main.yml
vendored
@ -40,11 +40,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Checkout test-data
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
repository: pyqtgraph/test-data
|
|
||||||
path: .pyqtgraph/test-data
|
|
||||||
- name: Setup Python ${{ matrix.python-version }}
|
- name: Setup Python ${{ matrix.python-version }}
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
@ -106,7 +101,7 @@ jobs:
|
|||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: |
|
run: |
|
||||||
mkdir $SCREENSHOT_DIR
|
mkdir $SCREENSHOT_DIR
|
||||||
pytest pyqtgraph examples -v \
|
pytest tests examples -v \
|
||||||
--junitxml pytest.xml \
|
--junitxml pytest.xml \
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Upload Screenshots
|
- name: Upload Screenshots
|
||||||
|
@ -1511,17 +1511,10 @@ def makeQImage(imgData, alpha=None, copy=True, transpose=True):
|
|||||||
return ndarray_to_qimage(imgData, imgFormat)
|
return ndarray_to_qimage(imgData, imgFormat)
|
||||||
|
|
||||||
|
|
||||||
def imageToArray(img, copy=False, transpose=True):
|
def qimage_to_ndarray(qimg):
|
||||||
"""
|
img_ptr = qimg.bits()
|
||||||
Convert a QImage into numpy array. The image must have format RGB32, ARGB32, or ARGB32_Premultiplied.
|
|
||||||
By default, the image is not copied; changes made to the array will appear in the QImage as well (beware: if
|
|
||||||
the QImage is collected before the array, there may be trouble).
|
|
||||||
The array will have shape (width, height, (b,g,r,a)).
|
|
||||||
"""
|
|
||||||
fmt = img.format()
|
|
||||||
img_ptr = img.bits()
|
|
||||||
|
|
||||||
if QT_LIB.startswith('PyQt'):
|
if hasattr(img_ptr, 'setsize'): # PyQt sip.voidptr
|
||||||
# sizeInBytes() was introduced in Qt 5.10
|
# sizeInBytes() was introduced in Qt 5.10
|
||||||
# however PyQt5 5.12 will fail with:
|
# however PyQt5 5.12 will fail with:
|
||||||
# "TypeError: QImage.sizeInBytes() is a private method"
|
# "TypeError: QImage.sizeInBytes() is a private method"
|
||||||
@ -1529,14 +1522,37 @@ def imageToArray(img, copy=False, transpose=True):
|
|||||||
# PyQt5 5.15, PySide2 5.12, PySide2 5.15
|
# PyQt5 5.15, PySide2 5.12, PySide2 5.15
|
||||||
try:
|
try:
|
||||||
# 64-bits size
|
# 64-bits size
|
||||||
nbytes = img.sizeInBytes()
|
nbytes = qimg.sizeInBytes()
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError):
|
||||||
# 32-bits size
|
# 32-bits size
|
||||||
nbytes = img.byteCount()
|
nbytes = qimg.byteCount()
|
||||||
img_ptr.setsize(nbytes)
|
img_ptr.setsize(nbytes)
|
||||||
|
|
||||||
arr = np.frombuffer(img_ptr, dtype=np.ubyte)
|
depth = qimg.depth()
|
||||||
arr = arr.reshape(img.height(), img.width(), 4)
|
if depth in (8, 24, 32):
|
||||||
|
dtype = np.uint8
|
||||||
|
nchan = depth // 8
|
||||||
|
elif depth in (16, 64):
|
||||||
|
dtype = np.uint16
|
||||||
|
nchan = depth // 16
|
||||||
|
else:
|
||||||
|
raise ValueError("Unsupported Image Type")
|
||||||
|
shape = qimg.height(), qimg.width()
|
||||||
|
if nchan != 1:
|
||||||
|
shape = shape + (nchan,)
|
||||||
|
return np.frombuffer(img_ptr, dtype=dtype).reshape(shape)
|
||||||
|
|
||||||
|
|
||||||
|
def imageToArray(img, copy=False, transpose=True):
|
||||||
|
"""
|
||||||
|
Convert a QImage into numpy array. The image must have format RGB32, ARGB32, or ARGB32_Premultiplied.
|
||||||
|
By default, the image is not copied; changes made to the array will appear in the QImage as well (beware: if
|
||||||
|
the QImage is collected before the array, there may be trouble).
|
||||||
|
The array will have shape (width, height, (b,g,r,a)).
|
||||||
|
"""
|
||||||
|
arr = qimage_to_ndarray(img)
|
||||||
|
|
||||||
|
fmt = img.format()
|
||||||
if fmt == img.Format_RGB32:
|
if fmt == img.Format_RGB32:
|
||||||
arr[...,3] = 255
|
arr[...,3] = 255
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ xvfb_height = 1080
|
|||||||
# use this due to some issues with ndarray reshape errors on CI systems
|
# use this due to some issues with ndarray reshape errors on CI systems
|
||||||
xvfb_colordepth = 24
|
xvfb_colordepth = 24
|
||||||
xvfb_args=-ac +extension GLX +render
|
xvfb_args=-ac +extension GLX +render
|
||||||
faulthandler_timeout = 30
|
faulthandler_timeout = 60
|
||||||
|
|
||||||
filterwarnings =
|
filterwarnings =
|
||||||
# re-enable standard library warnings
|
# re-enable standard library warnings
|
||||||
|
@ -5,7 +5,7 @@ import pytest
|
|||||||
from pyqtgraph.Qt import QtCore, QtGui, QtTest
|
from pyqtgraph.Qt import QtCore, QtGui, QtTest
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.tests import assertImageApproved, TransposedImageItem
|
from tests.image_testing import assertImageApproved, TransposedImageItem
|
||||||
try:
|
try:
|
||||||
import cupy
|
import cupy
|
||||||
except ImportError:
|
except ImportError:
|
@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.Qt import QtGui, QtCore, QtTest
|
from pyqtgraph.Qt import QtGui, QtCore, QtTest
|
||||||
from pyqtgraph.tests import mouseDrag, mouseMove
|
from tests.ui_testing import mouseDrag, mouseMove
|
||||||
pg.mkQApp()
|
pg.mkQApp()
|
||||||
|
|
||||||
|
|
@ -2,7 +2,7 @@ import numpy as np
|
|||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.Qt import QtTest
|
from pyqtgraph.Qt import QtTest
|
||||||
from pyqtgraph.graphicsItems.NonUniformImage import NonUniformImage
|
from pyqtgraph.graphicsItems.NonUniformImage import NonUniformImage
|
||||||
from pyqtgraph.tests import assertImageApproved
|
from tests.image_testing import assertImageApproved
|
||||||
from pyqtgraph.colormap import ColorMap
|
from pyqtgraph.colormap import ColorMap
|
||||||
import pyqtgraph.functions as fn
|
import pyqtgraph.functions as fn
|
||||||
import pytest
|
import pytest
|
||||||
@ -93,7 +93,7 @@ def test_NonUniformImage_colormap():
|
|||||||
|
|
||||||
image = NonUniformImage(x, y, Z, border=fn.mkPen('g'))
|
image = NonUniformImage(x, y, Z, border=fn.mkPen('g'))
|
||||||
|
|
||||||
cmap = ColorMap(pos=[0.0, 1.0], color=[(0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)])
|
cmap = ColorMap(pos=[0.0, 1.0], color=[(0, 0, 0), (255, 255, 255)])
|
||||||
image.setColorMap(cmap)
|
image.setColorMap(cmap)
|
||||||
|
|
||||||
viewbox.addItem(image)
|
viewbox.addItem(image)
|
@ -1,6 +1,6 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
from pyqtgraph.tests import assertImageApproved
|
from tests.image_testing import assertImageApproved
|
||||||
|
|
||||||
|
|
||||||
def test_PlotCurveItem():
|
def test_PlotCurveItem():
|
@ -3,9 +3,10 @@ import sys
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pytest
|
import pytest
|
||||||
import pyqtgraph as pg
|
import pyqtgraph as pg
|
||||||
|
import platform
|
||||||
from pyqtgraph.Qt import QtCore, QtGui, QtTest
|
from pyqtgraph.Qt import QtCore, QtGui, QtTest
|
||||||
from pyqtgraph.tests import assertImageApproved, mouseMove, mouseDrag, mouseClick, TransposedImageItem, resizeWindow
|
from tests.image_testing import assertImageApproved
|
||||||
import pytest
|
from tests.ui_testing import mouseMove, mouseDrag, mouseClick, resizeWindow
|
||||||
|
|
||||||
app = pg.mkQApp()
|
app = pg.mkQApp()
|
||||||
pg.setConfigOption("mouseRateLimit", 0)
|
pg.setConfigOption("mouseRateLimit", 0)
|
||||||
@ -39,17 +40,20 @@ def test_getArrayRegion_axisorder():
|
|||||||
|
|
||||||
|
|
||||||
def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
||||||
|
# on windows, edges corner pixels seem to be slightly different from other platforms
|
||||||
|
# giving a pxCount=2 for a fudge factor
|
||||||
|
if isinstance(roi, (pg.ROI, pg.RectROI)) and platform.system() == "Windows":
|
||||||
|
pxCount = 2
|
||||||
|
else:
|
||||||
|
pxCount=-1
|
||||||
|
|
||||||
|
|
||||||
initState = roi.getState()
|
initState = roi.getState()
|
||||||
|
|
||||||
#win = pg.GraphicsLayoutWidget()
|
|
||||||
win = pg.GraphicsView()
|
win = pg.GraphicsView()
|
||||||
win.show()
|
win.show()
|
||||||
resizeWindow(win, 200, 400)
|
resizeWindow(win, 200, 400)
|
||||||
# Don't use Qt's layouts for testing--these generate unpredictable results.
|
# Don't use Qt's layouts for testing--these generate unpredictable results.
|
||||||
#vb1 = win.addViewBox()
|
|
||||||
#win.nextRow()
|
|
||||||
#vb2 = win.addViewBox()
|
|
||||||
|
|
||||||
# Instead, place the viewboxes manually
|
# Instead, place the viewboxes manually
|
||||||
vb1 = pg.ViewBox()
|
vb1 = pg.ViewBox()
|
||||||
win.scene().addItem(vb1)
|
win.scene().addItem(vb1)
|
||||||
@ -97,7 +101,7 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
|||||||
vb2.enableAutoRange(True, True)
|
vb2.enableAutoRange(True, True)
|
||||||
|
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion', 'Simple ROI region selection.')
|
assertImageApproved(win, name+'/roi_getarrayregion', 'Simple ROI region selection.', pxCount=pxCount)
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
roi.setPos(0, False)
|
roi.setPos(0, False)
|
||||||
@ -106,38 +110,33 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
|||||||
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
||||||
img2.setImage(rgn[0, ..., 0])
|
img2.setImage(rgn[0, ..., 0])
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion_halfpx', 'Simple ROI region selection, 0.5 pixel shift.')
|
assertImageApproved(win, name+'/roi_getarrayregion_halfpx', 'Simple ROI region selection, 0.5 pixel shift.', pxCount=pxCount)
|
||||||
|
|
||||||
roi.setAngle(45)
|
roi.setAngle(45)
|
||||||
roi.setPos([3, 0])
|
roi.setPos([3, 0])
|
||||||
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
||||||
img2.setImage(rgn[0, ..., 0])
|
img2.setImage(rgn[0, ..., 0])
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion_rotate', 'Simple ROI region selection, rotation.')
|
assertImageApproved(win, name+'/roi_getarrayregion_rotate', 'Simple ROI region selection, rotation.', pxCount=pxCount)
|
||||||
|
|
||||||
if testResize:
|
if testResize:
|
||||||
roi.setSize([60, 60])
|
roi.setSize([60, 60])
|
||||||
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
||||||
img2.setImage(rgn[0, ..., 0])
|
img2.setImage(rgn[0, ..., 0])
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion_resize', 'Simple ROI region selection, resized.')
|
assertImageApproved(win, name+'/roi_getarrayregion_resize', 'Simple ROI region selection, resized.', pxCount=pxCount)
|
||||||
|
|
||||||
img1.setPos(0, img1.height())
|
img1.setPos(0, img1.height())
|
||||||
img1.setTransform(QtGui.QTransform().scale(1, -1).rotate(20), True)
|
img1.setTransform(QtGui.QTransform().scale(1, -1).rotate(20), True)
|
||||||
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
||||||
img2.setImage(rgn[0, ..., 0])
|
img2.setImage(rgn[0, ..., 0])
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion_img_trans', 'Simple ROI region selection, image transformed.')
|
assertImageApproved(win, name+'/roi_getarrayregion_img_trans', 'Simple ROI region selection, image transformed.', pxCount=pxCount)
|
||||||
|
|
||||||
vb1.invertY()
|
vb1.invertY()
|
||||||
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
||||||
img2.setImage(rgn[0, ..., 0])
|
img2.setImage(rgn[0, ..., 0])
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
# on windows, one edge of one ROI handle is shifted slightly; letting this slide with pxCount=10
|
|
||||||
if pg.Qt.QT_LIB in {'PyQt4', 'PySide'}:
|
|
||||||
pxCount = 10
|
|
||||||
else:
|
|
||||||
pxCount=-1
|
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion_inverty', 'Simple ROI region selection, view inverted.', pxCount=pxCount)
|
assertImageApproved(win, name+'/roi_getarrayregion_inverty', 'Simple ROI region selection, view inverted.', pxCount=pxCount)
|
||||||
|
|
||||||
roi.setState(initState)
|
roi.setState(initState)
|
||||||
@ -146,7 +145,7 @@ def check_getArrayRegion(roi, name, testResize=True, transpose=False):
|
|||||||
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
rgn = roi.getArrayRegion(data, img1, axes=(1, 2))
|
||||||
img2.setImage(rgn[0, ..., 0])
|
img2.setImage(rgn[0, ..., 0])
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
assertImageApproved(win, name+'/roi_getarrayregion_anisotropic', 'Simple ROI region selection, image scaled anisotropically.')
|
assertImageApproved(win, name+'/roi_getarrayregion_anisotropic', 'Simple ROI region selection, image scaled anisotropically.', pxCount=pxCount)
|
||||||
|
|
||||||
# allow the roi to be re-used
|
# allow the roi to be re-used
|
||||||
roi.scene().removeItem(roi)
|
roi.scene().removeItem(roi)
|
@ -3,68 +3,32 @@
|
|||||||
"""
|
"""
|
||||||
Procedure for unit-testing with images:
|
Procedure for unit-testing with images:
|
||||||
|
|
||||||
1. Run unit tests at least once; this initializes a git clone of
|
Run individual test scripts with the PYQTGRAPH_AUDIT environment variable set:
|
||||||
pyqtgraph/test-data in ~/.pyqtgraph.
|
|
||||||
|
|
||||||
2. Run individual test scripts with the PYQTGRAPH_AUDIT environment variable set:
|
|
||||||
|
|
||||||
$ PYQTGRAPH_AUDIT=1 python pyqtgraph/graphicsItems/tests/test_PlotCurveItem.py
|
$ PYQTGRAPH_AUDIT=1 python pyqtgraph/graphicsItems/tests/test_PlotCurveItem.py
|
||||||
|
|
||||||
Any failing tests will display the test results, standard image, and the
|
Any failing tests will display the test results, standard image, and the
|
||||||
differences between the two. If the test result is bad, then press (f)ail.
|
differences between the two. If the test result is bad, then press (f)ail.
|
||||||
If the test result is good, then press (p)ass and the new image will be
|
If the test result is good, then press (p)ass and the new image will be
|
||||||
saved to the test-data directory.
|
saved to the test-data directory.
|
||||||
|
|
||||||
To check all test results regardless of whether the test failed, set the
|
|
||||||
environment variable PYQTGRAPH_AUDIT_ALL=1.
|
|
||||||
|
|
||||||
3. After adding or changing test images, create a new commit:
|
|
||||||
|
|
||||||
$ cd ~/.pyqtgraph/test-data
|
|
||||||
$ git add ...
|
|
||||||
$ git commit -a
|
|
||||||
|
|
||||||
4. Look up the most recent tag name from the `testDataTag` global variable
|
|
||||||
below. Increment the tag name by 1 and create a new tag in the test-data
|
|
||||||
repository:
|
|
||||||
|
|
||||||
$ git tag test-data-NNN
|
|
||||||
$ git push --tags origin master
|
|
||||||
|
|
||||||
This tag is used to ensure that each pyqtgraph commit is linked to a specific
|
|
||||||
commit in the test-data repository. This makes it possible to push new
|
|
||||||
commits to the test-data repository without interfering with existing
|
|
||||||
tests, and also allows unit tests to continue working on older pyqtgraph
|
|
||||||
versions.
|
|
||||||
|
|
||||||
|
To check all test results regardless of whether the test failed, set the
|
||||||
|
environment variable PYQTGRAPH_AUDIT_ALL=1.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# This is the name of a tag in the test-data repository that this version of
|
|
||||||
# pyqtgraph should be tested against. When adding or changing test images,
|
|
||||||
# create and push a new tag and update this variable. To test locally, begin
|
|
||||||
# by creating the tag in your ~/.pyqtgraph/test-data repository.
|
|
||||||
testDataTag = 'test-data-8'
|
|
||||||
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
import base64
|
import warnings
|
||||||
import subprocess as sp
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
if sys.version[0] >= '3':
|
from pathlib import Path
|
||||||
import http.client as httplib
|
|
||||||
import urllib.parse as urllib
|
from pyqtgraph.Qt import QtGui, QtCore
|
||||||
else:
|
from pyqtgraph import functions as fn
|
||||||
import httplib
|
from pyqtgraph import GraphicsLayoutWidget
|
||||||
import urllib
|
from pyqtgraph import ImageItem, TextItem
|
||||||
from ..Qt import QtGui, QtCore, QtTest, QT_LIB
|
|
||||||
from .. import functions as fn
|
|
||||||
from .. import GraphicsLayoutWidget
|
|
||||||
from .. import ImageItem, TextItem
|
|
||||||
|
|
||||||
|
|
||||||
tester = None
|
tester = None
|
||||||
@ -101,6 +65,21 @@ def getTester():
|
|||||||
return tester
|
return tester
|
||||||
|
|
||||||
|
|
||||||
|
def getImageFromWidget(widget):
|
||||||
|
|
||||||
|
# just to be sure the widget size is correct (new window may be resized):
|
||||||
|
QtGui.QApplication.processEvents()
|
||||||
|
|
||||||
|
qimg = QtGui.QImage(widget.size(), QtGui.QImage.Format.Format_ARGB32)
|
||||||
|
qimg.fill(QtCore.Qt.GlobalColor.transparent)
|
||||||
|
painter = QtGui.QPainter(qimg)
|
||||||
|
widget.render(painter)
|
||||||
|
painter.end()
|
||||||
|
|
||||||
|
qimg = qimg.convertToFormat(QtGui.QImage.Format.Format_RGBA8888)
|
||||||
|
return fn.qimage_to_ndarray(qimg).copy()
|
||||||
|
|
||||||
|
|
||||||
def assertImageApproved(image, standardFile, message=None, **kwargs):
|
def assertImageApproved(image, standardFile, message=None, **kwargs):
|
||||||
"""Check that an image test result matches a pre-approved standard.
|
"""Check that an image test result matches a pre-approved standard.
|
||||||
|
|
||||||
@ -108,10 +87,6 @@ def assertImageApproved(image, standardFile, message=None, **kwargs):
|
|||||||
to compare the images and decide whether to fail the test or save the new
|
to compare the images and decide whether to fail the test or save the new
|
||||||
image as the standard.
|
image as the standard.
|
||||||
|
|
||||||
This function will automatically clone the test-data repository into
|
|
||||||
~/.pyqtgraph/test-data. However, it is up to the user to ensure this repository
|
|
||||||
is kept up to date and to commit/push new images after they are saved.
|
|
||||||
|
|
||||||
Run the test with the environment variable PYQTGRAPH_AUDIT=1 to bring up
|
Run the test with the environment variable PYQTGRAPH_AUDIT=1 to bring up
|
||||||
the auditing GUI.
|
the auditing GUI.
|
||||||
|
|
||||||
@ -131,43 +106,28 @@ def assertImageApproved(image, standardFile, message=None, **kwargs):
|
|||||||
comparison (see ``assertImageMatch()``).
|
comparison (see ``assertImageMatch()``).
|
||||||
"""
|
"""
|
||||||
if isinstance(image, QtGui.QWidget):
|
if isinstance(image, QtGui.QWidget):
|
||||||
w = image
|
# just to be sure the widget size is correct (new window may be resized):
|
||||||
|
|
||||||
# just to be sure the widget size is correct (new window may be resized):
|
|
||||||
QtGui.QApplication.processEvents()
|
QtGui.QApplication.processEvents()
|
||||||
|
|
||||||
graphstate = scenegraphState(w, standardFile)
|
graphstate = scenegraphState(image, standardFile)
|
||||||
qimg = QtGui.QImage(w.size(), QtGui.QImage.Format.Format_ARGB32)
|
image = getImageFromWidget(image)
|
||||||
qimg.fill(QtCore.Qt.GlobalColor.transparent)
|
|
||||||
painter = QtGui.QPainter(qimg)
|
|
||||||
w.render(painter)
|
|
||||||
painter.end()
|
|
||||||
|
|
||||||
image = fn.imageToArray(qimg, copy=False, transpose=False)
|
|
||||||
|
|
||||||
# the standard images seem to have their Red and Blue swapped
|
|
||||||
if sys.byteorder == 'little':
|
|
||||||
# transpose B,G,R,A to R,G,B,A
|
|
||||||
image = image[..., [2, 1, 0, 3]]
|
|
||||||
else:
|
|
||||||
# transpose A,R,G,B to A,B,G,R
|
|
||||||
image = image[..., [0, 3, 2, 1]]
|
|
||||||
|
|
||||||
if message is None:
|
if message is None:
|
||||||
code = inspect.currentframe().f_back.f_code
|
code = inspect.currentframe().f_back.f_code
|
||||||
message = "%s::%s" % (code.co_filename, code.co_name)
|
message = "%s::%s" % (code.co_filename, code.co_name)
|
||||||
|
|
||||||
# Make sure we have a test data repo available, possibly invoking git
|
# Make sure we have a test data repo available
|
||||||
dataPath = getTestDataRepo()
|
dataPath = getTestDataDirectory()
|
||||||
|
|
||||||
# Read the standard image if it exists
|
# Read the standard image if it exists
|
||||||
stdFileName = os.path.join(dataPath, standardFile + '.png')
|
stdFileName = os.path.join(dataPath, standardFile + '.png')
|
||||||
if not os.path.isfile(stdFileName):
|
if not os.path.isfile(stdFileName):
|
||||||
stdImage = None
|
stdImage = None
|
||||||
else:
|
else:
|
||||||
pxm = QtGui.QPixmap()
|
qimg = QtGui.QImage(stdFileName)
|
||||||
pxm.load(stdFileName)
|
qimg = qimg.convertToFormat(QtGui.QImage.Format.Format_RGBA8888)
|
||||||
stdImage = fn.imageToArray(pxm.toImage(), copy=True, transpose=False)
|
stdImage = fn.qimage_to_ndarray(qimg).copy()
|
||||||
|
del qimg
|
||||||
|
|
||||||
# If the test image does not match, then we go to audit if requested.
|
# If the test image does not match, then we go to audit if requested.
|
||||||
try:
|
try:
|
||||||
@ -191,18 +151,13 @@ def assertImageApproved(image, standardFile, message=None, **kwargs):
|
|||||||
image = fn.downsample(image, sr[0], axis=(0, 1)).astype(image.dtype)
|
image = fn.downsample(image, sr[0], axis=(0, 1)).astype(image.dtype)
|
||||||
|
|
||||||
assertImageMatch(image, stdImage, **kwargs)
|
assertImageMatch(image, stdImage, **kwargs)
|
||||||
|
|
||||||
if bool(os.getenv('PYQTGRAPH_PRINT_TEST_STATE', False)):
|
if bool(os.getenv('PYQTGRAPH_PRINT_TEST_STATE', False)):
|
||||||
print(graphstate)
|
print(graphstate)
|
||||||
|
|
||||||
if os.getenv('PYQTGRAPH_AUDIT_ALL') == '1':
|
if os.getenv('PYQTGRAPH_AUDIT_ALL') == '1':
|
||||||
raise Exception("Image test passed, but auditing due to PYQTGRAPH_AUDIT_ALL evnironment variable.")
|
raise Exception("Image test passed, but auditing due to PYQTGRAPH_AUDIT_ALL evnironment variable.")
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
||||||
if stdFileName in gitStatus(dataPath):
|
|
||||||
print("\n\nWARNING: unit test failed against modified standard "
|
|
||||||
"image %s.\nTo revert this file, run `cd %s; git checkout "
|
|
||||||
"%s`\n" % (stdFileName, dataPath, standardFile))
|
|
||||||
if os.getenv('PYQTGRAPH_AUDIT') == '1' or os.getenv('PYQTGRAPH_AUDIT_ALL') == '1':
|
if os.getenv('PYQTGRAPH_AUDIT') == '1' or os.getenv('PYQTGRAPH_AUDIT_ALL') == '1':
|
||||||
sys.excepthook(*sys.exc_info())
|
sys.excepthook(*sys.exc_info())
|
||||||
getTester().test(image, stdImage, message)
|
getTester().test(image, stdImage, message)
|
||||||
@ -210,20 +165,18 @@ def assertImageApproved(image, standardFile, message=None, **kwargs):
|
|||||||
print('Saving new standard image to "%s"' % stdFileName)
|
print('Saving new standard image to "%s"' % stdFileName)
|
||||||
if not os.path.isdir(stdPath):
|
if not os.path.isdir(stdPath):
|
||||||
os.makedirs(stdPath)
|
os.makedirs(stdPath)
|
||||||
img = fn.makeQImage(image, alpha=True, transpose=False)
|
qimg = fn.ndarray_to_qimage(image, QtGui.QImage.Format.Format_RGBA8888)
|
||||||
img.save(stdFileName)
|
qimg.save(stdFileName)
|
||||||
|
del qimg
|
||||||
else:
|
else:
|
||||||
if stdImage is None:
|
if stdImage is None:
|
||||||
raise Exception("Test standard %s does not exist. Set "
|
raise Exception("Test standard %s does not exist. Set "
|
||||||
"PYQTGRAPH_AUDIT=1 to add this image." % stdFileName)
|
"PYQTGRAPH_AUDIT=1 to add this image." % stdFileName)
|
||||||
else:
|
if os.getenv('CI') is not None:
|
||||||
if os.getenv('TRAVIS') is not None:
|
standardFile = os.path.join(os.getenv("SCREENSHOT_DIR", "screenshots"), standardFile)
|
||||||
saveFailedTest(image, stdImage, standardFile, upload=True)
|
saveFailedTest(image, stdImage, standardFile)
|
||||||
elif os.getenv('CI') is not None:
|
print(graphstate)
|
||||||
standardFile = os.path.join(os.getenv("SCREENSHOT_DIR", "screenshots"), standardFile)
|
raise
|
||||||
saveFailedTest(image, stdImage, standardFile)
|
|
||||||
print(graphstate)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
||||||
@ -249,8 +202,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. Default is 0 for Qt4 and
|
Maximum number of pixels that may differ. Default is 0, on Windows some
|
||||||
1% of image size for Qt5.
|
tests have a value of 2.
|
||||||
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
|
||||||
@ -264,12 +217,7 @@ def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
|||||||
assert im1.dtype == im2.dtype
|
assert im1.dtype == im2.dtype
|
||||||
|
|
||||||
if pxCount == -1:
|
if pxCount == -1:
|
||||||
if QT_LIB in {'PyQt5', 'PySide2', 'PySide6', 'PyQt6'}:
|
pxCount = 0
|
||||||
# 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:
|
||||||
@ -292,9 +240,7 @@ def assertImageMatch(im1, im2, minCorr=None, pxThreshold=50.,
|
|||||||
assert corr >= minCorr
|
assert corr >= minCorr
|
||||||
|
|
||||||
|
|
||||||
def saveFailedTest(data, expect, filename, upload=False):
|
def saveFailedTest(data, expect, filename):
|
||||||
"""Upload failed test images to web server to allow CI test debugging.
|
|
||||||
"""
|
|
||||||
# concatenate data, expect, and diff into a single image
|
# concatenate data, expect, and diff into a single image
|
||||||
ds = data.shape
|
ds = data.shape
|
||||||
es = expect.shape
|
es = expect.shape
|
||||||
@ -310,7 +256,7 @@ def saveFailedTest(data, expect, filename, upload=False):
|
|||||||
diff = makeDiffImage(data, expect)
|
diff = makeDiffImage(data, expect)
|
||||||
img[2:2+diff.shape[0], -diff.shape[1]-2:-2] = diff
|
img[2:2+diff.shape[0], -diff.shape[1]-2:-2] = diff
|
||||||
|
|
||||||
png = makePng(img)
|
png = makePng(data) # change `img` to `data` to save just the failed image
|
||||||
directory = os.path.dirname(filename)
|
directory = os.path.dirname(filename)
|
||||||
if not os.path.isdir(directory):
|
if not os.path.isdir(directory):
|
||||||
os.makedirs(directory)
|
os.makedirs(directory)
|
||||||
@ -318,38 +264,15 @@ def saveFailedTest(data, expect, filename, upload=False):
|
|||||||
png_file.write(png)
|
png_file.write(png)
|
||||||
print("\nImage comparison failed. Test result: %s %s Expected result: "
|
print("\nImage comparison failed. Test result: %s %s Expected result: "
|
||||||
"%s %s" % (data.shape, data.dtype, expect.shape, expect.dtype))
|
"%s %s" % (data.shape, data.dtype, expect.shape, expect.dtype))
|
||||||
if upload:
|
|
||||||
uploadFailedTest(filename, png)
|
|
||||||
|
|
||||||
|
|
||||||
def uploadFailedTest(filename, png):
|
|
||||||
commit = runSubprocess(['git', 'rev-parse', 'HEAD'])
|
|
||||||
name = filename.split(os.path.sep)
|
|
||||||
name.insert(-1, commit.strip())
|
|
||||||
filename = os.path.sep.join(name)
|
|
||||||
|
|
||||||
host = 'data.pyqtgraph.org'
|
|
||||||
conn = httplib.HTTPConnection(host)
|
|
||||||
req = urllib.urlencode({'name': filename,
|
|
||||||
'data': base64.b64encode(png)})
|
|
||||||
conn.request('POST', '/upload.py', req)
|
|
||||||
response = conn.getresponse().read()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
print("Uploaded to: \nhttp://%s/data/%s" % (host, filename))
|
|
||||||
if not response.startswith(b'OK'):
|
|
||||||
print("WARNING: Error uploading data to %s" % host)
|
|
||||||
print(response)
|
|
||||||
|
|
||||||
|
|
||||||
def makePng(img):
|
def makePng(img):
|
||||||
"""Given an array like (H, W, 4), return a PNG-encoded byte string.
|
"""Given an array like (H, W, 4), return a PNG-encoded byte string.
|
||||||
"""
|
"""
|
||||||
io = QtCore.QBuffer()
|
io = QtCore.QBuffer()
|
||||||
qim = fn.makeQImage(img.transpose(1, 0, 2), alpha=False)
|
qim = fn.ndarray_to_qimage(img, QtGui.QImage.Format.Format_RGBX8888)
|
||||||
qim.save(io, 'PNG')
|
qim.save(io, 'PNG')
|
||||||
png = bytes(io.data().data())
|
return bytes(io.data().data())
|
||||||
return png
|
|
||||||
|
|
||||||
|
|
||||||
def makeDiffImage(im1, im2):
|
def makeDiffImage(im1, im2):
|
||||||
@ -467,155 +390,18 @@ class ImageTester(QtGui.QWidget):
|
|||||||
|
|
||||||
|
|
||||||
def getTestDataRepo():
|
def getTestDataRepo():
|
||||||
"""Return the path to a git repository with the required commit checked
|
warnings.warn(
|
||||||
out.
|
"Test data data repo has been merged with the main repo"
|
||||||
|
"use getTestDataDirectory() instead, this method will be removed"
|
||||||
If the repository does not exist, then it is cloned from
|
"in a future version of pyqtgraph",
|
||||||
https://github.com/pyqtgraph/test-data. If the repository already exists
|
DeprecationWarning, stacklevel=2
|
||||||
then the required commit is checked out.
|
)
|
||||||
"""
|
return getTestDataDirectory()
|
||||||
global testDataTag
|
|
||||||
|
|
||||||
if os.getenv("CI"):
|
|
||||||
dataPath = os.path.join(os.environ["GITHUB_WORKSPACE"], '.pyqtgraph', 'test-data')
|
|
||||||
else:
|
|
||||||
dataPath = os.path.join(os.path.expanduser('~'), '.pyqtgraph', 'test-data')
|
|
||||||
gitPath = 'https://github.com/pyqtgraph/test-data'
|
|
||||||
gitbase = gitCmdBase(dataPath)
|
|
||||||
|
|
||||||
if os.path.isdir(dataPath):
|
|
||||||
# Already have a test-data repository to work with.
|
|
||||||
|
|
||||||
# Get the commit ID of testDataTag. Do a fetch if necessary.
|
|
||||||
try:
|
|
||||||
tagCommit = gitCommitId(dataPath, testDataTag)
|
|
||||||
except NameError:
|
|
||||||
cmd = gitbase + ['fetch', '--tags', 'origin']
|
|
||||||
print(' '.join(cmd))
|
|
||||||
sp.check_call(cmd)
|
|
||||||
try:
|
|
||||||
tagCommit = gitCommitId(dataPath, testDataTag)
|
|
||||||
except NameError:
|
|
||||||
raise Exception("Could not find tag '%s' in test-data repo at"
|
|
||||||
" %s" % (testDataTag, dataPath))
|
|
||||||
except Exception:
|
|
||||||
if not os.path.exists(os.path.join(dataPath, '.git')):
|
|
||||||
raise Exception("Directory '%s' does not appear to be a git "
|
|
||||||
"repository. Please remove this directory." %
|
|
||||||
dataPath)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
# If HEAD is not the correct commit, then do a checkout
|
|
||||||
if gitCommitId(dataPath, 'HEAD') != tagCommit:
|
|
||||||
print("Checking out test-data tag '%s'" % testDataTag)
|
|
||||||
sp.check_call(gitbase + ['checkout', testDataTag])
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Attempting to create git clone of test data repo in %s.." %
|
|
||||||
dataPath)
|
|
||||||
|
|
||||||
parentPath = os.path.split(dataPath)[0]
|
|
||||||
if not os.path.isdir(parentPath):
|
|
||||||
os.makedirs(parentPath)
|
|
||||||
|
|
||||||
if os.getenv('TRAVIS') is not None or os.getenv('CI') is not None:
|
|
||||||
# Create a shallow clone of the test-data repository (to avoid
|
|
||||||
# downloading more data than is necessary)
|
|
||||||
os.makedirs(dataPath)
|
|
||||||
cmds = [
|
|
||||||
gitbase + ['init'],
|
|
||||||
gitbase + ['remote', 'add', 'origin', gitPath],
|
|
||||||
gitbase + ['fetch', '--tags', 'origin', testDataTag,
|
|
||||||
'--depth=1'],
|
|
||||||
gitbase + ['checkout', '-b', 'master', 'FETCH_HEAD'],
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
# Create a full clone
|
|
||||||
cmds = [['git', 'clone', gitPath, dataPath]]
|
|
||||||
|
|
||||||
for cmd in cmds:
|
|
||||||
print(' '.join(cmd))
|
|
||||||
rval = sp.check_call(cmd)
|
|
||||||
if rval == 0:
|
|
||||||
continue
|
|
||||||
raise RuntimeError("Test data path '%s' does not exist and could "
|
|
||||||
"not be created with git. Please create a git "
|
|
||||||
"clone of %s at this path." %
|
|
||||||
(dataPath, gitPath))
|
|
||||||
|
|
||||||
return dataPath
|
|
||||||
|
|
||||||
|
|
||||||
def gitCmdBase(path):
|
def getTestDataDirectory():
|
||||||
return ['git', '--git-dir=%s/.git' % path, '--work-tree=%s' % path]
|
dataPath = Path(__file__).absolute().parent / "images"
|
||||||
|
return dataPath.as_posix()
|
||||||
|
|
||||||
def gitStatus(path):
|
|
||||||
"""Return a string listing all changes to the working tree in a git
|
|
||||||
repository.
|
|
||||||
"""
|
|
||||||
cmd = gitCmdBase(path) + ['status', '--porcelain']
|
|
||||||
return runSubprocess(cmd, stderr=None, universal_newlines=True)
|
|
||||||
|
|
||||||
|
|
||||||
def gitCommitId(path, ref):
|
|
||||||
"""Return the commit id of *ref* in the git repository at *path*.
|
|
||||||
"""
|
|
||||||
cmd = gitCmdBase(path) + ['show', ref]
|
|
||||||
try:
|
|
||||||
output = runSubprocess(cmd, stderr=None, universal_newlines=True)
|
|
||||||
except sp.CalledProcessError:
|
|
||||||
print(cmd)
|
|
||||||
raise NameError("Unknown git reference '%s'" % ref)
|
|
||||||
commit = output.split('\n')[0]
|
|
||||||
assert commit[:7] == 'commit '
|
|
||||||
return commit[7:]
|
|
||||||
|
|
||||||
|
|
||||||
def runSubprocess(command, return_code=False, **kwargs):
|
|
||||||
"""Run command using subprocess.Popen
|
|
||||||
|
|
||||||
Similar to subprocess.check_output(), which is not available in 2.6.
|
|
||||||
|
|
||||||
Run command and wait for command to complete. If the return code was zero
|
|
||||||
then return, otherwise raise CalledProcessError.
|
|
||||||
By default, this will also add stdout= and stderr=subproces.PIPE
|
|
||||||
to the call to Popen to suppress printing to the terminal.
|
|
||||||
|
|
||||||
Parameters
|
|
||||||
----------
|
|
||||||
command : list of str
|
|
||||||
Command to run as subprocess (see subprocess.Popen documentation).
|
|
||||||
**kwargs : dict
|
|
||||||
Additional kwargs to pass to ``subprocess.Popen``.
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
stdout : str
|
|
||||||
Stdout returned by the process.
|
|
||||||
"""
|
|
||||||
# code adapted with permission from mne-python
|
|
||||||
use_kwargs = dict(stderr=None, stdout=sp.PIPE)
|
|
||||||
use_kwargs.update(kwargs)
|
|
||||||
|
|
||||||
p = sp.Popen(command, **use_kwargs)
|
|
||||||
output = p.communicate()[0]
|
|
||||||
|
|
||||||
# communicate() may return bytes, str, or None depending on the kwargs
|
|
||||||
# passed to Popen(). Convert all to unicode str:
|
|
||||||
output = '' if output is None else output
|
|
||||||
output = output.decode('utf-8') if isinstance(output, bytes) else output
|
|
||||||
|
|
||||||
if p.returncode != 0:
|
|
||||||
print(output)
|
|
||||||
err_fun = sp.CalledProcessError.__init__
|
|
||||||
if 'output' in inspect.getfullargspec(err_fun).args:
|
|
||||||
raise sp.CalledProcessError(p.returncode, command, output)
|
|
||||||
else:
|
|
||||||
raise sp.CalledProcessError(p.returncode, command)
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
|
|
||||||
def scenegraphState(view, name):
|
def scenegraphState(view, name):
|
||||||
@ -632,7 +418,7 @@ def scenegraphState(view, name):
|
|||||||
|
|
||||||
def itemState(root):
|
def itemState(root):
|
||||||
state = str(root) + '\n'
|
state = str(root) + '\n'
|
||||||
from .. import ViewBox
|
from pyqtgraph import ViewBox
|
||||||
state += 'bounding rect: ' + str(root.boundingRect()) + '\n'
|
state += 'bounding rect: ' + str(root.boundingRect()) + '\n'
|
||||||
if isinstance(root, ViewBox):
|
if isinstance(root, ViewBox):
|
||||||
state += "view range: " + str(root.viewRange()) + '\n'
|
state += "view range: " + str(root.viewRange()) + '\n'
|
||||||
@ -647,7 +433,7 @@ def transformStr(t):
|
|||||||
|
|
||||||
|
|
||||||
def indent(s, pfx):
|
def indent(s, pfx):
|
||||||
return '\n'.join([pfx+line for line in s.split('\n')])
|
return '\n'.join(pfx+line for line in s.split('\n'))
|
||||||
|
|
||||||
|
|
||||||
class TransposedImageItem(ImageItem):
|
class TransposedImageItem(ImageItem):
|
BIN
tests/images/imageitem/bool.png
Normal file
After Width: | Height: | Size: 622 B |
BIN
tests/images/imageitem/gradient_mono_byte.png
Normal file
After Width: | Height: | Size: 660 B |
BIN
tests/images/imageitem/gradient_mono_byte_levels.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
tests/images/imageitem/gradient_mono_int.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
tests/images/imageitem/gradient_mono_int_levels.png
Normal file
After Width: | Height: | Size: 670 B |
BIN
tests/images/imageitem/gradient_rgba_byte.png
Normal file
After Width: | Height: | Size: 718 B |
BIN
tests/images/imageitem/gradient_rgba_byte_levels.png
Normal file
After Width: | Height: | Size: 671 B |
BIN
tests/images/imageitem/gradient_rgba_float.png
Normal file
After Width: | Height: | Size: 749 B |
BIN
tests/images/imageitem/gradient_rgba_float_additive.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
tests/images/imageitem/gradient_rgba_float_alpha.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
tests/images/imageitem/init.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
tests/images/imageitem/levels1.png
Normal file
After Width: | Height: | Size: 647 B |
BIN
tests/images/imageitem/lut.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
tests/images/imageitem/monochrome.png
Normal file
After Width: | Height: | Size: 622 B |
BIN
tests/images/imageitem/resolution_with_downsampling_x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
tests/images/imageitem/resolution_with_downsampling_y.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
tests/images/imageitem/resolution_without_downsampling.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
tests/images/nonuniform_image/colormap-3x3.png
Normal file
After Width: | Height: | Size: 656 B |
BIN
tests/images/nonuniform_image/lut-3x3.png
Normal file
After Width: | Height: | Size: 668 B |
BIN
tests/images/plotcurveitem/connectall.png
Normal file
After Width: | Height: | Size: 885 B |
BIN
tests/images/plotcurveitem/connectarray.png
Normal file
After Width: | Height: | Size: 788 B |
BIN
tests/images/plotcurveitem/connectfinite.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
tests/images/plotcurveitem/connectpairs.png
Normal file
After Width: | Height: | Size: 770 B |
BIN
tests/images/roi/baseroi/roi_getarrayregion.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
tests/images/roi/baseroi/roi_getarrayregion_anisotropic.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
tests/images/roi/baseroi/roi_getarrayregion_halfpx.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
tests/images/roi/baseroi/roi_getarrayregion_img_trans.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
tests/images/roi/baseroi/roi_getarrayregion_inverty.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
tests/images/roi/baseroi/roi_getarrayregion_resize.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
tests/images/roi/baseroi/roi_getarrayregion_rotate.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion_anisotropic.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion_halfpx.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion_img_trans.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion_inverty.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion_resize.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
tests/images/roi/ellipseroi/roi_getarrayregion_rotate.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
tests/images/roi/polylineroi/closed_clear.png
Normal file
After Width: | Height: | Size: 627 B |
BIN
tests/images/roi/polylineroi/closed_click_segment.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
tests/images/roi/polylineroi/closed_drag_handle.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
tests/images/roi/polylineroi/closed_drag_new_handle.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
tests/images/roi/polylineroi/closed_drag_roi.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
tests/images/roi/polylineroi/closed_hover_handle.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
tests/images/roi/polylineroi/closed_hover_roi.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
tests/images/roi/polylineroi/closed_hover_segment.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
tests/images/roi/polylineroi/closed_init.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
tests/images/roi/polylineroi/closed_setpoints.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
tests/images/roi/polylineroi/closed_setstate.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
tests/images/roi/polylineroi/open_clear.png
Normal file
After Width: | Height: | Size: 627 B |
BIN
tests/images/roi/polylineroi/open_click_segment.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
tests/images/roi/polylineroi/open_drag_handle.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
tests/images/roi/polylineroi/open_drag_new_handle.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
tests/images/roi/polylineroi/open_drag_roi.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
tests/images/roi/polylineroi/open_hover_handle.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
tests/images/roi/polylineroi/open_hover_roi.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/images/roi/polylineroi/open_hover_segment.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
tests/images/roi/polylineroi/open_init.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/images/roi/polylineroi/open_setpoints.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
tests/images/roi/polylineroi/open_setstate.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
tests/images/roi/polylineroi/roi_getarrayregion.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
tests/images/roi/polylineroi/roi_getarrayregion_anisotropic.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
tests/images/roi/polylineroi/roi_getarrayregion_halfpx.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
tests/images/roi/polylineroi/roi_getarrayregion_img_trans.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/images/roi/polylineroi/roi_getarrayregion_inverty.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/images/roi/polylineroi/roi_getarrayregion_rotate.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
tests/images/roi/rectroi/roi_getarrayregion.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
tests/images/roi/rectroi/roi_getarrayregion_anisotropic.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
tests/images/roi/rectroi/roi_getarrayregion_halfpx.png
Normal file
After Width: | Height: | Size: 6.8 KiB |