diff --git a/.flake8 b/.flake8 index 0556c925..d988c302 100644 --- a/.flake8 +++ b/.flake8 @@ -3,47 +3,5 @@ exclude = .git,.tox,__pycache__,doc,old,build,dist show_source = True statistics = True verbose = 2 -select = - E101, - E112, - E122, - E125, - E133, - E223, - E224, - E242, - E273, - E274, - E901, - E902, - W191, - W601, - W602, - W603, - W604, - E124, - E231, - E211, - E261, - E271, - E272, - E304, - F401, - F402, - F403, - F404, - E501, - E502, - E702, - E703, - E711, - E712, - E721, - F811, - F812, - F821, - F822, - F823, - F831, - F841, - W292 +max-line-length = 88 +extend-ignore = E203, W503 \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 781aeebb..83924218 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,19 +24,19 @@ jobs: - python-version: "3.8" qt-lib: "pyqt" qt-version: "PyQt5~=5.15.0" - numpy-version: "~=1.19.0" + numpy-version: "~=1.20.0" - python-version: "3.8" qt-lib: "pyside" qt-version: "PySide2~=5.15.0" - numpy-version: "~=1.19.0" + numpy-version: "~=1.20.0" - python-version: "3.9" qt-lib: "pyqt" qt-version: "PyQt6" - numpy-version: "~=1.19.0" + numpy-version: "~=1.20.0" - python-version: "3.9" qt-lib: "pyside" qt-version: "PySide6" - numpy-version: "~=1.19.0" + numpy-version: "~=1.20.0" steps: - name: Checkout uses: actions/checkout@v2 @@ -70,9 +70,9 @@ jobs: - name: Install Dependencies run: | pip install --upgrade pip - pip install ${{ matrix.qt-version }} numpy${{ matrix.numpy-version }} scipy pyopengl h5py matplotlib + pip install ${{ matrix.qt-version }} numpy${{ matrix.numpy-version }} scipy pyopengl h5py matplotlib numba pip install . - pip install pytest pytest-cov pytest-xdist coverage + pip install pytest - name: "Install Linux VirtualDisplay" if: runner.os == 'Linux' run: | @@ -106,10 +106,8 @@ jobs: - name: Run Tests run: | mkdir $SCREENSHOT_DIR - pytest . -v \ - -n auto \ + pytest pyqtgraph examples -v \ --junitxml pytest.xml \ - --cov pyqtgraph --cov-report=xml --cov-report=html shell: bash - name: Upload Screenshots uses: actions/upload-artifact@v2 @@ -120,29 +118,9 @@ jobs: env: SCREENSHOT_DIR: ./screenshots - build-docs: - name: build docs - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Setup Python 3.9 - uses: actions/setup-python@v2 - with: - python-version: '3.9' - - name: Install Dependencies - run: | - cd doc - python -m pip install -r requirements.txt - - name: Build Documentation - run: | - cd doc - make html SPHINXOPTS='-W --keep-going -v' - build-wheel: name: build wheel runs-on: ubuntu-latest - steps: - uses: actions/checkout@v2 - name: Setup Python 3.9 diff --git a/.gitignore b/.gitignore index 8a81fee3..67810e95 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,4 @@ rtr.cvs .tags* .asv/ +asv.conf.json diff --git a/CHANGELOG b/CHANGELOG index d4f65c89..34e3320d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,102 @@ +pyqtgraph-0.12.1 + + New Features + - #1596 - Add ColorBarItem for simplified image level adjustment + + Performance enhancement: + - #1650 Don't use clip_array on scalers in DynamicPlotRange + - #1617 Optimize makeARGB for ubyte images + - #1648 Implement blocked variant of rescaleData + + API/Behavior Change: + - #1690 Parameter Item default created from value if not present + + Bug Fixes: + - #1665 Don't pass brush to mkPen in LegendItem.paint() + - #1660 Include colormaps in the python wheel + - #1653 Fix accidental disabling of style updates in PlotDataItem + - #1647 Handle empty adjacency array for GraphItem + - #1680 Fix test suite for big-endian architectures + - #1694 DateAxisItem now accounts for Daylight Savings Time + - #1691 Make sure dynamic range limiter runs on PlotDataItem + +pyqtgraph-0.12.0 + + Deprecations: + - Qt < 5.12, Python < 3.7, and NumPy < 1.17 are no longer supported + + New Features: + - Qt6 Compatibility (thank you so much @pijyoi !) + - #1466 CuPy/CUDA support for ImageItem! (thanks you so much @outofculture !) + - #1520/#1518 i18n Localization support for dialogs + - #1497 Allow toggling visibility via mouse click on LegendItem + - #1527 Expand parameter tree documentation + - #1563 Fixes to FlowChart documentation + - #1534 Extend pixmaps for GraphIcons + - #1566 Toggle-able options for ScatterPlotSpeedTest.py + - #1572 Arbitrary scale center ROI + - #1581 Equilateral Triangle ROI + + Performance enhancement: + - #1493 significant speedup to invertQTransform() + - #1501 various ImageItem performance improvements + - #1509 mkQApp will now have settings for better HiDPI settings + - #1518 Small Optimizations for functions.rescaleData() + - #1556 Reduce reallocation in PlotDataItem dynamic range limiter + - #1560 Cache scatter-plot items by hashable properties + - #1564 Correct id-based keyring of scatter plot pixmap cache + - #1569 Fix ScatterPlotItem performance regression + - #1619 Stop PlotDataItem from always sending full style information to PlotCurveItem/ScatterPlotItem + - #1630 Combine levels and lut only if both are present + - #1632/#1641/#1649 workaround for np.clip regression since numpy 1.17 + - #1641 Introduce functions.clip_array as faster replacement for currently slow np.clip + - #1637 PlotDataItem Fix viewRange <-> dynamic range limit + - #1650 Introduce functions.clip_scalar to clip scalar values + + API/Behavior Change: + - #1476 Use log modulus transform for y-axis log scaling + - #1522 InfiniteLine emits clicked signal event + - #1525 Use QOpenGLWidget instead of QGLWidget + - #1540 Support siPrefix with no suffix in SpinBox + - #1541 Use qWaitForWindowExposed instead of qWaitForWindowShown + - #1554 Disable paint in GLScatterPlotItem if it has no data + - #1573/1576 Add deprecation warnings to portions of library + - #1587 qApp.property('darkMode') is now a dynamic property + - #1613 Added keys() method to Parameter class + - #1646 Removed unhelpful warnings + - #1645 Make main stanza PyQt6 compatible + - #1644 Deprecate use of hex strings that do not start with "#" in mkColor + + Bug Fixes: + - #1487 Fix InfiniteLabel object has no attribute 'updateText' + - #1491 LinearRegionItem would break with setSpan + - #1496 enableMenu setting now preserved when passing ViewBox to PlotItem + - #1498 PlotDataItem now signals on setPos() + - #1500 AlignCenter should have been AlignHCenter + - #1506 Fix "camerPosition" typo in GLViewWidget + - #1510 Fix RemoteGraphicsView mouse interactions on Qt 5.12 + - #1517 Fix RemoteSpeedTest Shutdown Errors + - #1528 Support suffix for int parameters in SpinBox + - #1546 ImageView guards against key events when there is no time axis + - #1558 Fix Small Heights in ErrorBarItem + - #1567 Handle 0-d arrays in InfiniteLine.setPos() + - #1583 RawImageWidget fix port to QOpenGLWidget + - #1594 PlotItem removeItem regression fixed + - #1595 Workaround for CuPy Indexing Bug + - #1597 Fix RawImageWidget transpose did not handle luminance only images + - #1598 Remove references to self from lambdas for Signals + - #1618 Install sys.excepthook for PyQt6 + - #1639 Fix transformations in GradientLegend + - #1647 Have GraphItem handle empty adjacency array + - #1653 Fix accidentally styled updates in PlotDataItem + - #1651 Use collections.abc for collections metaclasses in colormap.py + pyqtgraph-0.11.1 New Features: - #800 Legend for bar graphs - #1244 Arrow scatter symbols - - #161 Allow hiding individulal points in scatter plot + - #161 Allow hiding individual points in scatter plot - #395 LegendItem display options - #1310 Added `Pa` to Units - #1310 `debug.ThreadTrace` add support for thread names @@ -35,14 +128,14 @@ pyqtgraph-0.11.1 - #496 Always antialias lines between gradient and region in HistogramLUTItem - #385 Add headWidth parameter to arrows - #551 fps variable on ImageView - - #1251 Allow explict utcOffset timezone in DateAxisItem + - #1251 Allow explicit utcOffset timezone in DateAxisItem - #1310 Add `SignalProxy.block` for temporary disabling of signal forwarding - #1310 `InfiniteLine.setPos` add support for array argument - #1310 Rate-limit Qt event processing in ProgressDialog if it is modal - #1289 Disable autoSIPrefix for DateAxisItem by default - #1274 Add tickAlpha to AxisItem Style Options - #402 Added `clear()` method to `GLViewWidget` - - #1264 Added exception to checkOpenGLVersion to highlight OpenGL ES incompatability + - #1264 Added exception to checkOpenGLVersion to highlight OpenGL ES incompatibility - #1257 Make painter tick font dependent for AxisItem - #1256 Added `setState`, `setState` and `saveState` to `ROI` - #1324 Pass through kwargs from MultiPlotItem.plot to PlotItem.plot diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 21e11727..d6d631e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,3 +70,14 @@ As PyQtGraph supports a wide array of Qt-bindings, and python versions, we make ### Continous Integration For our Continuous Integration, we utilize Azure Pipelines. Tested configurations are visible on [README](README.md). More information on coverage and test failures can be found on the respective tabs of the [build results page](https://dev.azure.com/pyqtgraph/pyqtgraph/_build?definitionId=1) + +### Benchmarks + +( *Still under development* ) To ensure this library is performant, we use [Air Speed Velocity (asv)](https://asv.readthedocs.io/en/stable/) to run benchmarks. For developing on core functions and classes, be aware of any impact your changes have on their speed. To configure and run asv: +``` +pip install asv +python setup.py asv_config +asv run +``` + +( TODO publish results ) diff --git a/README.md b/README.md index 618bff5f..7488887e 100644 --- a/README.md +++ b/README.md @@ -55,15 +55,18 @@ The following table represents the python environments we test in our CI system. | Qt-Bindings | Python 3.7 | Python 3.8 | Python 3.9 | | :------------- | :----------------: | :----------------: | :----------------: | | PySide2-5.12 | :white_check_mark: | :x: | :x: | -| PyQt5-5.12 | :white_check_mark: | :x: | :x: | -| PySide2-5.15 | :x: | :white_check_mark: | :x: | -| PyQt5-5.15 | :x: | :white_check_mark: | :x: | -| PySide6-6.0 | :x: | :x: | :white_check_mark: | -| PyQt6-6.0 | :x: | :x: | :white_check_mark: | +| PyQt5-5.12 | :white_check_mark: | | :x: | +| PySide2-5.15 | | :white_check_mark: | | +| PyQt5-5.15 | | :white_check_mark: | | +| PySide6-6.0 | | | :white_check_mark: | +| PyQt6-6.0 | | | :white_check_mark: | + +* :x: - Not compatible +* :white_check_mark: - Tested Support ------- - + * Report issues on the [GitHub issue tracker](https://github.com/pyqtgraph/pyqtgraph/issues) * Post questions to the [mailing list / forum](https://groups.google.com/forum/?fromgroups#!forum/pyqtgraph) or [StackOverflow](https://stackoverflow.com/questions/tagged/pyqtgraph) @@ -94,7 +97,8 @@ Here is a partial listing of some of the applications that make use of PyQtGraph * [ACQ4](https://github.com/acq4/acq4) * [Orange3](https://orangedatamining.com/) -* [neurotic](neurotic) +* [neurotic](https://neurotic.readthedocs.io) +* [ephyviewer](https://ephyviewer.readthedocs.io) * [Joulescope](https://www.joulescope.com/) * [rapidtide](https://rapidtide.readthedocs.io/en/latest/) * [argos](https://github.com/titusjan/argos) diff --git a/asv.conf.json b/asv.conf.json deleted file mode 100644 index bb7d805d..00000000 --- a/asv.conf.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - // The version of the config file format. Do not change, unless - // you know what you are doing. - "version": 1, - - // The name of the project being benchmarked - "project": "pyqtgraph", - - // The project's homepage - "project_url": "http://pyqtgraph.org/", - - // The URL or local path of the source code repository for the - // project being benchmarked - "repo": ".", - - // List of branches to benchmark. If not provided, defaults to "master" - // (for git) or "default" (for mercurial). - "branches": ["master"], // for git - // "branches": ["default"], // for mercurial - - // The DVCS being used. If not set, it will be automatically - // determined from "repo" by looking at the protocol in the URL - // (if remote), or by looking for special directories, such as - // ".git" (if local). - // "dvcs": "git", - - // The tool to use to create environments. May be "conda", - // "virtualenv" or other value depending on the plugins in use. - // If missing or the empty string, the tool will be automatically - // determined by looking for tools on the PATH environment - // variable. - "environment_type": "conda", - - // timeout in seconds for installing any dependencies in environment - // defaults to 10 min - //"install_timeout": 600, - - // the base URL to show a commit for the project. - "show_commit_url": "http://github.com/pyqtgraph/pyqtgraph/commit/", - - // The Pythons you'd like to test against. If not provided, defaults - // to the current version of Python used to run `asv`. - "pythons": ["2.7", "3.8"], - - // The matrix of dependencies to test. Each key is the name of a - // package (in PyPI) and the values are version numbers. An empty - // list or empty string indicates to just test against the default - // (latest) version. null indicates that the package is to not be - // installed. If the package to be tested is only available from - // PyPi, and the 'environment_type' is conda, then you can preface - // the package name by 'pip+', and the package will be installed via - // pip (with all the conda available packages installed first, - // followed by the pip installed packages). - // - "matrix": { - "numpy": [], - "numba": [], - "pyqt": ["4", "5"], - }, - - // Combinations of libraries/python versions can be excluded/included - // from the set to test. Each entry is a dictionary containing additional - // key-value pairs to include/exclude. - // - // An exclude entry excludes entries where all values match. The - // values are regexps that should match the whole string. - // - // An include entry adds an environment. Only the packages listed - // are installed. The 'python' key is required. The exclude rules - // do not apply to includes. - // - // In addition to package names, the following keys are available: - // - // - python - // Python version, as in the *pythons* variable above. - // - environment_type - // Environment type, as above. - // - sys_platform - // Platform, as in sys.platform. Possible values for the common - // cases: 'linux2', 'win32', 'cygwin', 'darwin'. - // - "exclude": [ - {"python": "3.8", "pyqt": "4"}, - ], - // - // "include": [ - // // additional env for python2.7 - // {"python": "2.7", "numpy": "1.8"}, - // // additional env if run on windows+conda - // {"platform": "win32", "environment_type": "conda", "python": "2.7", "libpython": ""}, - // ], - - // The directory (relative to the current directory) that benchmarks are - // stored in. If not provided, defaults to "benchmarks" - "benchmark_dir": "benchmarks", - - // The directory (relative to the current directory) to cache the Python - // environments in. If not provided, defaults to "env" - "env_dir": ".asv/env", - - // The directory (relative to the current directory) that raw benchmark - // results are stored in. If not provided, defaults to "results". - "results_dir": ".asv/results", - - // The directory (relative to the current directory) that the html tree - // should be written to. If not provided, defaults to "html". - "html_dir": ".asv/html", - - // The number of characters to retain in the commit hashes. - // "hash_length": 8, - - // `asv` will cache wheels of the recent builds in each - // environment, making them faster to install next time. This is - // number of builds to keep, per environment. - "build_cache_size": 5 - - // The commits after which the regression search in `asv publish` - // should start looking for regressions. Dictionary whose keys are - // regexps matching to benchmark names, and values corresponding to - // the commit (exclusive) after which to start looking for - // regressions. The default is to start from the first commit - // with results. If the commit is `null`, regression detection is - // skipped for the matching benchmark. - // - // "regressions_first_commits": { - // "some_benchmark": "352cdf", // Consider regressions only after this commit - // "another_benchmark": null, // Skip regression detection altogether - // } - - // The thresholds for relative change in results, after which `asv - // publish` starts reporting regressions. Dictionary of the same - // form as in ``regressions_first_commits``, with values - // indicating the thresholds. If multiple entries match, the - // maximum is taken. If no entry matches, the default is 5%. - // - // "regressions_thresholds": { - // "some_benchmark": 0.01, // Threshold of 1% - // "another_benchmark": 0.5, // Threshold of 50% - // } -} diff --git a/benchmarks/makeARGB.py b/benchmarks/makeARGB.py index 48656c52..4696c48e 100644 --- a/benchmarks/makeARGB.py +++ b/benchmarks/makeARGB.py @@ -1,72 +1,130 @@ import numpy as np +import pyqtgraph as pg from pyqtgraph.functions import makeARGB +try: + import cupy as cp -class TimeSuite(object): + pg.setConfigOption("useCupy", True) +except ImportError: + cp = None + + +class _TimeSuite(object): def __init__(self): - self.c_map = None + super(_TimeSuite, self).__init__() self.float_data = None self.uint8_data = None self.uint8_lut = None self.uint16_data = None self.uint16_lut = None + self.output = None + self.cupy_output = None def setup(self): - size = (500, 500) - - self.float_data = { - 'data': np.random.normal(size=size), - 'levels': [-4., 4.], - } - - self.uint16_data = { - 'data': np.random.randint(100, 4500, size=size).astype('uint16'), - 'levels': [250, 3000], - } - - self.uint8_data = { - 'data': np.random.randint(0, 255, size=size).astype('ubyte'), - 'levels': [20, 220], - } - - self.c_map = np.array([ - [-500., 255.], - [-255., 255.], - [0., 500.], - ]) - - self.uint8_lut = np.zeros((256, 4), dtype='ubyte') - for i in range(3): - self.uint8_lut[:, i] = np.clip(np.linspace(self.c_map[i][0], self.c_map[i][1], 256), 0, 255) - self.uint8_lut[:, 3] = 255 - - self.uint16_lut = np.zeros((2 ** 16, 4), dtype='ubyte') - for i in range(3): - self.uint16_lut[:, i] = np.clip(np.linspace(self.c_map[i][0], self.c_map[i][1], 2 ** 16), 0, 255) - self.uint16_lut[:, 3] = 255 - - -def make_test(dtype, use_levels, lut_name, func_name): - def time_test(self): - data = getattr(self, dtype + '_data') - makeARGB( - data['data'], - lut=getattr(self, lut_name + '_lut', None), - levels=use_levels and data['levels'], + size = (self.size, self.size) + self.float_data, self.uint16_data, self.uint8_data, self.uint16_lut, self.uint8_lut = self._create_data( + size, np ) + self.output = np.zeros(size + (4,), dtype=np.ubyte) + makeARGB(self.uint16_data["data"]) # prime the cpu + if cp: + self.cupy_output = cp.zeros(size + (4,), dtype=cp.ubyte) + makeARGB(cp.asarray(self.uint16_data["data"])) # prime the gpu + + @staticmethod + def _create_data(size, xp): + float_data = { + "data": xp.random.normal(size=size), + "levels": [-4.0, 4.0], + } + uint16_data = { + "data": xp.random.randint(100, 4500, size=size).astype("uint16"), + "levels": [250, 3000], + } + uint8_data = { + "data": xp.random.randint(0, 255, size=size).astype("ubyte"), + "levels": [20, 220], + } + c_map = xp.array([[-500.0, 255.0], [-255.0, 255.0], [0.0, 500.0]]) + uint8_lut = xp.zeros((256, 4), dtype="ubyte") + for i in range(3): + uint8_lut[:, i] = xp.clip(xp.linspace(c_map[i][0], c_map[i][1], 256), 0, 255) + uint8_lut[:, 3] = 255 + uint16_lut = xp.zeros((2 ** 16, 4), dtype="ubyte") + for i in range(3): + uint16_lut[:, i] = xp.clip(xp.linspace(c_map[i][0], c_map[i][1], 2 ** 16), 0, 255) + uint16_lut[:, 3] = 255 + return float_data, uint16_data, uint8_data, uint16_lut, uint8_lut + + +def make_test(dtype, use_cupy, use_levels, lut_name, func_name): + def time_test(self): + data = getattr(self, dtype + "_data") + levels = data["levels"] if use_levels else None + lut = getattr(self, lut_name + "_lut", None) if lut_name is not None else None + for _ in range(10): + img_data = data["data"] + output = self.output + if use_cupy: + img_data = cp.asarray(img_data) + output = self.cupy_output + makeARGB( + img_data, lut=lut, levels=levels, output=output, + ) + if use_cupy: + output.get(out=self.output) time_test.__name__ = func_name return time_test -for dt in ['float', 'uint16', 'uint8']: - for levels in [True, False]: - for ln in [None, 'uint8', 'uint16']: - name = f'time_makeARGB_{dt}_{"" if levels else "no"}levels_{ln or "no"}lut' - setattr(TimeSuite, name, make_test(dt, levels, ln, name)) +for cupy in [True, False]: + if cupy and cp is None: + continue + for dtype in ["float", "uint16", "uint8"]: + for levels in [True, False]: + if dtype == "float" and not levels: + continue + for lutname in [None, "uint8", "uint16"]: + name = ( + f'time_10x_makeARGB_{"cupy" if cupy else ""}{dtype}_{"" if levels else "no"}levels_{lutname or "no"}lut' + ) + setattr(_TimeSuite, name, make_test(dtype, cupy, levels, lutname, name)) -if __name__ == "__main__": - ts = TimeSuite() - ts.setup() +class Time0256Suite(_TimeSuite): + def __init__(self): + self.size = 256 + super(Time0256Suite, self).__init__() + + +class Time0512Suite(_TimeSuite): + def __init__(self): + self.size = 512 + super(Time0512Suite, self).__init__() + + +class Time1024Suite(_TimeSuite): + def __init__(self): + self.size = 1024 + super(Time1024Suite, self).__init__() + + +class Time2048Suite(_TimeSuite): + def __init__(self): + self.size = 2048 + super(Time2048Suite, self).__init__() + + +class Time3072Suite(_TimeSuite): + def __init__(self): + self.size = 3072 + super(Time3072Suite, self).__init__() + + +class Time4096Suite(_TimeSuite): + def __init__(self): + self.size = 4096 + super(Time4096Suite, self).__init__() diff --git a/doc/source/config_options.rst b/doc/source/config_options.rst index 797404c6..2fe5672f 100644 --- a/doc/source/config_options.rst +++ b/doc/source/config_options.rst @@ -26,10 +26,7 @@ imageAxisOrder str 'col-major' For 'row-major', image change in the future. editorCommand str or None None Command used to invoke code editor from ConsoleWidget. exitCleanup bool True Attempt to work around some exit crash bugs in PyQt and PySide. -useWeave bool False Use weave to speed up some operations, if it is available. -weaveDebug bool False Print full error message if weave compile fails. -useOpenGL bool False Enable OpenGL in GraphicsView. This can have unpredictable effects on stability - and performance. +useOpenGL bool False Enable OpenGL in GraphicsView. useCupy bool False Use cupy to perform calculations on the GPU. Only currently applies to ImageItem and its associated functions. enableExperimental bool False Enable experimental features (the curious can search for this key in the code). diff --git a/doc/source/graphicsItems/index.rst b/doc/source/graphicsItems/index.rst index 9b24f30f..54bf12f2 100644 --- a/doc/source/graphicsItems/index.rst +++ b/doc/source/graphicsItems/index.rst @@ -46,3 +46,4 @@ Contents: uigraphicsitem graphicswidgetanchor dateaxisitem + targetitem diff --git a/doc/source/graphicsItems/targetitem.rst b/doc/source/graphicsItems/targetitem.rst new file mode 100644 index 00000000..a21e6c8f --- /dev/null +++ b/doc/source/graphicsItems/targetitem.rst @@ -0,0 +1,17 @@ +TargetItem +========== + +.. autoclass:: pyqtgraph.TargetItem + :members: + + .. automethod:: pyqtgraph.TargetItem.__init__ + + +TargetLabel +================== + +.. autoclass:: pyqtgraph.TargetLabel + :members: + + .. automethod:: pyqtgraph.TargetLabel.__init__ + \ No newline at end of file diff --git a/doc/source/how_to_use.rst b/doc/source/how_to_use.rst index 0c5a1748..83b0fd01 100644 --- a/doc/source/how_to_use.rst +++ b/doc/source/how_to_use.rst @@ -101,11 +101,11 @@ Embedding PyQtGraph as a sub-package of a larger project When writing applications or python packages that make use of pyqtgraph, it is most common to install pyqtgraph system-wide (or within a virtualenv) and simply call `import pyqtgraph` from within your application. The main benefit to this is that pyqtgraph is configured independently of your application and thus you (or your users) are free to install newer versions of pyqtgraph without changing anything in your application. This is standard practice when developing with python. -However, it is also often the case, especially for scientific applications, that software is written for a very specific purpose and then archived. If we want to ensure that the software will still work ten years later, then it is preferrable to tie the application to a very specific version of pyqtgraph and *avoid* importing the system-installed version of pyqtgraph, which may be much newer (and potentially incompatible). This is especially the case when the application requires site-specific modifications to the pyqtgraph package which may not be present in the main releases. +Occasionally, a specific program needs to be kept in working order for an extended amount of time after development has been completed. This is often the case for single-purpose scientific applications. If we want to ensure that the software will still work ten years later, then it is preferrable to tie it to a very specific version of pyqtgraph and *avoid* importing the system-installed version, which may be much newer and potentially incompatible. This is especially true when the application requires site-specific modifications to the pyqtgraph package. -PyQtGraph facilitates this usage through two mechanisms. First, all internal import statements in pyqtgraph are relative, which allows the package to be renamed or used as a sub-package without any naming conflicts with other versions of pyqtgraph on the system (that is, pyqtgraph never refers to itself internally as 'pyqtgraph'). Second, a git subtree repository is available at https://github.com/pyqtgraph/pyqtgraph-core.git that contains only the 'pyqtgraph/' subtree, allowing the code to be cloned directly as a subtree of the application which uses it. +To support such a separate local installation, all internal import statements in pyqtgraph are relative. That means that pyqtgraph never refers to itself internally as 'pyqtgraph'. This allows the package to be renamed or used as a sub-package without any naming conflicts with other versions of pyqtgraph on the system. -The basic approach is to clone the repository into the appropriate location in your package. When you import pyqtgraph from within your package, be sure to use the full name to avoid importing any system-installed pyqtgraph packages. For example, imagine a simple project has the following structure:: +The basic approach is to clone the repository into the appropriate location in your project. When you import pyqtgraph, be sure to use the full name to avoid importing any system-installed pyqtgraph packages. For example, imagine a simple project has the following structure:: my_project/ __init__.py @@ -115,32 +115,32 @@ The basic approach is to clone the repository into the appropriate location in y def my_plot_function(*data): pg.plot(*data) -To embed a specific version of pyqtgraph, we would clone the pyqtgraph-core repository inside the project:: +To embed a specific version of pyqtgraph, we would clone the pyqtgraph repository inside the project, with a directory name that distinguishes it from a system-wide installation:: - my_project$ git clone https://github.com/pyqtgraph/pyqtgraph-core.git + my_project$ git clone https://github.com/pyqtgraph/pyqtgraph.git local_pyqtgraph Then adjust the import statements accordingly:: my_project/ __init__.py - pyqtgraph/ + local_pyqtgraph/ plotting.py """Plotting functions used by this package""" - import my_project.pyqtgraph as pg # be sure to use the local subpackage - # rather than any globally-installed - # versions. + import local_pyqtgraph.pyqtgraph as pg # be sure to use the local subpackage + # rather than any globally-installed + # version. def my_plot_function(*data): pg.plot(*data) -Use ``git checkout pyqtgraph-core-x.x.x`` to select a specific version of the repository, or use ``git pull`` to pull pyqtgraph updates from upstream (see the git documentation for more information). +Use ``git checkout pyqtgraph-x.x.x`` to select a specific library version from the repository, or use ``git pull`` to pull pyqtgraph updates from upstream (see the git documentation for more information). If you do not plan to make use of git's versioning features, adding the option ``--depth 1`` to the ``git clone`` command retrieves only the latest version. For projects that already use git for code control, it is also possible to include pyqtgraph as a git subtree within your own repository. The major advantage to this approach is that, in addition to being able to pull pyqtgraph updates from the upstream repository, it is also possible to commit your local pyqtgraph changes into the project repository and push those changes upstream:: - my_project$ git remote add pyqtgraph-core https://github.com/pyqtgraph/pyqtgraph-core.git - my_project$ git fetch pyqtgraph-core - my_project$ git merge -s ours --no-commit pyqtgraph-core/core - my_project$ mkdir pyqtgraph - my_project$ git read-tree -u --prefix=pyqtgraph/ pyqtgraph-core/core + my_project$ git remote add pyqtgraph https://github.com/pyqtgraph/pyqtgraph.git + my_project$ git fetch pyqtgraph + my_project$ git merge -s ours --allow-unrelated-histories --no-commit pyqtgraph/master + my_project$ mkdir local_pyqtgraph + my_project$ git read-tree -u --prefix=local_pyqtgraph/ pyqtgraph/master my_project$ git commit -m "Added pyqtgraph to project repository" See the ``git subtree`` documentation for more information. diff --git a/doc/source/images.rst b/doc/source/images.rst index 0a4ac147..b1813b7f 100644 --- a/doc/source/images.rst +++ b/doc/source/images.rst @@ -21,6 +21,6 @@ There are a few other methods for displaying images as well: * Instances of :class:`~pyqtgraph.ImageItem` can be used inside a :class:`ViewBox ` or :class:`GraphicsView `. * For higher performance, use :class:`~pyqtgraph.RawImageWidget`. -Any of these classes are acceptable for displaying video by calling setImage() to display a new frame. To increase performance, the image processing system uses scipy.weave to produce compiled libraries. If your computer has a compiler available, weave will automatically attempt to build the libraries it needs on demand. If this fails, then the slower pure-python methods will be used instead. +Any of these classes are acceptable for displaying video by calling setImage() to display a new frame. For more information, see the classes listed above and the 'VideoSpeedTest', 'ImageItem', 'ImageView', and 'HistogramLUT' :ref:`examples`. \ No newline at end of file diff --git a/examples/Arrow.py b/examples/Arrow.py index bd0b6b07..7f5a5c97 100644 --- a/examples/Arrow.py +++ b/examples/Arrow.py @@ -50,8 +50,5 @@ p2.addItem(a) anim = a.makeAnimation(loop=-1) anim.start() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/BarGraphItem.py b/examples/BarGraphItem.py index 6caa8862..a5b0a0c2 100644 --- a/examples/BarGraphItem.py +++ b/examples/BarGraphItem.py @@ -34,8 +34,5 @@ class BarGraph(pg.BarGraphItem): bg = BarGraph(x=x, y=y1*0.3+2, height=0.4+y1*0.2, width=0.8) win.addItem(bg) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/CLIexample.py b/examples/CLIexample.py index f32cf81c..02bb989b 100644 --- a/examples/CLIexample.py +++ b/examples/CLIexample.py @@ -18,9 +18,5 @@ pg.plot(data, title="Simplest possible plotting example") data = np.random.normal(size=(500,500)) pg.image(data, title="Simplest possible image example") - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'): - pg.QtGui.QApplication.exec_() + pg.mkQApp().exec_() diff --git a/examples/ColorBarItem.py b/examples/ColorBarItem.py new file mode 100644 index 00000000..ee3f6485 --- /dev/null +++ b/examples/ColorBarItem.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +""" +This example demonstrates the use of ColorBarItem, which displays a simple interactive color bar. +""" +## Add path to library (just for examples; you do not need this) +import initExample + +import numpy as np +from pyqtgraph.Qt import QtWidgets, mkQApp +import pyqtgraph as pg + +class MainWindow(QtWidgets.QMainWindow): + """ example application main window """ + def __init__(self, *args, **kwargs): + super(MainWindow, self).__init__(*args, **kwargs) + gr_wid = pg.GraphicsLayoutWidget(show=True) + self.setCentralWidget(gr_wid) + self.setWindowTitle('pyqtgraph example: Interactive color bar') + self.resize(800,700) + self.show() + + ## Create image items + data = np.fromfunction(lambda i, j: (1+0.3*np.sin(i)) * (i)**2 + (j)**2, (100, 100)) + noisy_data = data * (1 + 0.2 * np.random.random(data.shape) ) + noisy_transposed = noisy_data.transpose() + + #--- add non-interactive image with integrated color ----------------- + i1 = pg.ImageItem(image=data) + p1 = gr_wid.addPlot(title="non-interactive") + p1.addItem( i1 ) + p1.setMouseEnabled( x=False, y=False) + p1.disableAutoRange() + p1.hideButtons() + p1.setRange(xRange=(0,100), yRange=(0,100), padding=0) + for key in ['left','right','top','bottom']: + p1.showAxis(key) + axis = p1.getAxis(key) + axis.setZValue(1) + if key in ['top', 'right']: + p1.getAxis(key).setStyle( showValues=False ) + + cmap = pg.colormap.get('CET-L9') + bar = pg.ColorBarItem( + interactive=False, values= (0, 30_000), cmap=cmap, + label='vertical fixed color bar' + ) + bar.setImageItem( i1, insert_in=p1 ) + + #--- add interactive image with integrated horizontal color bar -------------- + i2 = pg.ImageItem(image=noisy_data) + p2 = gr_wid.addPlot(1,0, 1,1, title="interactive") + p2.addItem( i2, title='' ) + # inserted color bar also works with labels on the right. + p2.showAxis('right') + p2.getAxis('left').setStyle( showValues=False ) + p2.getAxis('bottom').setLabel('bottom axis label') + p2.getAxis('right').setLabel('right axis label') + + cmap = pg.colormap.get('CET-L4') + bar = pg.ColorBarItem( + values = (0, 30_000), + cmap=cmap, + label='horizontal color bar', + limits = (0, None), + rounding=1000, + orientation = 'horizontal', + pen='#8888FF', hoverPen='#EEEEFF', hoverBrush='#EEEEFF80' + ) + bar.setImageItem( i2, insert_in=p2 ) + + #--- multiple images adjusted by a separate color bar ------------------------ + i3 = pg.ImageItem(image=noisy_data) + p3 = gr_wid.addPlot(0,1, 1,1, title="shared 1") + p3.addItem( i3 ) + + i4 = pg.ImageItem(image=noisy_transposed) + p4 = gr_wid.addPlot(1,1, 1,1, title="shared 2") + p4.addItem( i4 ) + + cmap = pg.colormap.get('CET-L8') + bar = pg.ColorBarItem( + # values = (-15_000, 15_000), + limits = (-30_000, 30_000), # start with full range... + rounding=1000, + width = 10, + cmap=cmap ) + bar.setImageItem( [i3, i4] ) + bar.setLevels( low=-5_000, high=15_000) # ... then adjust to retro sunset. + + # manually adjust reserved space at top and bottom to align with plot + bar.getAxis('bottom').setHeight(21) + bar.getAxis('top').setHeight(31) + gr_wid.addItem(bar, 0,2, 2,1) # large bar spanning both rows + +mkQApp("ColorBarItem Example") +main_window = MainWindow() + +## Start Qt event loop +if __name__ == '__main__': + mkQApp().exec_() diff --git a/examples/ColorButton.py b/examples/ColorButton.py index e9df9750..c9621f54 100644 --- a/examples/ColorButton.py +++ b/examples/ColorButton.py @@ -26,8 +26,5 @@ def done(btn): btn.sigColorChanging.connect(change) btn.sigColorChanged.connect(done) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ConsoleWidget.py b/examples/ConsoleWidget.py index 8234269d..ab390a9c 100644 --- a/examples/ConsoleWidget.py +++ b/examples/ConsoleWidget.py @@ -29,8 +29,5 @@ c = pyqtgraph.console.ConsoleWidget(namespace=namespace, text=text) c.show() c.setWindowTitle('pyqtgraph example: ConsoleWidget') -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/CustomGraphItem.py b/examples/CustomGraphItem.py index 8e494c3a..ff40f571 100644 --- a/examples/CustomGraphItem.py +++ b/examples/CustomGraphItem.py @@ -129,8 +129,5 @@ g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False, tex -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/DataSlicing.py b/examples/DataSlicing.py index 8bd1e04d..610dd194 100644 --- a/examples/DataSlicing.py +++ b/examples/DataSlicing.py @@ -57,8 +57,5 @@ imv1.setLevels(-0.003, 0.003) update() -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/DataTreeWidget.py b/examples/DataTreeWidget.py index 47a5f32b..e129f137 100644 --- a/examples/DataTreeWidget.py +++ b/examples/DataTreeWidget.py @@ -42,8 +42,5 @@ tree.setWindowTitle('pyqtgraph example: DataTreeWidget') tree.resize(600,600) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() \ No newline at end of file + pg.mkQApp().exec_() diff --git a/examples/DateAxisItem.py b/examples/DateAxisItem.py index d789308d..86be0176 100644 --- a/examples/DateAxisItem.py +++ b/examples/DateAxisItem.py @@ -26,8 +26,5 @@ w.plot(now-(2*np.pi/x)**2*100*np.pi*1e7, np.sin(x), symbol='o') w.setWindowTitle('pyqtgraph example: DateAxisItem') w.show() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - app.exec_() + app.exec_() diff --git a/examples/DateAxisItem_QtDesigner.py b/examples/DateAxisItem_QtDesigner.py index d92a7503..a7dc46a3 100644 --- a/examples/DateAxisItem_QtDesigner.py +++ b/examples/DateAxisItem_QtDesigner.py @@ -42,8 +42,5 @@ window = ExampleApp() window.setWindowTitle('pyqtgraph example: DateAxisItem_QtDesigner') window.show() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - app.exec_() + app.exec_() diff --git a/examples/DiffTreeWidget.py b/examples/DiffTreeWidget.py index 780e1eaf..0c7fcd78 100644 --- a/examples/DiffTreeWidget.py +++ b/examples/DiffTreeWidget.py @@ -45,8 +45,5 @@ tree.setWindowTitle('pyqtgraph example: DiffTreeWidget') tree.resize(1000, 800) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() \ No newline at end of file + pg.mkQApp().exec_() diff --git a/examples/Draw.py b/examples/Draw.py index 1401c398..22ddf7e9 100644 --- a/examples/Draw.py +++ b/examples/Draw.py @@ -42,8 +42,5 @@ kern = np.array([ img.setDrawKernel(kern, mask=kern, center=(1,1), mode='add') img.setLevels([0, 10]) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ErrorBarItem.py b/examples/ErrorBarItem.py index cd576d51..5748f913 100644 --- a/examples/ErrorBarItem.py +++ b/examples/ErrorBarItem.py @@ -26,8 +26,5 @@ err = pg.ErrorBarItem(x=x, y=y, top=top, bottom=bottom, beam=0.5) plt.addItem(err) plt.plot(x, y, symbol='o', pen={'color': 0.8, 'width': 2}) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ExampleApp.py b/examples/ExampleApp.py index c54d79b2..01d00567 100644 --- a/examples/ExampleApp.py +++ b/examples/ExampleApp.py @@ -360,15 +360,7 @@ class ExampleLoader(QtGui.QMainWindow): fn = self.currentFile() if fn is None: return - if sys.platform.startswith('win'): - args = [os.P_NOWAIT, sys.executable, '"'+sys.executable+'"', '"' + fn + '"'] - else: - args = [os.P_NOWAIT, sys.executable, sys.executable, fn] - if env is None: - os.spawnl(*args) - else: - args.append(env) - os.spawnle(*args) + subprocess.Popen([sys.executable, fn], env=env) def showFile(self): fn = self.currentFile() @@ -377,7 +369,8 @@ class ExampleLoader(QtGui.QMainWindow): return if os.path.isdir(fn): fn = os.path.join(fn, '__main__.py') - text = open(fn).read() + with open(fn, "r") as currentFile: + text = currentFile.read() self.ui.codeView.setPlainText(text) self.ui.loadedFileLabel.setText(fn) self.codeBtn.hide() @@ -393,7 +386,6 @@ def main(): app = pg.mkQApp() loader = ExampleLoader() app.exec_() -# or condition so pytest runs ExampleApp as part of test suite + if __name__ == '__main__': - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - main() + main() diff --git a/examples/FillBetweenItem.py b/examples/FillBetweenItem.py index fc91ee32..c53276bb 100644 --- a/examples/FillBetweenItem.py +++ b/examples/FillBetweenItem.py @@ -45,8 +45,5 @@ timer.timeout.connect(update) timer.start(30) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/Flowchart.py b/examples/Flowchart.py index 45e833ce..f9167872 100644 --- a/examples/Flowchart.py +++ b/examples/Flowchart.py @@ -75,10 +75,5 @@ fc.connectTerminals(fc['dataIn'], pw1Node['In']) fc.connectTerminals(fNode['Out'], pw2Node['In']) fc.connectTerminals(fNode['Out'], fc['dataOut']) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/FlowchartCustomNode.py b/examples/FlowchartCustomNode.py index dc1fd55f..48927224 100644 --- a/examples/FlowchartCustomNode.py +++ b/examples/FlowchartCustomNode.py @@ -150,10 +150,5 @@ fc.connectTerminals(fc['dataIn'], v1Node['data']) fc.connectTerminals(fNode['dataOut'], v2Node['data']) fc.connectTerminals(fNode['dataOut'], fc['dataOut']) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLBarGraphItem.py b/examples/GLBarGraphItem.py index e593d54a..84a44a48 100644 --- a/examples/GLBarGraphItem.py +++ b/examples/GLBarGraphItem.py @@ -39,9 +39,5 @@ size[...,2] = np.random.normal(size=(10,10)) bg = gl.GLBarGraphItem(pos, size) w.addItem(bg) - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLImageItem.py b/examples/GLImageItem.py index 6a31c09a..f5663df4 100644 --- a/examples/GLImageItem.py +++ b/examples/GLImageItem.py @@ -50,8 +50,5 @@ w.addItem(v3) ax = gl.GLAxisItem() w.addItem(ax) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLIsosurface.py b/examples/GLIsosurface.py index 0beeea66..1afe797c 100644 --- a/examples/GLIsosurface.py +++ b/examples/GLIsosurface.py @@ -7,7 +7,7 @@ This example uses the isosurface function to convert a scalar field ## Add path to library (just for examples; you do not need this) import initExample -from pyqtgraph.Qt import QtCore, QtGui +import numpy as np import pyqtgraph as pg import pyqtgraph.opengl as gl @@ -22,23 +22,16 @@ g = gl.GLGridItem() g.scale(2,2,1) w.addItem(g) -import numpy as np - ## Define a scalar field from which we will generate an isosurface def psi(i, j, k, offset=(25, 25, 50)): x = i-offset[0] y = j-offset[1] z = k-offset[2] - th = np.arctan2(z, (x**2+y**2)**0.5) - phi = np.arctan2(y, x) - r = (x**2 + y**2 + z **2)**0.5 + th = np.arctan2(z, np.hypot(x, y)) + r = np.sqrt(x**2 + y**2 + z **2) a0 = 1 - #ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th) ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1) - return ps - - #return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2 print("Generating scalar field..") @@ -66,10 +59,5 @@ m2.setGLOptions('additive') w.addItem(m2) m2.translate(-25, -25, -50) - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLLinePlotItem.py b/examples/GLLinePlotItem.py index 0d07b153..82e1f6fb 100644 --- a/examples/GLLinePlotItem.py +++ b/examples/GLLinePlotItem.py @@ -29,24 +29,16 @@ gz = gl.GLGridItem() gz.translate(0, 0, -10) w.addItem(gz) -def fn(x, y): - return np.cos((x**2 + y**2)**0.5) - n = 51 y = np.linspace(-10,10,n) x = np.linspace(-10,10,100) for i in range(n): - yi = np.array([y[i]]*100) - d = (x**2 + yi**2)**0.5 + yi = y[i] + d = np.hypot(x, yi) z = 10 * np.cos(d) / (d+1) - pts = np.vstack([x,yi,z]).transpose() + pts = np.column_stack([x, np.full_like(x, yi), z]) plt = gl.GLLinePlotItem(pos=pts, color=pg.glColor((i,n*1.3)), width=(i+1)/10., antialias=True) w.addItem(plt) - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLMeshItem.py b/examples/GLMeshItem.py index 692f8f8f..16a647b4 100644 --- a/examples/GLMeshItem.py +++ b/examples/GLMeshItem.py @@ -117,13 +117,5 @@ m6.rotate(0., 0, 1, 1) w.addItem(m5) w.addItem(m6) - - - - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLScatterPlotItem.py b/examples/GLScatterPlotItem.py index b52e17b5..1d8d5e06 100644 --- a/examples/GLScatterPlotItem.py +++ b/examples/GLScatterPlotItem.py @@ -8,6 +8,7 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots. import initExample import pyqtgraph as pg +from pyqtgraph import functions as fn from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph.opengl as gl import numpy as np @@ -84,10 +85,10 @@ def update(): global phase, sp2, d2 s = -np.cos(d2*2+phase) color = np.empty((len(d2),4), dtype=np.float32) - color[:,3] = np.clip(s * 0.1, 0, 1) - color[:,0] = np.clip(s * 3.0, 0, 1) - color[:,1] = np.clip(s * 1.0, 0, 1) - color[:,2] = np.clip(s ** 3, 0, 1) + color[:,3] = fn.clip_array(s * 0.1, 0., 1.) + color[:,0] = fn.clip_array(s * 3.0, 0., 1.) + color[:,1] = fn.clip_array(s * 1.0, 0., 1.) + color[:,2] = fn.clip_array(s ** 3, 0., 1.) sp2.setData(color=color) phase -= 0.1 @@ -106,9 +107,5 @@ t = QtCore.QTimer() t.timeout.connect(update) t.start(50) - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLSurfacePlot.py b/examples/GLSurfacePlot.py index cac8d5fe..0cee2adc 100644 --- a/examples/GLSurfacePlot.py +++ b/examples/GLSurfacePlot.py @@ -92,8 +92,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(30) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLViewWidget.py b/examples/GLViewWidget.py index d06a6ed8..dec4f89a 100644 --- a/examples/GLViewWidget.py +++ b/examples/GLViewWidget.py @@ -27,8 +27,5 @@ ax2.setParentItem(b) b.translate(1,1,1) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLVolumeItem.py b/examples/GLVolumeItem.py index 628ee971..ba67514b 100644 --- a/examples/GLVolumeItem.py +++ b/examples/GLVolumeItem.py @@ -7,9 +7,10 @@ Demonstrates GLVolumeItem for displaying volumetric data. ## Add path to library (just for examples; you do not need this) import initExample +import numpy as np import pyqtgraph as pg -from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph.opengl as gl +from pyqtgraph import functions as fn app = pg.mkQApp("GLVolumeItem Example") w = gl.GLViewWidget() @@ -23,27 +24,21 @@ g = gl.GLGridItem() g.scale(10, 10, 1) w.addItem(g) -import numpy as np ## Hydrogen electron probability density def psi(i, j, k, offset=(50,50,100)): x = i-offset[0] y = j-offset[1] z = k-offset[2] - th = np.arctan2(z, (x**2+y**2)**0.5) - phi = np.arctan2(y, x) - r = (x**2 + y**2 + z **2)**0.5 + th = np.arctan2(z, np.hypot(x, y)) + r = np.sqrt(x**2 + y**2 + z **2) a0 = 2 - #ps = (1./81.) * (2./np.pi)**0.5 * (1./a0)**(3/2) * (6 - r/a0) * (r/a0) * np.exp(-r/(3*a0)) * np.cos(th) ps = (1./81.) * 1./(6.*np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * np.exp(-r/(3*a0)) * (3 * np.cos(th)**2 - 1) - return ps - - #return ((1./81.) * (1./np.pi)**0.5 * (1./a0)**(3/2) * (r/a0)**2 * (r/a0) * np.exp(-r/(3*a0)) * np.sin(th) * np.cos(th) * np.exp(2 * 1j * phi))**2 data = np.fromfunction(psi, (100,100,200)) -positive = np.log(np.clip(data, 0, data.max())**2) -negative = np.log(np.clip(-data, 0, -data.min())**2) +positive = np.log(fn.clip_array(data, np.finfo(data.dtype).eps, data.max())**2) +negative = np.log(fn.clip_array(-data, -np.finfo(data.dtype).eps, -data.min())**2) d2 = np.empty(data.shape + (4,), dtype=np.ubyte) d2[..., 0] = positive * (255./positive.max()) @@ -63,8 +58,5 @@ w.addItem(v) ax = gl.GLAxisItem() w.addItem(ax) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GLshaders.py b/examples/GLshaders.py index 86719c6d..38e6988d 100644 --- a/examples/GLshaders.py +++ b/examples/GLshaders.py @@ -9,6 +9,7 @@ used to affect the appearance of a surface. ## Add path to library (just for examples; you do not need this) import initExample +import numpy as np from pyqtgraph.Qt import QtCore, QtGui import pyqtgraph as pg import pyqtgraph.opengl as gl @@ -23,9 +24,6 @@ g = gl.GLGridItem() g.scale(2,2,1) w.addItem(g) -import numpy as np - - md = gl.MeshData.sphere(rows=10, cols=20) x = np.linspace(-8, 8, 6) @@ -101,10 +99,5 @@ w.addItem(m6) #w.addItem(m2) #m2.translate(-25, -25, -50) - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GradientEditor.py b/examples/GradientEditor.py index ad8c8eee..e334f501 100644 --- a/examples/GradientEditor.py +++ b/examples/GradientEditor.py @@ -20,9 +20,5 @@ mw.show() ge = pg.GradientEditorItem() mw.setCentralItem(ge) - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GradientWidget.py b/examples/GradientWidget.py index 623171b2..1ae53bc4 100644 --- a/examples/GradientWidget.py +++ b/examples/GradientWidget.py @@ -47,12 +47,8 @@ l.addWidget(w3, 2, 1) l.addWidget(w4, 1, 0) l.addWidget(label, 1, 1) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GraphItem.py b/examples/GraphItem.py index 094b84bd..8e4e5c99 100644 --- a/examples/GraphItem.py +++ b/examples/GraphItem.py @@ -57,11 +57,5 @@ lines = np.array([ ## Update the graph g.setData(pos=pos, adj=adj, pen=lines, size=1, symbol=symbols, pxMode=False) - - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GraphicsLayout.py b/examples/GraphicsLayout.py index 74d61c10..8edea2e6 100644 --- a/examples/GraphicsLayout.py +++ b/examples/GraphicsLayout.py @@ -78,10 +78,5 @@ p2.plot([1,3,2,4,3,5]) p4.plot([1,3,2,4,3,5]) p5.plot([1,3,2,4,3,5]) - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/GraphicsScene.py b/examples/GraphicsScene.py index 69be5b53..3caf87c6 100644 --- a/examples/GraphicsScene.py +++ b/examples/GraphicsScene.py @@ -58,9 +58,5 @@ vb.addItem(prox) g = pg.GridItem() vb.addItem(g) - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/HistogramLUT.py b/examples/HistogramLUT.py index 4c9ef4cf..826dcc6b 100644 --- a/examples/HistogramLUT.py +++ b/examples/HistogramLUT.py @@ -3,45 +3,49 @@ Use a HistogramLUTWidget to control the contrast / coloration of an image. """ -## Add path to library (just for examples; you do not need this) +# Add path to library (just for examples; you do not need this) import initExample import numpy as np -from pyqtgraph.Qt import QtGui, QtCore -import pyqtgraph as pg +import pyqtgraph as pg +from pyqtgraph.Qt import QtGui app = pg.mkQApp("Histogram Lookup Table Example") win = QtGui.QMainWindow() -win.resize(800,600) +win.resize(880, 600) win.show() win.setWindowTitle('pyqtgraph example: Histogram LUT') cw = QtGui.QWidget() win.setCentralWidget(cw) -l = QtGui.QGridLayout() -cw.setLayout(l) -l.setSpacing(0) +layout = QtGui.QGridLayout() +cw.setLayout(layout) +layout.setSpacing(0) -v = pg.GraphicsView() +view = pg.GraphicsView() vb = pg.ViewBox() vb.setAspectLocked() -v.setCentralItem(vb) -l.addWidget(v, 0, 0, 3, 1) +view.setCentralItem(vb) +layout.addWidget(view, 0, 1, 3, 1) + +hist = pg.HistogramLUTWidget(gradientPosition="left") +layout.addWidget(hist, 0, 2) -w = pg.HistogramLUTWidget() -l.addWidget(w, 0, 1) monoRadio = QtGui.QRadioButton('mono') rgbaRadio = QtGui.QRadioButton('rgba') -l.addWidget(monoRadio, 1, 1) -l.addWidget(rgbaRadio, 2, 1) +layout.addWidget(monoRadio, 1, 2) +layout.addWidget(rgbaRadio, 2, 2) monoRadio.setChecked(True) + def setLevelMode(): mode = 'mono' if monoRadio.isChecked() else 'rgba' - w.setLevelMode(mode) + hist.setLevelMode(mode) + + monoRadio.toggled.connect(setLevelMode) data = pg.gaussianFilter(np.random.normal(size=(256, 256, 3)), (20, 20, 0)) @@ -52,11 +56,7 @@ img = pg.ImageItem(data) vb.addItem(img) vb.autoRange() -w.setImageItem(img) +hist.setImageItem(img) - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ImageItem.py b/examples/ImageItem.py index 49141084..29994826 100644 --- a/examples/ImageItem.py +++ b/examples/ImageItem.py @@ -58,8 +58,5 @@ def updateData(): timer.timeout.connect(updateData) updateData() -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ImageView.py b/examples/ImageView.py index 56ee2d0f..8c776a97 100644 --- a/examples/ImageView.py +++ b/examples/ImageView.py @@ -63,8 +63,5 @@ imv.setColorMap(cmap) imv.ui.roiBtn.setChecked(True) imv.roiClicked() -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/InfiniteLine.py b/examples/InfiniteLine.py index d90ae143..78ead34b 100644 --- a/examples/InfiniteLine.py +++ b/examples/InfiniteLine.py @@ -32,14 +32,54 @@ p1.addItem(inf1) p1.addItem(inf2) p1.addItem(inf3) +targetItem1 = pg.TargetItem() + +targetItem2 = pg.TargetItem( + pos=(30, 5), + size=20, + symbol="star", + pen="#F4511E", + label="vert={1:0.2f}", + labelOpts={ + "offset": QtCore.QPoint(15, 15) + } +) +targetItem2.label().setAngle(45) + +targetItem3 = pg.TargetItem( + pos=(10, 10), + size=10, + symbol="x", + pen="#00ACC1", +) +targetItem3.setLabel( + "Third Label", + { + "anchor": QtCore.QPointF(0.5, 0.5), + "offset": QtCore.QPointF(30, 0), + "color": "#558B2F", + "rotateAxis": (0, 1) + } +) + + +def callableFunction(x, y): + return f"Square Values: ({x**2:.4f}, {y**2:.4f})" + +targetItem4 = pg.TargetItem( + pos=(10, -10), + label=callableFunction +) + +p1.addItem(targetItem1) +p1.addItem(targetItem2) +p1.addItem(targetItem3) +p1.addItem(targetItem4) + # Add a linear region with a label lr = pg.LinearRegionItem(values=[70, 80]) p1.addItem(lr) label = pg.InfLineLabel(lr.lines[1], "region 1", position=0.95, rotateAxis=(1,0), anchor=(1, 1)) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/JoystickButton.py b/examples/JoystickButton.py index c6965900..8288ad8f 100644 --- a/examples/JoystickButton.py +++ b/examples/JoystickButton.py @@ -46,11 +46,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(30) - - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/Legend.py b/examples/Legend.py index f3683cd0..73cecf5e 100644 --- a/examples/Legend.py +++ b/examples/Legend.py @@ -39,8 +39,5 @@ legend.addItem(c1, 'curve1') legend.addItem(c2, 'curve2') legend.addItem(s1, 'scatter') -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/LogPlotTest.py b/examples/LogPlotTest.py index 1e6bf669..eb34bcea 100644 --- a/examples/LogPlotTest.py +++ b/examples/LogPlotTest.py @@ -30,8 +30,5 @@ p5.setLabel('bottom', "Y Axis", units='s') p5.setLogMode(x=True, y=False) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/MouseSelection.py b/examples/MouseSelection.py index 3a573751..a964932b 100644 --- a/examples/MouseSelection.py +++ b/examples/MouseSelection.py @@ -30,8 +30,5 @@ for c in curves: win.addItem(c) c.sigClicked.connect(plotClicked) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/MultiPlotSpeedTest.py b/examples/MultiPlotSpeedTest.py index e8f7ee85..bcdd1d83 100644 --- a/examples/MultiPlotSpeedTest.py +++ b/examples/MultiPlotSpeedTest.py @@ -62,8 +62,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(0) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/MultiPlotWidget.py b/examples/MultiPlotWidget.py index 4802a046..f8670785 100644 --- a/examples/MultiPlotWidget.py +++ b/examples/MultiPlotWidget.py @@ -32,9 +32,6 @@ ma = MetaArray(data, info=[ ]) pw.plot(ma, pen='y') -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/MultiplePlotAxes.py b/examples/MultiplePlotAxes.py index 75e0c680..7215cbf3 100644 --- a/examples/MultiplePlotAxes.py +++ b/examples/MultiplePlotAxes.py @@ -60,8 +60,5 @@ p1.plot([1,2,4,8,16,32]) p2.addItem(pg.PlotCurveItem([10,20,40,80,40,20], pen='b')) p3.addItem(pg.PlotCurveItem([3200,1600,800,400,200,100], pen='r')) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/NonUniformImage.py b/examples/NonUniformImage.py index 9d13cc0b..2048c900 100644 --- a/examples/NonUniformImage.py +++ b/examples/NonUniformImage.py @@ -78,9 +78,5 @@ p.setLabel(axis='left', text='Torque [Nm]') p.axes['bottom']['item'].setZValue(1000) p.axes['left']['item'].setZValue(1000) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/PColorMeshItem.py b/examples/PColorMeshItem.py index 44604c8e..fbf920e5 100644 --- a/examples/PColorMeshItem.py +++ b/examples/PColorMeshItem.py @@ -83,8 +83,5 @@ def updateData(): timer.timeout.connect(updateData) updateData() -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/PaletteApplicationExample.py b/examples/PaletteApplicationExample.py index 7a16b33f..25ed1b82 100644 --- a/examples/PaletteApplicationExample.py +++ b/examples/PaletteApplicationExample.py @@ -26,7 +26,7 @@ class MainWindow(QtWidgets.QMainWindow): test_palette = pg.palette.get('system') - pg.palette.get('relaxed-dark').apply() + pg.palette.get('relaxed_dark').apply() main_layout = QtWidgets.QGridLayout( main_wid ) gr_wid = pg.GraphicsLayoutWidget(show=True) diff --git a/examples/PaletteTestAndEdit.py b/examples/PaletteTestAndEdit.py index 9f225955..31f1be09 100644 --- a/examples/PaletteTestAndEdit.py +++ b/examples/PaletteTestAndEdit.py @@ -75,7 +75,7 @@ class MainWindow(QtWidgets.QMainWindow): ax.setZValue(0.1) self.curves = [] - curve = pg.PlotCurveItem(pen='p0', brush='p0') # ('p0',127)) + curve = pg.PlotCurveItem(pen='p0', brush=('p0',127)) curve.setFillLevel(0) self.curves.append( (1, 1, curve) ) # dataset 1, vertical offset 3 plt.addItem(curve) @@ -101,7 +101,7 @@ class MainWindow(QtWidgets.QMainWindow): plt.getAxis('left').setLabel('plot color') plt.getAxis('left').setGrid(0.5) # 63) - pen_list = [('p0',255),'p1','p2','p3','p4','p5','p6','p7'] # add right-side plots for each main color + pen_list = [('p0',2),('p1',2),('p2',2),('p3',2),('p4',2),('p5',2),('p6',2),('p7',2)] # add right-side plots for each main color for idx, pen in enumerate( pen_list ): curve = pg.PlotCurveItem(pen=pen) self.curves.append( (1+idx, idx, curve) ) # datasets 2+, vertical offset by index @@ -231,9 +231,12 @@ class MainWindow(QtWidgets.QMainWindow): def make_dark_QPalette(self): # color definitions match QDarkstyle BLACK = QtGui.QColor('#000000') - BG_LIGHT = QtGui.QColor('#505F69') - BG_NORMAL = QtGui.QColor('#32414B') - BG_DARK = QtGui.QColor('#19232D') + # BG_LIGHT = QtGui.QColor('#505F69') + # BG_NORMAL = QtGui.QColor('#32414B') + # BG_DARK = QtGui.QColor('#19232D') + BG_LIGHT = QtGui.QColor('#32414B') + BG_NORMAL = QtGui.QColor('#212a31') + BG_DARK = QtGui.QColor('#101518') FG_LIGHT = QtGui.QColor('#F0F0F0') FG_NORMAL = QtGui.QColor('#AAAAAA') FG_DARK = QtGui.QColor('#787878') @@ -296,22 +299,27 @@ class MainWindow(QtWidgets.QMainWindow): l_layout.setContentsMargins(0,0,0,0) l_layout.setSpacing(1) row_idx = 0 - - label = QtWidgets.QLabel('Select a palette:') + + label = QtWidgets.QLabel('System style:') l_layout.addWidget( label, row_idx,0, 1,2 ) + + label = QtWidgets.QLabel('Select a palette:') + l_layout.addWidget( label, row_idx,2, 1,2 ) row_idx += 1 - + + btn = QtWidgets.QPushButton('dark GUI') + btn.setCheckable(True) + btn.setChecked(False) + btn.clicked.connect(self.handle_dark_button) + box = QtWidgets.QComboBox() for text, identifier, args in self.palette_options: del identifier, args # not needed here box.addItem(text) box.activated.connect(self.handle_palette_select) - btn = QtWidgets.QPushButton('dark GUI') - btn.setCheckable(True) - btn.setChecked(False) - btn.clicked.connect(self.handle_dark_button) - l_layout.addWidget( box, row_idx,0, 1,2 ) - l_layout.addWidget( btn, row_idx,3, 1,1 ) + + l_layout.addWidget( box, row_idx,2, 1,2 ) + l_layout.addWidget( btn, row_idx,0, 1,2 ) ui['dark'] = btn row_idx += 1 diff --git a/examples/PanningPlot.py b/examples/PanningPlot.py index 874bf330..161fd1f1 100644 --- a/examples/PanningPlot.py +++ b/examples/PanningPlot.py @@ -30,8 +30,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(50) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/PlotAutoRange.py b/examples/PlotAutoRange.py index a15f2e3f..c6b6077b 100644 --- a/examples/PlotAutoRange.py +++ b/examples/PlotAutoRange.py @@ -40,10 +40,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(50) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() - + pg.mkQApp().exec_() diff --git a/examples/PlotSpeedTest.py b/examples/PlotSpeedTest.py index f5d630b4..90d8f7ee 100644 --- a/examples/PlotSpeedTest.py +++ b/examples/PlotSpeedTest.py @@ -48,10 +48,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(0) - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/PlotWidget.py b/examples/PlotWidget.py index a3f77b6d..ea8e3feb 100644 --- a/examples/PlotWidget.py +++ b/examples/PlotWidget.py @@ -86,8 +86,5 @@ line = pg.InfiniteLine(angle=90, movable=True) pw3.addItem(line) line.setBounds([0,200]) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/Plotting.py b/examples/Plotting.py index c3831c0a..ea831110 100644 --- a/examples/Plotting.py +++ b/examples/Plotting.py @@ -95,8 +95,5 @@ lr.sigRegionChanged.connect(updatePlot) p9.sigXRangeChanged.connect(updateRegion) updatePlot() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ROIExamples.py b/examples/ROIExamples.py index abea9220..23721f0f 100644 --- a/examples/ROIExamples.py +++ b/examples/ROIExamples.py @@ -157,15 +157,5 @@ def remove(): v4.removeItem(r4) r4.sigRemoveRequested.connect(remove) - - - - - - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ROItypes.py b/examples/ROItypes.py index 6d01667e..b65e544f 100644 --- a/examples/ROItypes.py +++ b/examples/ROItypes.py @@ -118,10 +118,5 @@ t = QtCore.QTimer() t.timeout.connect(updateImage) t.start(50) - - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/RemoteGraphicsView.py b/examples/RemoteGraphicsView.py index 2b74a8c6..5810f222 100644 --- a/examples/RemoteGraphicsView.py +++ b/examples/RemoteGraphicsView.py @@ -26,9 +26,5 @@ plt = v.pg.PlotItem() v.setCentralItem(plt) plt.plot([1,4,2,3,6,2,3,4,2,3], pen='g') - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/RemoteSpeedTest.py b/examples/RemoteSpeedTest.py index 8d8dd210..cd62e173 100644 --- a/examples/RemoteSpeedTest.py +++ b/examples/RemoteSpeedTest.py @@ -72,10 +72,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(0) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ScaleBar.py b/examples/ScaleBar.py index 94110a5d..7c7e0cd4 100644 --- a/examples/ScaleBar.py +++ b/examples/ScaleBar.py @@ -24,8 +24,5 @@ scale = pg.ScaleBar(size=0.1) scale.setParentItem(vb) scale.anchor((1, 1), (1, 1), offset=(-20, -20)) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ScatterPlot.py b/examples/ScatterPlot.py index 070f21c5..1e3e9409 100644 --- a/examples/ScatterPlot.py +++ b/examples/ScatterPlot.py @@ -134,9 +134,5 @@ s4.addPoints( w4.addItem(s4) s4.sigClicked.connect(clicked) - -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ScatterPlotSpeedTest.py b/examples/ScatterPlotSpeedTest.py index e0edefca..4c7f89c7 100644 --- a/examples/ScatterPlotSpeedTest.py +++ b/examples/ScatterPlotSpeedTest.py @@ -128,10 +128,5 @@ param.child('paused').sigValueChanged.connect(lambda _, v: timer.stop() if v els timer.timeout.connect(update) timer.start(0) - -# Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ScatterPlotWidget.py b/examples/ScatterPlotWidget.py index f3766d56..b2d95340 100644 --- a/examples/ScatterPlotWidget.py +++ b/examples/ScatterPlotWidget.py @@ -62,9 +62,5 @@ spw.setFields([ spw.setData(data) spw.show() - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/SimplePlot.py b/examples/SimplePlot.py index 03ee2204..8a190689 100644 --- a/examples/SimplePlot.py +++ b/examples/SimplePlot.py @@ -5,8 +5,5 @@ import pyqtgraph.exporters import numpy as np plt = pg.plot(np.random.normal(size=100), title="Simplest possible plotting example") -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if sys.flags.interactive != 1 or not hasattr(pg.QtCore, 'PYQT_VERSION'): - pg.QtGui.QApplication.exec_() + pg.mkQApp().exec_() diff --git a/examples/SpinBox.py b/examples/SpinBox.py index 88366cdf..e7bff2e4 100644 --- a/examples/SpinBox.py +++ b/examples/SpinBox.py @@ -128,8 +128,5 @@ layout.addWidget(changedLabel, 2, 1) #s.editingFinished.disconnect() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/Symbols.py b/examples/Symbols.py index 3a683e6d..9eaf7371 100755 --- a/examples/Symbols.py +++ b/examples/Symbols.py @@ -35,8 +35,5 @@ plot.plot([13, 14, 15, 16, 17], pen=(187, 26, 95), symbolBrush=(187, 26, 95), sy plot.plot([14, 15, 16, 17, 18], pen=(248, 187, 208), symbolBrush=(248, 187, 208), symbolPen='w', symbol='arrow_right', symbolSize=22, name="symbol='arrow_right'") plot.setXRange(-2, 4) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/TableWidget.py b/examples/TableWidget.py index 0fb1aae4..8bc86e34 100644 --- a/examples/TableWidget.py +++ b/examples/TableWidget.py @@ -26,9 +26,5 @@ data = np.array([ w.setData(data) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/TreeWidget.py b/examples/TreeWidget.py index 56940316..feea83a7 100644 --- a/examples/TreeWidget.py +++ b/examples/TreeWidget.py @@ -48,8 +48,5 @@ i2.addChild(i22) b1 = QtGui.QPushButton("Button") w.setItemWidget(i1, 1, b1) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/VideoSpeedTest.py b/examples/VideoSpeedTest.py index c8c05d8e..37d854e7 100644 --- a/examples/VideoSpeedTest.py +++ b/examples/VideoSpeedTest.py @@ -18,6 +18,8 @@ import pyqtgraph as pg import pyqtgraph.ptime as ptime from pyqtgraph.Qt import QtGui, QtCore, QT_LIB +pg.setConfigOption('imageAxisOrder', 'row-major') + import importlib ui_template = importlib.import_module(f'VideoTemplate_{QT_LIB.lower()}') @@ -157,7 +159,7 @@ def mkData(): dt = xp.uint16 loc = 4096 scale = 1024 - mx = 2**16 + mx = 2**16 - 1 elif cacheKey[0] == 'float': dt = xp.float32 loc = 1.0 @@ -165,19 +167,25 @@ def mkData(): mx = 1.0 else: raise ValueError(f"unable to handle dtype: {cacheKey[0]}") - + + chan_shape = (height, width) if ui.rgbCheck.isChecked(): - data = xp.random.normal(size=(frames,width,height,3), loc=loc, scale=scale) - data = pg.gaussianFilter(data, (0, 6, 6, 0)) + frame_shape = chan_shape + (3,) else: - data = xp.random.normal(size=(frames,width,height), loc=loc, scale=scale) - data = pg.gaussianFilter(data, (0, 6, 6)) - if cacheKey[0] != 'float': - data = xp.clip(data, 0, mx) - data = data.astype(dt) - data[:, 10, 10:50] = mx - data[:, 9:12, 48] = mx - data[:, 8:13, 47] = mx + frame_shape = chan_shape + data = xp.empty((frames,) + frame_shape, dtype=dt) + view = data.reshape((-1,) + chan_shape) + for idx in range(view.shape[0]): + subdata = xp.random.normal(loc=loc, scale=scale, size=chan_shape) + # note: gaussian filtering has been removed as it slows down array + # creation greatly. + if cacheKey[0] != 'float': + xp.clip(subdata, 0, mx, out=subdata) + view[idx] = subdata + + data[:, 10:50, 10] = mx + data[:, 48, 9:12] = mx + data[:, 47, 8:13] = mx cache = {cacheKey: data} # clear to save memory (but keep one to prevent unnecessary regeneration) data = cache[cacheKey] @@ -272,10 +280,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(0) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ViewBox.py b/examples/ViewBox.py index f9dbac43..eca55ef5 100644 --- a/examples/ViewBox.py +++ b/examples/ViewBox.py @@ -93,8 +93,5 @@ t = QtCore.QTimer() t.timeout.connect(updateData) t.start(50) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ViewBoxFeatures.py b/examples/ViewBoxFeatures.py index 5757924b..239b13f2 100644 --- a/examples/ViewBoxFeatures.py +++ b/examples/ViewBoxFeatures.py @@ -81,10 +81,5 @@ v6.setAutoVisible(x=False, y=True) l6 = pg.PlotDataItem(y) v6.addItem(l6) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/ViewLimits.py b/examples/ViewLimits.py index c8f0dd21..4f2e85fc 100644 --- a/examples/ViewLimits.py +++ b/examples/ViewLimits.py @@ -7,9 +7,5 @@ import numpy as np plt = pg.plot(np.random.normal(size=100), title="View limit example") plt.centralWidget.vb.setLimits(xMin=-20, xMax=120, minXRange=5, maxXRange=100) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'): - pg.QtGui.QApplication.exec_() + pg.mkQApp().exec_() diff --git a/examples/beeswarm.py b/examples/beeswarm.py index 48ee4236..3f119eb2 100644 --- a/examples/beeswarm.py +++ b/examples/beeswarm.py @@ -31,8 +31,5 @@ err = pg.ErrorBarItem(x=np.arange(4), y=data.mean(axis=1), height=data.std(axis= win.addItem(err) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/colorMaps.py b/examples/colorMaps.py index 4099f2fc..34008d68 100644 --- a/examples/colorMaps.py +++ b/examples/colorMaps.py @@ -1,14 +1,8 @@ # -*- coding: utf-8 -*- """ -This example demonstrates the use of ImageView, which is a high-level widget for -displaying and analyzing 2D and 3D data. ImageView provides: - - 1. A zoomable region (ViewBox) for displaying the image - 2. A combination histogram and gradient editor (HistogramLUTItem) for - controlling the visual appearance of the image - 3. A timeline for selecting the currently displayed frame (for 3D data only). - 4. Tools for very basic analysis of image data (see ROI and Norm buttons) - +This example demonstrates generating ColorMap objects from external data. +It displays the full list of color maps available as local files or by import +from Matplotlib or ColorCET. """ ## Add path to library (just for examples; you do not need this) import initExample @@ -111,8 +105,5 @@ for map_name in list_of_maps: lw.setFixedHeight(num_bars * (height+5) ) -## Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/contextMenu.py b/examples/contextMenu.py index 904b9030..adc6c7b7 100644 --- a/examples/contextMenu.py +++ b/examples/contextMenu.py @@ -135,8 +135,5 @@ box2.setParentItem(box1) box2.setPos(5, 5) box2.setScale(0.2) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/crosshair.py b/examples/crosshair.py index 150ed53d..15102adb 100644 --- a/examples/crosshair.py +++ b/examples/crosshair.py @@ -80,8 +80,5 @@ proxy = pg.SignalProxy(p1.scene().sigMouseMoved, rateLimit=60, slot=mouseMoved) #p1.scene().sigMouseMoved.connect(mouseMoved) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/customGraphicsItem.py b/examples/customGraphicsItem.py index 9723b83a..bc006a23 100644 --- a/examples/customGraphicsItem.py +++ b/examples/customGraphicsItem.py @@ -54,8 +54,5 @@ plt = pg.plot() plt.addItem(item) plt.setWindowTitle('pyqtgraph example: customGraphicsItem') -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/customPlot.py b/examples/customPlot.py index 007ec691..ec632f73 100644 --- a/examples/customPlot.py +++ b/examples/customPlot.py @@ -42,7 +42,7 @@ class CustomTickSliderItem(pg.TickSliderItem): self.removeTick(tick) for pos in ticks: - tickItem = self.addTick(pos, movable=False, color="333333") + tickItem = self.addTick(pos, movable=False, color="#333333") self.all_ticks[pos] = tickItem self.updateRange(None, self._range) @@ -91,8 +91,5 @@ pw.setWindowTitle('pyqtgraph example: customPlot') r = pg.PolyLineROI([(0,0), (10, 10)]) pw.addItem(r) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/designerExample.py b/examples/designerExample.py index 812eff6b..2c33bddd 100644 --- a/examples/designerExample.py +++ b/examples/designerExample.py @@ -38,9 +38,5 @@ class MainWindow(TemplateBaseClass): win = MainWindow() - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/dockarea.py b/examples/dockarea.py index f87a8da3..a678ee02 100644 --- a/examples/dockarea.py +++ b/examples/dockarea.py @@ -111,8 +111,5 @@ win.show() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/fractal.py b/examples/fractal.py index 3f008141..05cd2409 100644 --- a/examples/fractal.py +++ b/examples/fractal.py @@ -56,7 +56,7 @@ def update(): p2 = pts[i+1] v2 = p2 - p1 t = p1 - pts[0] - r = v2.angle(v1) + r = v1.angle(v2) s = v2.length() / l1 trs.append(pg.SRTTransform({'pos': t, 'scale': (s, s), 'angle': r})) @@ -106,10 +106,5 @@ depthSpin.valueChanged.connect(update) # Initialize update() - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() - \ No newline at end of file + pg.mkQApp().exec_() diff --git a/examples/hdf5.py b/examples/hdf5.py index 3cd5de29..338a44ff 100644 --- a/examples/hdf5.py +++ b/examples/hdf5.py @@ -141,15 +141,5 @@ curve = HDF5Plot() curve.setHDF5(f['data']) plt.addItem(curve) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - - - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() - - - - + pg.mkQApp().exec_() diff --git a/examples/histogram.py b/examples/histogram.py index 53d4fdad..d91631d2 100644 --- a/examples/histogram.py +++ b/examples/histogram.py @@ -29,8 +29,5 @@ y = pg.pseudoScatter(vals, spacing=0.15) #plt2.plot(vals, y, pen=None, symbol='o', symbolSize=5) plt2.plot(vals, y, pen=None, symbol='o', symbolSize=5, symbolPen=(255,255,255,200), symbolBrush=(0,0,255,150)) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/imageAnalysis.py b/examples/imageAnalysis.py index d6febb14..0a821420 100644 --- a/examples/imageAnalysis.py +++ b/examples/imageAnalysis.py @@ -113,9 +113,5 @@ def imageHoverEvent(event): # but it works for a very simple use like this. img.hoverEvent = imageHoverEvent - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/infiniteline_performance.py b/examples/infiniteline_performance.py index 9682080e..86d62b62 100644 --- a/examples/infiniteline_performance.py +++ b/examples/infiniteline_performance.py @@ -44,9 +44,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(0) - -# Start Qt event loop unless running in interactive mode. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() \ No newline at end of file + pg.mkQApp().exec_() diff --git a/examples/isocurve.py b/examples/isocurve.py index 1ed30591..767107f5 100644 --- a/examples/isocurve.py +++ b/examples/isocurve.py @@ -53,8 +53,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(50) -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/linkedViews.py b/examples/linkedViews.py index 0a387ddf..794ef092 100644 --- a/examples/linkedViews.py +++ b/examples/linkedViews.py @@ -40,10 +40,6 @@ p3.setXLink(p1) p3.setLabel('left', "Label to test offset") #QtGui.QApplication.processEvents() - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/logAxis.py b/examples/logAxis.py index 9a66f114..5d1e179e 100644 --- a/examples/logAxis.py +++ b/examples/logAxis.py @@ -34,9 +34,5 @@ p3.plot(x, y) #p.getAxis('bottom').setLogMode(True) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/multiplePlotSpeedTest.py b/examples/multiplePlotSpeedTest.py index 07df7522..4bc39ed9 100644 --- a/examples/multiplePlotSpeedTest.py +++ b/examples/multiplePlotSpeedTest.py @@ -86,8 +86,5 @@ else: plt.autoRange() -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/multiprocess.py b/examples/multiprocess.py index 2e32b041..9c163e9f 100644 --- a/examples/multiprocess.py +++ b/examples/multiprocess.py @@ -51,9 +51,3 @@ d1 = proc.transfer(np.random.normal(size=1000)) d2 = proc.transfer(np.random.normal(size=1000)) rpg = proc._import('pyqtgraph') plt = rpg.plot(d1+d2) - - -## Start Qt event loop unless running in interactive mode or using pyside. -#import sys -#if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - #QtGui.QApplication.instance().exec_() diff --git a/examples/optics/pyoptic.py b/examples/optics/pyoptic.py index f70edd2a..99221bd7 100644 --- a/examples/optics/pyoptic.py +++ b/examples/optics/pyoptic.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, asin, sin, cos, degrees, sqrt, hypot import pyqtgraph as pg from pyqtgraph.Qt import QtGui, QtCore import numpy as np @@ -49,7 +50,7 @@ class GlassDB: B = list(map(float, [info['B1'], info['B2'], info['B3']])) C = list(map(float, [info['C1'], info['C2'], info['C3']])) w2 = (wl/1000.)**2 - n = np.sqrt(1.0 + (B[0]*w2 / (w2-C[0])) + (B[1]*w2 / (w2-C[1])) + (B[2]*w2 / (w2-C[2]))) + n = sqrt(1.0 + (B[0]*w2 / (w2-C[0])) + (B[1]*w2 / (w2-C[1])) + (B[2]*w2 / (w2-C[2]))) cache[wl] = n return cache[wl] @@ -249,10 +250,14 @@ class Lens(Optic): p1 = surface.mapToItem(ray, p1) rd = ray['dir'] - a1 = np.arctan2(rd[1], rd[0]) - ar = a1 - ai + np.arcsin((np.sin(ai) * ray['ior'] / ior)) + + a1 = atan2(rd[1], rd[0]) + try: + ar = a1 - ai + asin((sin(ai) * ray['ior'] / ior)) + except ValueError: + ar = np.nan ray.setEnd(p1) - dp = Point(np.cos(ar), np.sin(ar)) + dp = Point(cos(ar), sin(ar)) ray = Ray(parent=ray, ior=ior, dir=dp) return [ray] @@ -279,10 +284,10 @@ class Mirror(Optic): if p1 is not None: p1 = surface.mapToItem(ray, p1) rd = ray['dir'] - a1 = np.arctan2(rd[1], rd[0]) - ar = a1 + np.pi - 2*ai + a1 = atan2(rd[1], rd[0]) + ar = a1 + np.pi - 2 * ai ray.setEnd(p1) - dp = Point(np.cos(ar), np.sin(ar)) + dp = Point(cos(ar), sin(ar)) ray = Ray(parent=ray, dir=dp) else: ray.setEnd(None) @@ -374,7 +379,7 @@ class CircleSurface(pg.GraphicsObject): ## half-height of surface can't be larger than radius h2 = min(h2, abs(r)) arc = QtCore.QRectF(0, -r, r*2, r*2) - a1 = np.arcsin(h2/r) * 180. / np.pi + a1 = degrees(asin(h2/r)) a2 = -2*a1 a1 += 180. self.path.arcMoveTo(arc, a1) @@ -406,13 +411,13 @@ class CircleSurface(pg.GraphicsObject): if abs(y) > h: return None, None else: - return (Point(0, y), np.arctan2(dir[1], dir[0])) + return (Point(0, y), atan2(dir[1], dir[0])) else: #print " curve" ## find intersection of circle and line (quadratic formula) dx = dir[0] dy = dir[1] - dr = (dx**2 + dy**2) ** 0.5 + dr = hypot(dx, dy) # length D = p[0] * (p[1]+dy) - (p[0]+dx) * p[1] idr2 = 1.0 / dr**2 disc = r**2 * dr**2 - D**2 @@ -423,8 +428,7 @@ class CircleSurface(pg.GraphicsObject): sgn = -1 else: sgn = 1 - - + br = self.path.boundingRect() x1 = (D*dy + sgn*dx*disc2) * idr2 y1 = (-D*dx + abs(dy)*disc2) * idr2 @@ -436,19 +440,12 @@ class CircleSurface(pg.GraphicsObject): pt = Point(x2, y2) if not br.contains(x2+r, y2): return None, None - raise Exception("No intersection!") - norm = np.arctan2(pt[1], pt[0]) + norm = atan2(pt[1], pt[0]) if r < 0: norm += np.pi - #print " norm:", norm*180/3.1415 dp = p - pt - #print " dp:", dp - ang = np.arctan2(dp[1], dp[0]) - #print " ang:", ang*180/3.1415 - #print " ai:", (ang-norm)*180/3.1415 - - #print " intersection:", pt + ang = atan2(dp[1], dp[0]) return pt + Point(r, 0), ang-norm diff --git a/examples/optics_demos.py b/examples/optics_demos.py index 1f173621..40211af4 100644 --- a/examples/optics_demos.py +++ b/examples/optics_demos.py @@ -159,12 +159,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(40) - - - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/parametertree.py b/examples/parametertree.py index fdd1bdae..6ce54918 100644 --- a/examples/parametertree.py +++ b/examples/parametertree.py @@ -69,7 +69,7 @@ params = [ {'name': 'List', 'type': 'list', 'values': [1,2,3], 'value': 2}, {'name': 'Named List', 'type': 'list', 'values': {"one": 1, "two": "twosies", "three": [3,3,3]}, 'value': 2}, {'name': 'Boolean', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"}, - {'name': 'Color', 'type': 'color', 'value': "FF0", 'tip': "This is a color button"}, + {'name': 'Color', 'type': 'color', 'value': "#FF0", 'tip': "This is a color button"}, {'name': 'Gradient', 'type': 'colormap'}, {'name': 'Subgroup', 'type': 'group', 'children': [ {'name': 'Sub-param 1', 'type': 'int', 'value': 10}, @@ -178,9 +178,5 @@ win.show() s = p.saveState() p.restoreState(s) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/relativity/relativity.py b/examples/relativity/relativity.py index 64a67119..c255fb00 100644 --- a/examples/relativity/relativity.py +++ b/examples/relativity/relativity.py @@ -750,7 +750,7 @@ class ClockItem(pg.ItemGroup): #pass if __name__ == '__main__': - pg.mkQApp() + app = pg.mkQApp() #import pyqtgraph.console #cw = pyqtgraph.console.ConsoleWidget() #cw.show() @@ -759,10 +759,5 @@ if __name__ == '__main__': win.setWindowTitle("Relativity!") win.show() win.resize(1100,700) - - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() - - - #win.params.param('Objects').restoreState(state, removeChildren=False) + app.exec_() diff --git a/examples/relativity_demo.py b/examples/relativity_demo.py index 24a1f476..f278a26a 100644 --- a/examples/relativity_demo.py +++ b/examples/relativity_demo.py @@ -16,8 +16,5 @@ win.resize(1100,700) win.show() win.loadPreset(None, 'Twin Paradox (grid)') -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(pg.QtCore, 'PYQT_VERSION'): - pg.QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/scrollingPlots.py b/examples/scrollingPlots.py index d370aa46..def2594e 100644 --- a/examples/scrollingPlots.py +++ b/examples/scrollingPlots.py @@ -109,10 +109,5 @@ timer = pg.QtCore.QTimer() timer.timeout.connect(update) timer.start(50) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/template.py b/examples/template.py index d39c6fb8..a462ac91 100644 --- a/examples/template.py +++ b/examples/template.py @@ -15,8 +15,5 @@ app = mkQApp() # win.setWindowTitle('pyqtgraph example: ____') -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/test_ExampleApp.py b/examples/test_ExampleApp.py index 7a1696d5..3162ffcf 100644 --- a/examples/test_ExampleApp.py +++ b/examples/test_ExampleApp.py @@ -7,7 +7,5 @@ from examples.ExampleApp import ExampleLoader loader = ExampleLoader() -if __name__ == "__main__": - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() \ No newline at end of file +if __name__ == '__main__': + pg.mkQApp().exec_() diff --git a/examples/test_examples.py b/examples/test_examples.py index 4339875c..5b8aa07f 100644 --- a/examples/test_examples.py +++ b/examples/test_examples.py @@ -58,7 +58,7 @@ installedFrontends = sorted([ darwin_opengl_broken = (platform.system() == "Darwin" and tuple(map(int, platform.mac_ver()[0].split("."))) >= (10, 16) and - (sys.version_info <= (3, 8, 7) or (3, 9) <= sys.version_info < (3, 9, 1))) + sys.version_info < (3, 9, 1)) darwin_opengl_reason = ("pyopenGL cannot find openGL library on big sur: " "https://github.com/python/cpython/pull/21241") diff --git a/examples/text.py b/examples/text.py index bf9bd6b9..50c87159 100644 --- a/examples/text.py +++ b/examples/text.py @@ -52,10 +52,5 @@ timer = QtCore.QTimer() timer.timeout.connect(update) timer.start(10) - - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/examples/utils.py b/examples/utils.py index 40b363b0..a7cfa532 100644 --- a/examples/utils.py +++ b/examples/utils.py @@ -11,6 +11,7 @@ examples = OrderedDict([ ('Plot Customization', 'customPlot.py'), ('Timestamps on x axis', 'DateAxisItem.py'), ('Image Analysis', 'imageAnalysis.py'), + ('Color Maps', 'colorMaps.py'), ('ViewBox Features', Namespace(filename='ViewBoxFeatures.py', recommended=True)), ('Dock widgets', 'dockarea.py'), ('Console', 'ConsoleWidget.py'), @@ -33,12 +34,14 @@ examples = OrderedDict([ ('GraphicsItems', OrderedDict([ ('Scatter Plot', 'ScatterPlot.py'), #('PlotItem', 'PlotItem.py'), + ('InfiniteLine', 'InfiniteLine.py'), ('IsocurveItem', 'isocurve.py'), ('GraphItem', 'GraphItem.py'), ('ErrorBarItem', 'ErrorBarItem.py'), ('FillBetweenItem', 'FillBetweenItem.py'), ('ImageItem - video', 'ImageItem.py'), ('ImageItem - draw', 'Draw.py'), + ('ColorBarItem','ColorBarItem.py'), ('Non-uniform Image', 'NonUniformImage.py'), ('Region-of-Interest', 'ROIExamples.py'), ('Bar Graph', 'BarGraphItem.py'), @@ -96,7 +99,6 @@ others = dict([ ('MultiplePlotAxes', 'MultiplePlotAxes.py'), ('ROItypes', 'ROItypes.py'), ('ScaleBar', 'ScaleBar.py'), - ('InfiniteLine', 'InfiniteLine.py'), ('ViewBox', 'ViewBox.py'), ('GradientEditor', 'GradientEditor.py'), ('GLBarGraphItem', 'GLBarGraphItem.py'), diff --git a/examples/verlet_chain_demo.py b/examples/verlet_chain_demo.py index 1197344d..d8aac5ca 100644 --- a/examples/verlet_chain_demo.py +++ b/examples/verlet_chain_demo.py @@ -118,9 +118,5 @@ timer = pg.QtCore.QTimer() timer.timeout.connect(update) timer.start(16) - -## Start Qt event loop unless running in interactive mode or using pyside. if __name__ == '__main__': - import sys - if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): - QtGui.QApplication.instance().exec_() + pg.mkQApp().exec_() diff --git a/pyqtgraph/GraphicsScene/exportDialogTemplate.ui b/pyqtgraph/GraphicsScene/exportDialogTemplate.ui index eacacd88..2d0308ca 100644 --- a/pyqtgraph/GraphicsScene/exportDialogTemplate.ui +++ b/pyqtgraph/GraphicsScene/exportDialogTemplate.ui @@ -62,6 +62,9 @@ + + 2 + false diff --git a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt5.py b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt5.py index 418fd0f0..de146c95 100644 --- a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt5.py +++ b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt5.py @@ -1,14 +1,16 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file './pyqtgraph/GraphicsScene/exportDialogTemplate.ui' +# Form implementation generated from reading ui file '.\exportDialogTemplate.ui' # -# Created: Wed Mar 26 15:09:29 2014 -# by: PyQt5 UI code generator 5.0.1 +# Created by: PyQt5 UI code generator 5.15.4 # -# WARNING! All changes made in this file will be lost! +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + from PyQt5 import QtCore, QtGui, QtWidgets + class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") @@ -37,6 +39,7 @@ class Ui_Form(object): self.closeBtn.setObjectName("closeBtn") self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1) self.paramTree = ParameterTree(Form) + self.paramTree.setColumnCount(2) self.paramTree.setObjectName("paramTree") self.paramTree.headerItem().setText(0, "1") self.paramTree.header().setVisible(False) @@ -60,5 +63,4 @@ class Ui_Form(object): self.closeBtn.setText(_translate("Form", "Close")) self.label_3.setText(_translate("Form", "Export options")) self.copyBtn.setText(_translate("Form", "Copy")) - from ..parametertree import ParameterTree diff --git a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py index 55cbe3a8..fa7aa72b 100644 --- a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py +++ b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyqt6.py @@ -1,6 +1,6 @@ -# Form implementation generated from reading ui file 'pyqtgraph\GraphicsScene\exportDialogTemplate.ui' +# Form implementation generated from reading ui file '.\exportDialogTemplate.ui' # -# Created by: PyQt6 UI code generator 6.0.0 +# Created by: PyQt6 UI code generator 6.0.3 # # WARNING: Any manual changes made to this file will be lost when pyuic6 is # run again. Do not edit this file unless you know what you are doing. @@ -37,6 +37,7 @@ class Ui_Form(object): self.closeBtn.setObjectName("closeBtn") self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1) self.paramTree = ParameterTree(Form) + self.paramTree.setColumnCount(2) self.paramTree.setObjectName("paramTree") self.paramTree.headerItem().setText(0, "1") self.paramTree.header().setVisible(False) diff --git a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside2.py b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside2.py index 6c0fec47..9cf5266d 100644 --- a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside2.py +++ b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside2.py @@ -1,63 +1,95 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file 'exportDialogTemplate.ui' -# -# Created: Sun Sep 18 19:19:58 2016 -# by: pyside2-uic running on PySide2 2.0.0~alpha0 -# -# WARNING! All changes made in this file will be lost! +################################################################################ +## Form generated from reading UI file 'exportDialogTemplate.ui' +## +## Created by: Qt User Interface Compiler version 5.15.2 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide2.QtCore import * +from PySide2.QtGui import * +from PySide2.QtWidgets import * + +from ..parametertree import ParameterTree -from PySide2 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): - Form.setObjectName("Form") + if not Form.objectName(): + Form.setObjectName(u"Form") Form.resize(241, 367) - self.gridLayout = QtWidgets.QGridLayout(Form) + self.gridLayout = QGridLayout(Form) self.gridLayout.setSpacing(0) - self.gridLayout.setObjectName("gridLayout") - self.label = QtWidgets.QLabel(Form) - self.label.setObjectName("label") + self.gridLayout.setObjectName(u"gridLayout") + self.label = QLabel(Form) + self.label.setObjectName(u"label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 3) - self.itemTree = QtWidgets.QTreeWidget(Form) - self.itemTree.setObjectName("itemTree") - self.itemTree.headerItem().setText(0, "1") + + self.itemTree = QTreeWidget(Form) + __qtreewidgetitem = QTreeWidgetItem() + __qtreewidgetitem.setText(0, u"1"); + self.itemTree.setHeaderItem(__qtreewidgetitem) + self.itemTree.setObjectName(u"itemTree") self.itemTree.header().setVisible(False) + self.gridLayout.addWidget(self.itemTree, 1, 0, 1, 3) - self.label_2 = QtWidgets.QLabel(Form) - self.label_2.setObjectName("label_2") + + self.label_2 = QLabel(Form) + self.label_2.setObjectName(u"label_2") + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 3) - self.formatList = QtWidgets.QListWidget(Form) - self.formatList.setObjectName("formatList") + + self.formatList = QListWidget(Form) + self.formatList.setObjectName(u"formatList") + self.gridLayout.addWidget(self.formatList, 3, 0, 1, 3) - self.exportBtn = QtWidgets.QPushButton(Form) - self.exportBtn.setObjectName("exportBtn") + + self.exportBtn = QPushButton(Form) + self.exportBtn.setObjectName(u"exportBtn") + self.gridLayout.addWidget(self.exportBtn, 6, 1, 1, 1) - self.closeBtn = QtWidgets.QPushButton(Form) - self.closeBtn.setObjectName("closeBtn") + + self.closeBtn = QPushButton(Form) + self.closeBtn.setObjectName(u"closeBtn") + self.gridLayout.addWidget(self.closeBtn, 6, 2, 1, 1) + self.paramTree = ParameterTree(Form) - self.paramTree.setObjectName("paramTree") - self.paramTree.headerItem().setText(0, "1") + __qtreewidgetitem1 = QTreeWidgetItem() + __qtreewidgetitem1.setText(0, u"1"); + self.paramTree.setHeaderItem(__qtreewidgetitem1) + self.paramTree.setObjectName(u"paramTree") + self.paramTree.setColumnCount(2) self.paramTree.header().setVisible(False) + self.gridLayout.addWidget(self.paramTree, 5, 0, 1, 3) - self.label_3 = QtWidgets.QLabel(Form) - self.label_3.setObjectName("label_3") + + self.label_3 = QLabel(Form) + self.label_3.setObjectName(u"label_3") + self.gridLayout.addWidget(self.label_3, 4, 0, 1, 3) - self.copyBtn = QtWidgets.QPushButton(Form) - self.copyBtn.setObjectName("copyBtn") + + self.copyBtn = QPushButton(Form) + self.copyBtn.setObjectName(u"copyBtn") + self.gridLayout.addWidget(self.copyBtn, 6, 0, 1, 1) + self.retranslateUi(Form) - QtCore.QMetaObject.connectSlotsByName(Form) + + QMetaObject.connectSlotsByName(Form) + # setupUi def retranslateUi(self, Form): - Form.setWindowTitle(QtWidgets.QApplication.translate("Form", "Export", None, -1)) - self.label.setText(QtWidgets.QApplication.translate("Form", "Item to export:", None, -1)) - self.label_2.setText(QtWidgets.QApplication.translate("Form", "Export format", None, -1)) - self.exportBtn.setText(QtWidgets.QApplication.translate("Form", "Export", None, -1)) - self.closeBtn.setText(QtWidgets.QApplication.translate("Form", "Close", None, -1)) - self.label_3.setText(QtWidgets.QApplication.translate("Form", "Export options", None, -1)) - self.copyBtn.setText(QtWidgets.QApplication.translate("Form", "Copy", None, -1)) + Form.setWindowTitle(QCoreApplication.translate("Form", u"Export", None)) + self.label.setText(QCoreApplication.translate("Form", u"Item to export:", None)) + self.label_2.setText(QCoreApplication.translate("Form", u"Export format", None)) + self.exportBtn.setText(QCoreApplication.translate("Form", u"Export", None)) + self.closeBtn.setText(QCoreApplication.translate("Form", u"Close", None)) + self.label_3.setText(QCoreApplication.translate("Form", u"Export options", None)) + self.copyBtn.setText(QCoreApplication.translate("Form", u"Copy", None)) + # retranslateUi -from ..parametertree import ParameterTree diff --git a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside6.py b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside6.py index 92d813fd..9e8e0ec5 100644 --- a/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside6.py +++ b/pyqtgraph/GraphicsScene/exportDialogTemplate_pyside6.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'exportDialogTemplate.ui' ## -## Created by: Qt User Interface Compiler version 6.0.0 +## Created by: Qt User Interface Compiler version 6.0.3 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -62,6 +62,7 @@ class Ui_Form(object): __qtreewidgetitem1.setText(0, u"1"); self.paramTree.setHeaderItem(__qtreewidgetitem1) self.paramTree.setObjectName(u"paramTree") + self.paramTree.setColumnCount(2) self.paramTree.header().setVisible(False) self.gridLayout.addWidget(self.paramTree, 5, 0, 1, 3) diff --git a/pyqtgraph/Point.py b/pyqtgraph/Point.py index fea37dda..973226dd 100644 --- a/pyqtgraph/Point.py +++ b/pyqtgraph/Point.py @@ -6,33 +6,29 @@ Distributed under MIT/X11 license. See license.txt for more information. """ from .Qt import QtCore -import numpy as np +from math import atan2, hypot, degrees -def clip(x, mn, mx): - if x > mx: - return mx - if x < mn: - return mn - return x class Point(QtCore.QPointF): """Extension of QPointF which adds a few missing methods.""" + __slots__ = () + def __init__(self, *args): if len(args) == 1: - if isinstance(args[0], QtCore.QSizeF): - QtCore.QPointF.__init__(self, float(args[0].width()), float(args[0].height())) + if isinstance(args[0], (QtCore.QSize, QtCore.QSizeF)): + super().__init__(float(args[0].width()), float(args[0].height())) return - elif isinstance(args[0], float) or isinstance(args[0], int): - QtCore.QPointF.__init__(self, float(args[0]), float(args[0])) + elif isinstance(args[0], (int, float)): + super().__init__(float(args[0]), float(args[0])) return elif hasattr(args[0], '__getitem__'): - QtCore.QPointF.__init__(self, float(args[0][0]), float(args[0][1])) - return + super().__init__(float(args[0][0]), float(args[0][1])) + return elif len(args) == 2: - QtCore.QPointF.__init__(self, args[0], args[1]) + super().__init__(args[0], args[1]) return - QtCore.QPointF.__init__(self, *args) + super().__init__(*args) def __len__(self): return 2 @@ -47,6 +43,10 @@ class Point(QtCore.QPointF): return self.y() else: raise IndexError("Point has no index %s" % str(i)) + + def __iter__(self): + yield(self.x()) + yield(self.y()) def __setitem__(self, i, x): if i == 0: @@ -93,69 +93,67 @@ class Point(QtCore.QPointF): return self._math_('__pow__', a) def _math_(self, op, x): - #print "point math:", op - #try: - #fn = getattr(QtCore.QPointF, op) - #pt = fn(self, x) - #print fn, pt, self, x - #return Point(pt) - #except AttributeError: - x = Point(x) - return Point(getattr(self[0], op)(x[0]), getattr(self[1], op)(x[1])) + if not isinstance(x, QtCore.QPointF): + x = Point(x) + return Point(getattr(self.x(), op)(x.x()), getattr(self.y(), op)(x.y())) def length(self): """Returns the vector length of this Point.""" - try: - return (self[0]**2 + self[1]**2) ** 0.5 - except OverflowError: - try: - return self[1] / np.sin(np.arctan2(self[1], self[0])) - except OverflowError: - return np.inf - + return hypot(self.x(), self.y()) # length + def norm(self): """Returns a vector in the same direction with unit length.""" return self / self.length() - def angle(self, a): - """Returns the angle in degrees between this vector and the vector a.""" - n1 = self.length() - n2 = a.length() - if n1 == 0. or n2 == 0.: - return None - ## Probably this should be done with arctan2 instead.. - ang = np.arccos(clip(self.dot(a) / (n1 * n2), -1.0, 1.0)) ### in radians - c = self.cross(a) - if c > 0: - ang *= -1. - return ang * 180. / np.pi + def angle(self, a, units="degrees"): + """ + Returns the angle in degrees between this vector and the vector a. + + Parameters + ---------- + a : Point, QPointF or QPoint + The Point to return the angle with + units : str, optional + The units with which to compute the angle with, "degrees" or "radians", + default "degrees" + + Returns + ------- + float + The angle between the two points + """ + rads = atan2(a.y(), a.x()) - atan2(self.y(), self.x()) + if units == "radians": + return rads + return degrees(rads) def dot(self, a): """Returns the dot product of a and this Point.""" - a = Point(a) - return self[0]*a[0] + self[1]*a[1] + if not isinstance(a, QtCore.QPointF): + a = Point(a) + return Point.dotProduct(self, a) def cross(self, a): - a = Point(a) - return self[0]*a[1] - self[1]*a[0] + if not isinstance(a, QtCore.QPointF): + a = Point(a) + return self.x() * a.y() - self.y() * a.x() def proj(self, b): """Return the projection of this vector onto the vector b""" - b1 = b / b.length() + b1 = b.norm() return self.dot(b1) * b1 def __repr__(self): - return "Point(%f, %f)" % (self[0], self[1]) - - + return "Point(%f, %f)" % (self.x(), self.y()) + def min(self): - return min(self[0], self[1]) + return min(self.x(), self.y()) def max(self): - return max(self[0], self[1]) + return max(self.x(), self.y()) def copy(self): return Point(self) def toQPoint(self): - return QtCore.QPoint(int(self[0]), int(self[1])) + return self.toPoint() diff --git a/pyqtgraph/Qt.py b/pyqtgraph/Qt.py index 3acded1f..06bcaa2d 100644 --- a/pyqtgraph/Qt.py +++ b/pyqtgraph/Qt.py @@ -376,6 +376,15 @@ if QT_LIB == PYQT6: QtCore.QEvent.type = new_method del new_method + # PyQt6 6.1 renames some enums and flags to be in line with the other bindings. + # "Alignment" and "Orientations" are PyQt6 6.0 and are used in the generated + # ui files. Pending a regeneration of the template files, which would mean a + # drop in support for PyQt6 6.0, provide the old names for PyQt6 6.1. + # This is strictly a temporary private shim. Do not depend on it in your code. + if hasattr(QtCore.Qt, 'AlignmentFlag') and not hasattr(QtCore.Qt, 'Alignment'): + QtCore.Qt.Alignment = QtCore.Qt.AlignmentFlag + if hasattr(QtCore.Qt, 'Orientation') and not hasattr(QtCore.Qt, 'Orientations'): + QtCore.Qt.Orientations = QtCore.Qt.Orientation # USE_XXX variables are deprecated USE_PYSIDE = QT_LIB == PYSIDE diff --git a/pyqtgraph/SRTTransform.py b/pyqtgraph/SRTTransform.py index 35ec0625..e40fa40f 100644 --- a/pyqtgraph/SRTTransform.py +++ b/pyqtgraph/SRTTransform.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, degrees from .Qt import QtCore, QtGui from .Point import Point import numpy as np @@ -65,8 +66,7 @@ class SRTTransform(QtGui.QTransform): dp3 = Point(p3-p1) ## detect flipped axes - if dp2.angle(dp3) > 0: - #da = 180 + if dp3.angle(dp2, units="radians") > 0: da = 0 sy = -1.0 else: @@ -76,7 +76,7 @@ class SRTTransform(QtGui.QTransform): self._state = { 'pos': Point(p1), 'scale': Point(dp2.length(), dp3.length() * sy), - 'angle': (np.arctan2(dp2[1], dp2[0]) * 180. / np.pi) + da + 'angle': degrees(atan2(dp2[1], dp2[0])) + da } self.update() diff --git a/pyqtgraph/SRTTransform3D.py b/pyqtgraph/SRTTransform3D.py index 7d458edd..2fd3ff87 100644 --- a/pyqtgraph/SRTTransform3D.py +++ b/pyqtgraph/SRTTransform3D.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, degrees from .Qt import QtCore, QtGui from .Vector import Vector from .Transform3D import Transform3D @@ -164,7 +165,7 @@ class SRTTransform3D(Transform3D): sin = (r-r.T)[rInd] / (2. * sign * axis[axisInd]) ## finally, we get the complete angle from arctan(sin/cos) - self._state['angle'] = np.arctan2(sin, cos) * 180 / np.pi + self._state['angle'] = degrees(atan2(sin, cos)) if self._state['angle'] == 0: self._state['axis'] = (0,0,1) diff --git a/pyqtgraph/Vector.py b/pyqtgraph/Vector.py index a9d28e41..a64e8968 100644 --- a/pyqtgraph/Vector.py +++ b/pyqtgraph/Vector.py @@ -4,9 +4,9 @@ Vector.py - Extension of QVector3D which adds a few missing methods. Copyright 2010 Luke Campagnola Distributed under MIT/X11 license. See license.txt for more information. """ - +from math import acos, degrees from .Qt import QtGui, QtCore, QT_LIB -import numpy as np +from . import functions as fn class Vector(QtGui.QVector3D): """Extension of QVector3D which adds a few helpful methods.""" @@ -88,11 +88,11 @@ class Vector(QtGui.QVector3D): if n1 == 0. or n2 == 0.: return None ## Probably this should be done with arctan2 instead.. - ang = np.arccos(np.clip(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians + rads = acos(fn.clip_scalar(QtGui.QVector3D.dotProduct(self, a) / (n1 * n2), -1.0, 1.0)) ### in radians # c = self.crossProduct(a) # if c > 0: # ang *= -1. - return ang * 180. / np.pi + return degrees(rads) def __abs__(self): return Vector(abs(self.x()), abs(self.y()), abs(self.z())) diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py index 69dc118a..e95340aa 100644 --- a/pyqtgraph/__init__.py +++ b/pyqtgraph/__init__.py @@ -4,7 +4,7 @@ PyQtGraph - Scientific Graphics and GUI Library for Python www.pyqtgraph.org """ -__version__ = '0.11.1.dev0' +__version__ = '0.12.1' ### import all the goodies and add some helper functions for easy CLI use @@ -36,8 +36,6 @@ if 'linux' in sys.platform: ## linux has numerous bugs in opengl implementation useOpenGL = False elif 'darwin' in sys.platform: ## openGL can have a major impact on mac, but also has serious bugs useOpenGL = False - if QtGui.QApplication.instance() is not None: - print('Warning: QApplication was created before pyqtgraph was imported; there may be problems.') else: useOpenGL = False ## on windows there's a more even performance / bugginess tradeoff. @@ -49,8 +47,6 @@ CONFIG_OPTIONS = { 'background': 'k', ## default background for GraphicsView 'antialias': False, 'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets - 'useWeave': False, ## Use weave to speed up some operations, if it is available - 'weaveDebug': False, ## Print full error message if weave compile fails 'exitCleanup': True, ## Attempt to work around some exit crash bugs in PyQt and PySide 'enableExperimental': False, ## Enable experimental features (the curious can search for this key in the code) 'crashWarning': False, # If True, print warnings about situations that may result in a crash @@ -60,6 +56,7 @@ CONFIG_OPTIONS = { # The default is 'col-major' for backward compatibility, but this may # change in the future. 'useCupy': False, # When True, attempt to use cupy ( currently only with ImageItem and related functions ) + 'useNumba': False, # When True, use numba } # def setConfigOption(opt, value): @@ -232,6 +229,7 @@ from .graphicsItems.GraphicsWidgetAnchor import * from .graphicsItems.PlotCurveItem import * from .graphicsItems.ButtonItem import * from .graphicsItems.GradientEditorItem import * +from .graphicsItems.ColorBarItem import * from .graphicsItems.MultiPlotItem import * from .graphicsItems.ErrorBarItem import * from .graphicsItems.IsocurveItem import * @@ -239,7 +237,8 @@ from .graphicsItems.LinearRegionItem import * from .graphicsItems.FillBetweenItem import * from .graphicsItems.LegendItem import * from .graphicsItems.ScatterPlotItem import * -from .graphicsItems.ItemGroup import * +from .graphicsItems.ItemGroup import * +from .graphicsItems.TargetItem import * from .widgets.MultiPlotWidget import * from .widgets.ScatterPlotWidget import * @@ -284,10 +283,8 @@ from .colormap import * from .ptime import time from .Qt import isQObjectAlive from .ThreadsafeTimer import * -from .namedPen import * -from .namedBrush import * from .palette import * -from . import namedColorManager +from . import colorRegistry def setConfigOption(opt, value): @@ -298,7 +295,7 @@ def setConfigOption(opt, value): # Intercept background / foreground updates and manually apply them to the palette: if opt in ('background', 'foreground'): - color_dict = functions.NAMED_COLOR_MANAGER.colors() + color_dict = functions.COLOR_REGISTRY.colors() if value in color_dict: qcol = color_dict[value] else: @@ -307,7 +304,7 @@ def setConfigOption(opt, value): color_dict['gr_bg'] = qcol elif opt == 'foreground': color_dict['gr_fg'] = qcol - functions.NAMED_COLOR_MANAGER.redefinePalette(colors=None) + functions.COLOR_REGISTRY.redefinePalette(colors=None) CONFIG_OPTIONS[opt] = value diff --git a/pyqtgraph/canvas/Canvas.py b/pyqtgraph/canvas/Canvas.py index 31a10b37..21df95b5 100644 --- a/pyqtgraph/canvas/Canvas.py +++ b/pyqtgraph/canvas/Canvas.py @@ -113,7 +113,7 @@ class Canvas(QtGui.QWidget): if not self.sizeApplied: self.sizeApplied = True - s = min(self.width(), max(100, min(200, self.width()*0.25))) + s = int( min(self.width(), max(100, min(200, self.width()//4))) ) s2 = self.width()-s self.ui.splitter.setSizes([s2, s]) diff --git a/pyqtgraph/canvas/CanvasItem.py b/pyqtgraph/canvas/CanvasItem.py index 88612055..3883919e 100644 --- a/pyqtgraph/canvas/CanvasItem.py +++ b/pyqtgraph/canvas/CanvasItem.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -import numpy as np -from ..Qt import QtGui, QtCore, QtSvg, QT_LIB +from ..Qt import QtGui, QtCore, QT_LIB +from .. import functions as fn from ..graphicsItems.ROI import ROI from .. import SRTTransform, ItemGroup import importlib @@ -240,7 +240,7 @@ class CanvasItem(QtCore.QObject): self._graphicsItem.setOpacity(alpha) def setAlpha(self, alpha): - self.alphaSlider.setValue(int(np.clip(alpha * 1023, 0, 1023))) + self.alphaSlider.setValue(int(fn.clip_scalar(alpha * 1023, 0, 1023))) def alpha(self): return self.alphaSlider.value() / 1023. diff --git a/pyqtgraph/colorRegistry.py b/pyqtgraph/colorRegistry.py new file mode 100644 index 00000000..aa5b2cf6 --- /dev/null +++ b/pyqtgraph/colorRegistry.py @@ -0,0 +1,289 @@ +from .Qt import QtCore, QtGui + +import weakref +import itertools +import warnings + +__all__ = ['ColorRegistry'] +DEBUG = False + +DEFAULT_COLORS = { + 'b': QtGui.QColor( 0, 0,255,255), + 'g': QtGui.QColor( 0,255, 0,255), + 'r': QtGui.QColor(255, 0, 0,255), + 'c': QtGui.QColor( 0,255,255,255), + 'm': QtGui.QColor(255, 0,255,255), + 'y': QtGui.QColor(255,255, 0,255), + 'k': QtGui.QColor( 0, 0, 0,255), + 'w': QtGui.QColor(255,255,255,255), + 'd': QtGui.QColor(150,150,150,255), + 'l': QtGui.QColor(200,200,200,255), + 's': QtGui.QColor(100,100,150,255), + 'gr_acc':QtGui.QColor(200,200,100,255), # graphical accent color: pastel yellow + 'gr_reg':QtGui.QColor( 0, 0,255, 50) # graphical region marker: translucent blue +} +for key, col in [ # add functional colors + ('gr_fg','d'), # graphical foreground + ('gr_bg','k'), # graphical background + ('gr_txt','d'), # graphical text color + ('gr_hlt','r') # graphical hover color +]: + DEFAULT_COLORS[key] = DEFAULT_COLORS[col] + +for idx, col in enumerate( ( # twelve predefined plot colors + 'l','y','r','m','b','c','g','d' +) ): + key = 'p{:X}'.format(idx) + DEFAULT_COLORS[key] = DEFAULT_COLORS[col] +del key + +# An instantiated QObject is required to emit QSignals. +# functions.py initializes and maintains COLOR_REGISTRY for this purpose. +class ColorRegistry(QtCore.QObject): + """ + Provides palette change signals and maintains color name dictionary + Typically instantiated by functions.py as COLOR_REGISTRY + Instantiated by 'functions.py' and retrievable as functions.COLOR_REGISTRY + """ + paletteHasChangedSignal = QtCore.Signal() # equated to pyqtSignal in qt.py for PyQt + + _registrationGenerator = itertools.count() + + def __init__(self, color_dic): + """ initialization """ + super().__init__() + self.color_dic = color_dic # this is the imported functions.Colors! + self.color_dic.clear() + self.color_dic.update( DEFAULT_COLORS) + # set of objects that are registered for update on palette change: + self.registered_objects = {} # stores registration: (weakref(object), descriptor), deletion handled by weakref.finalize + self.color_cache = weakref.WeakValueDictionary() # keeps a cache of QColor objects, key: (name, alpha) + self.pen_cache = weakref.WeakValueDictionary() # keeps a cache of QPen objects, key: (name, width, alpha) + self.brush_cache = weakref.WeakValueDictionary() # keeps a cache of QBrush objects, key: (name, alpha) + + def confirmColorDescriptor(self, color): + """ + Takes a color or brush argument and tries to convert it to a normalized descriptor in the form + ``( str(name), None/int(alpha) )``. Returns False if this is not possible. + """ + name = alpha = None + if color is None or isinstance( color, str): + name = color + elif hasattr( color, '__len__'): + length = len(color) + if length >= 1: name = color[0] + if length >= 2: alpha = color[1] + if name is not None: + if not isinstance(name,str): return False # pen id has to be str or a list-like starting with str + if len(name) < 1: name = None # map '' to None + # elif name[0] == '#': return False # strings that start with # are explicit rgb codes + if alpha is not None: alpha = int(alpha) + return (name, alpha) + + def confirmPenDescriptor(self, pen): + """ + Takes a pen argument and tries to convert it to a normalized descriptor in the form + ``( str(name), None/int(width), None/int(alpha) )``. Returns False if this is not possible. + """ + name = width = alpha = None + if pen is None or isinstance( pen, str): + name = pen + elif hasattr( pen, '__len__'): + length = len(pen) + if length >= 1: name = pen[0] + if length >= 2: width = pen[1] + if length >= 3: alpha = pen[2] + if name is not None: + if type(name) != str: return False # pen id has to be str or a list-like starting with str + if len(name) < 1 : name = None # map '' to None + # elif name[0] == '#' : return False # strings that start with # are explicit rgb codes + if width is not None: width = int(width) + if alpha is not None: alpha = int(alpha) + return (name, width, alpha) + + def getRegisteredColor( self, color_desc, skipCache=False ): + """ + Returns a registered QColor according to (name, alpha) color descriptor + Setting skipCache = True creates a new registered QColor that is not added to the cache + Otherwise the color is cached and reused on the next request with the same pen id. + """ + register = True # register, unless this is a hex code color + # print('color descriptor:',color_desc) + desc = self.confirmColorDescriptor(color_desc) + if desc is False: return None # not a valid color id + name, alpha = desc + if name is None: + return QtGui.QColor(0, 0, 0, 0) # fully transparent + if name[0] == '#': # skip cache and registry for fixed hex colors + # print('preparting hex color:',name) + skipCache = True + register = False + if not skipCache and desc in self.color_cache: + return self.color_cache[desc] + qcol = QtGui.QColor() + self._update_QColor(qcol, desc) + if register: self.register(qcol, desc) # register for updates on palette change + if not skipCache: # skipCache disable both reading and writing cache + self.color_cache[desc] = qcol + return qcol + + def getRegisteredPen( self, pen_desc, skipCache=False ): + """ + Returns a registered QPen according to (name, width, alpha) pen descriptor + Setting skipCache = True creates a new registered QPen that is not added to the cache. + Otherwise the pen is cached and reused on the next request with the same pen descriptor. + """ + register = True # register, unless this is a hex code color + # print('pen descriptor:',pen_desc) + desc = self.confirmPenDescriptor(pen_desc) + if desc is False: return None # not a valid pen id + name, width, alpha = desc + if name is None: + return QtGui.QPen( QtCore.Qt.NoPen ) + if name[0] == '#': # skip cache and registry for fixed hex colors + # print('preparting hex pen:',name) + skipCache = True + register = False + if not skipCache and desc in self.pen_cache: + return self.pen_cache[desc] + qpen = QtGui.QPen() + self._update_QPen(qpen,desc) + if register: self.register( qpen, desc ) # register for updates on palette change + if not skipCache: # skipCache disable both reading and writing cache + self.pen_cache[desc] = qpen + return qpen + + def getRegisteredBrush( self, brush_desc, skipCache=False ): + """ + Returns a registered QBrush according to (name, alpha) brush descriptor + Setting skipCache = True creates a new registered QBrush that is not added to the cache. + Otherwise the brush is cached and reused on the next request with the same descriptor. + """ + register = True # register, unless this is a hex code color + # print('brush descriptor:',brush_desc) + desc = self.confirmColorDescriptor(brush_desc) # brush id is the same (name, alpha) format as color id + if desc is False: return None # not a valid brush id + name, alpha = desc + if name is None: + # print('returning blank brush!') + return QtGui.QBrush( QtCore.Qt.NoBrush ) + if name[0] == '#': # skip cache and registry for fixed hex colors + # print('preparing hex brush:',name) + skipCache = True + register = False + if not skipCache and desc in self.brush_cache: + # print('using cached brush', desc) + return self.brush_cache[desc] + # print('making brush!') + qbrush = QtGui.QBrush(QtCore.Qt.SolidPattern) # make sure this brush fills once color is set + self._update_QBrush(qbrush,desc) + if register: self.register( qbrush, desc ) # register for updates on palette change + if not skipCache: # skipCache disable both reading and writing cache + self.brush_cache[desc] = qbrush + return qbrush + + def _update_QColor(self, qcol, desc): + """ updates qcol to match descriptor for current palette """ + # print('updating color to match',desc) + name, alpha = desc + if name[0] != '#': + qcol.setRgba( self.color_dic[name].rgba() ) + else: + qcol.setNamedColor(name) # set from hex string + if alpha is not None: qcol.setAlpha(alpha) + # print('= hex:', qcol.name() ) + + def _update_QPen(self, qpen, desc): + """ updates qpen to match descriptor for current palette """ + # print('updating pen to match',desc) + name, width, alpha = desc + if name[0] != '#': + if alpha is None: + qpen.setColor( self.color_dic[name] ) # automatically copies + else: + qcol = QtGui.QColor(self.color_dic[name]) # make a copy + qcol.setAlpha(alpha) + qpen.setColor(qcol) + else: # set from hex string instead: + qcol = QtGui.QColor(name) + if alpha is not None: qcol.setAlpha(alpha) + qpen.setColor(qcol) + if width is not None: + qpen.setWidth(width) + # print('= hex:', qpen.color().name() ) + + def _update_QBrush(self, qbrush, desc): + """ updates qbrush to match descriptor for current palette """ + # print('updating brush',qbrush,'to match',desc) + name, alpha = desc + if name[0] != '#': + if alpha is None: + qbrush.setColor( self.color_dic[name] ) # automatically copies + else: + qcol = QtGui.QColor(self.color_dic[name]) # make a copy + qcol.setAlpha(alpha) + qbrush.setColor(qcol) + else: # set from hex string instead: + qcol = QtGui.QColor(name) + if alpha is not None: qcol.setAlpha(alpha) + qbrush.setColor(qcol) + # print('= hex:', qbrush.color().name(), qbrush.color().alpha() ) + + + def register(self, obj, desc): + """ + Registers obj (QColor, QPen or QBrush) to be updated according to the descriptor on palette change + """ + if hasattr(obj,'registration'): + registration = obj.registration + else: + registration = next(ColorRegistry._registrationGenerator) + obj.registration = registration # patch in attribute + fin = weakref.finalize(obj, self.unregister, registration) + fin.atexit = False # no need to clean up registry on program exit + # print('registering', registration, '(',str(obj),'):',str(desc)) + self.registered_objects[registration] = (weakref.ref(obj), desc) + + def unregister(self, registration): + """ + Removes obj (QColor, QPen or QBrush) from the registry, usually called by finalize on deletion + """ + obj, desc = self.registered_objects[registration] + # print('unregistering', registration, '(',str(obj),'):',str(desc)) + del self.registered_objects[registration] + del obj, desc + + + def colors(self): + """ return current list of colors """ + return self.color_dic # it would be safer (but slower) to provide only a copy + + + def redefinePalette(self, colors=None): + """ + Update list of named colors if 'colors' dictionary is given + Emits paletteHasChanged signals to color objects and widgets, even if color_dict is None + """ + if colors is not None: + for key in DEFAULT_COLORS: + if key not in colors: + raise ValueError("Palette definition is missing '"+str(key)+"'") + if DEBUG: print(' ColorRegistry: Setting palette, all color definitions are present.') + self.color_dic.clear() + self.color_dic.update(colors) + + # notifies named color objects of new assignments: + for key in self.registered_objects: + ref, desc = self.registered_objects[key] + obj = ref() + # print('updating', obj) + if obj is None: + warnings.warn('Expired object with descriptor '+str(desc)+' remains in color registry.', RuntimeWarning) + elif isinstance(obj, QtGui.QColor): + self._update_QColor(obj, desc) + elif isinstance(obj, QtGui.QPen): + self._update_QPen(obj, desc) + elif isinstance(obj, QtGui.QBrush): + self._update_QBrush(obj, desc) + # notify all graphics widgets that redraw is required: + self.paletteHasChangedSignal.emit() diff --git a/pyqtgraph/colormap.py b/pyqtgraph/colormap.py index f5c3583a..0eb17e13 100644 --- a/pyqtgraph/colormap.py +++ b/pyqtgraph/colormap.py @@ -1,9 +1,9 @@ import numpy as np from .Qt import QtGui, QtCore -from .python2_3 import basestring from .functions import mkColor, eq from os import path, listdir -import collections +from collections.abc import Callable, Sequence +import warnings __all__ = ['ColorMap'] @@ -63,14 +63,14 @@ def get(name, source=None, skipCache=False): if not skipCache and name in _mapCache: return _mapCache[name] if source is None: - return _get_from_file(name) + return _getFromFile(name) elif source == 'matplotlib': - return _get_from_matplotlib(name) + return getFromMatplotlib(name) elif source == 'colorcet': - return _get_from_colorcet(name) + return getFromColorcet(name) return None -def _get_from_file(name): +def _getFromFile(name): filename = name if filename[0] !='.': # load from built-in directory dirname = path.dirname(__file__) @@ -115,7 +115,7 @@ def _get_from_file(name): _mapCache[name] = cmap return cmap -def _get_from_matplotlib(name): +def getFromMatplotlib(name): """ import colormap from matplotlib definition """ # inspired and informed by "mpl_cmaps_in_ImageItem.py", published by Sebastian Hoefer at # https://github.com/honkomonk/pyqtgraph_sandbox/blob/master/mpl_cmaps_in_ImageItem.py @@ -127,7 +127,7 @@ def _get_from_matplotlib(name): col_map = mpl_plt.get_cmap(name) if hasattr(col_map, '_segmentdata'): # handle LinearSegmentedColormap data = col_map._segmentdata - if ('red' in data) and isinstance(data['red'], collections.Sequence): + if ('red' in data) and isinstance(data['red'], Sequence): positions = set() # super-set of handle positions in individual channels for key in ['red','green','blue']: for tup in data[key]: @@ -143,7 +143,7 @@ def _get_from_matplotlib(name): col_data[:,idx] = np.interp(col_data[:,3], positions, comp_vals) cmap = ColorMap(pos=col_data[:,-1], color=255*col_data[:,:3]+0.5) # some color maps (gnuplot in particular) are defined by RGB component functions: - elif ('red' in data) and isinstance(data['red'], collections.Callable): + elif ('red' in data) and isinstance(data['red'], Callable): col_data = np.zeros((64, 4)) col_data[:,-1] = np.linspace(0., 1., 64) for idx, key in enumerate(['red','green','blue']): @@ -157,7 +157,7 @@ def _get_from_matplotlib(name): _mapCache[name] = cmap return cmap -def _get_from_colorcet(name): +def getFromColorcet(name): """ import colormap from colorcet definition """ try: import colorcet @@ -229,16 +229,11 @@ class ColorMap(object): | 1.0 -> white The colors for intermediate values are determined by interpolating between - the two nearest colors in either RGB or HSV color space. + the two nearest colors in RGB color space. To provide user-defined color mappings, see :class:`GradientWidget `. """ - ## color interpolation modes - RGB = 1 - HSV_POS = 2 - HSV_NEG = 3 - ## mapping modes CLIP = 1 REPEAT = 2 @@ -251,60 +246,53 @@ class ColorMap(object): QCOLOR = 3 enumMap = { - 'rgb': RGB, - # 'hsv+': HSV_POS, - # 'hsv-': HSV_NEG, - # 'clip': CLIP, - # 'repeat': REPEAT, + 'clip': CLIP, + 'repeat': REPEAT, + 'mirror': MIRROR, + 'diverging': DIVERGING, 'byte': BYTE, 'float': FLOAT, 'qcolor': QCOLOR, } - def __init__(self, pos, color, name=None, mode=None, mapping=None): #, names=None): + def __init__(self, pos, color, mapping=CLIP, mode=None, name=None): #, names=None): """ - =============== ================================================================= + =============== ======================================================================= **Arguments:** pos Array of positions where each color is defined color Array of colors. Values are interpreted via :func:`mkColor() `. - mode Array of color modes (ColorMap.RGB, HSV_POS, or HSV_NEG) - indicating the color space that should be used when - interpolating between stops. Note that the last mode value is - ignored. By default, the mode is entirely RGB. - mapping Mapping mode (ColorMap.CLIP, REPEAT, MIRROR, or DIVERGING) - controlling mapping of relative index to color. - CLIP maps colors to [0.0;1.0] + mapping Mapping mode (ColorMap.CLIP, REPEAT, MIRROR or DIVERGING) + controlling mapping of relative index to color. String representations + 'clip', 'repeat', 'mirror' or 'diverging' are also accepted. + CLIP maps colors to [0.0;1.0] and is the default. REPEAT maps colors to repeating intervals [0.0;1.0];[1.0-2.0],... MIRROR maps colors to [0.0;-1.0] and [0.0;+1.0] identically DIVERGING maps colors to [-1.0;+1.0] - =============== ================================================================= + =============== ======================================================================= """ self.name = name # storing a name helps identify ColorMaps sampled by Palette + if mode is not None: + warnings.warn( + "'mode' argument is deprecated and does nothing.", + DeprecationWarning, stacklevel=2 + ) + if isinstance(mapping, str): + mapping = self.enumMap[mapping.lower()] + self.pos = np.array(pos) order = np.argsort(self.pos) self.pos = self.pos[order] self.color = np.apply_along_axis( - func1d = lambda x: mkColor(x).getRgb(), + func1d = lambda x: np.uint8( mkColor(x).getRgb() ), # cast RGB integer values to uint8 axis = -1, arr = color, )[order] - if mode is None: - mode = np.ones(len(pos)) - self.mode = mode - if mapping is None: - self.mapping_mode = self.CLIP - elif mapping == self.REPEAT: - self.mapping_mode = self.REPEAT - elif mapping == self.DIVERGING: - self.mapping_mode = self.DIVERGING - elif mapping == self.MIRROR: - self.mapping_mode = self.MIRROR - else: - self.mapping_mode = self.CLIP - + self.mapping_mode = self.CLIP # default to CLIP mode + if mapping in [self.CLIP, self.REPEAT, self.DIVERGING, self.MIRROR]: + self.mapping_mode = mapping # only allow defined values self.stopsCache = {} def __str__(self): @@ -324,7 +312,7 @@ class ColorMap(object): except ValueError: pass return None - def map(self, data, mode='byte'): + def map(self, data, mode=BYTE): """ Return an array of colors corresponding to the values in *data*. Data must be either a scalar position or an array (any shape) of positions. @@ -337,7 +325,7 @@ class ColorMap(object): qcolor Values are returned as an array of QColor objects. =========== =============================================================== """ - if isinstance(mode, basestring): + if isinstance(mode, str): mode = self.enumMap[mode.lower()] if mode == self.QCOLOR: @@ -345,9 +333,6 @@ class ColorMap(object): else: pos, color = self.getStops(mode) - # Interpolate - # TODO: is griddata faster? - # interp = scipy.interpolate.griddata(pos, color, data) if np.isscalar(data): interp = np.empty((color.shape[1],), dtype=color.dtype) else: @@ -407,7 +392,7 @@ class ColorMap(object): def getColors(self, mode=None): """Return list of all color stops converted to the specified mode. If mode is None, then no conversion is done.""" - if isinstance(mode, basestring): + if isinstance(mode, str): mode = self.enumMap[mode.lower()] color = self.color @@ -426,11 +411,9 @@ class ColorMap(object): if mode not in self.stopsCache: color = self.color if mode == self.BYTE and color.dtype.kind == 'f': - color = (color * 255).astype(np.ubyte) + color = (color*255).astype(np.ubyte) elif mode == self.FLOAT and color.dtype.kind != 'f': color = color.astype(float) / 255. - - ## to support HSV mode, we need to do a little more work.. self.stopsCache[mode] = (self.pos, color) return self.stopsCache[mode] @@ -449,7 +432,7 @@ class ColorMap(object): See :func:`map() `. =============== ============================================================================= """ - if isinstance(mode, basestring): + if isinstance(mode, str): mode = self.enumMap[mode.lower()] if alpha is None: diff --git a/pyqtgraph/debug.py b/pyqtgraph/debug.py index 1d12e9e0..37b612cd 100644 --- a/pyqtgraph/debug.py +++ b/pyqtgraph/debug.py @@ -5,14 +5,21 @@ Copyright 2010 Luke Campagnola Distributed under MIT/X11 license. See license.txt for more information. """ + from __future__ import print_function import sys, traceback, time, gc, re, types, weakref, inspect, os, cProfile, threading +import warnings from . import ptime from numpy import ndarray -from .Qt import QtCore, QtGui -from .util.mutex import Mutex +from .Qt import QtCore, QT_LIB from .util import cprint +if sys.version.startswith("3.8") and QT_LIB == "PySide2": + from .Qt import PySide2 + if tuple(map(int, PySide2.__version__.split("."))) < (5, 14): + warnings.warn("Due to PYSIDE-1140, ThreadChase and ThreadColor won't work") +from .util.mutex import Mutex + __ftraceDepth = 0 def ftrace(func): @@ -195,7 +202,7 @@ def findRefPath(startObj, endObj, maxLen=8, restart=True, seen={}, path=None, ig #print prefix+" FRAME" continue try: - if any([r is x for x in path]): + if any(r is x for x in path): #print prefix+" LOOP", objChainString([r]+path) continue except: @@ -275,7 +282,7 @@ def refPathString(chain): o2 = chain[i] cont = False if isinstance(o1, list) or isinstance(o1, tuple): - if any([o2 is x for x in o1]): + if any(o2 is x for x in o1): s += "[%d]" % o1.index(o2) continue #print " not list" diff --git a/pyqtgraph/dockarea/Container.py b/pyqtgraph/dockarea/Container.py index 04b775f9..34f16288 100644 --- a/pyqtgraph/dockarea/Container.py +++ b/pyqtgraph/dockarea/Container.py @@ -126,7 +126,7 @@ class SplitContainer(Container, QtGui.QSplitter): def saveState(self): sizes = self.sizes() - if all([x == 0 for x in sizes]): + if all(x == 0 for x in sizes): sizes = [10] * len(sizes) return {'sizes': sizes} diff --git a/pyqtgraph/exporters/SVGExporter.py b/pyqtgraph/exporters/SVGExporter.py index 465c4526..b904ed15 100644 --- a/pyqtgraph/exporters/SVGExporter.py +++ b/pyqtgraph/exporters/SVGExporter.py @@ -172,7 +172,6 @@ def _generateItemSvg(item, nodes=None, root=None, options={}): ## Generate SVG text for just this item (exclude its children; we'll handle them later) - tr = QtGui.QTransform() if isinstance(item, QtGui.QGraphicsScene): xmlStr = "\n\n" doc = xml.parseString(xmlStr) diff --git a/pyqtgraph/flowchart/library/Data.py b/pyqtgraph/flowchart/library/Data.py index 6edd3a80..b4d229ba 100644 --- a/pyqtgraph/flowchart/library/Data.py +++ b/pyqtgraph/flowchart/library/Data.py @@ -160,10 +160,9 @@ class RegionSelectNode(CtrlNode): sliced = data[0:s['start']:s['stop']] else: mask = (data['time'] >= s['start']) * (data['time'] < s['stop']) - sliced = data[mask] + sliced = data[mask] else: sliced = None - return {'selected': sliced, 'widget': self.items, 'region': region} diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 9ac90e5f..fe7da610 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -13,24 +13,26 @@ import re import struct import sys import warnings +import math import numpy as np from .util.cupy_helper import getCupy +from .util.numba_helper import getNumbaFunctions from . import debug, reload from .Qt import QtGui, QtCore, QT_LIB, QtVersion from . import Qt -from .namedColorManager import NamedColorManager -from .namedPen import NamedPen -from .namedBrush import NamedBrush +from .colorRegistry import ColorRegistry from .metaarray import MetaArray from collections import OrderedDict -from .python2_3 import asUnicode, basestring +from .python2_3 import asUnicode # legacy color definitions: -# NamedColorManager now maintains the primary list of palette colors, -# accessible through functions.NAMED_COLOR_MANAGER.colors(). +# ColorRegistry now maintains the primary list of palette colors, +# accessible through functions.COLOR_REGISTRY.colors(). +# # NamedColorManager now maintains the primary list of palette colors, +# # accessible through functions.NAMED_COLOR_MANAGER.colors(). # For backwards compatibility, this dictionary is updated to contain the same information. # # For the user, colors and color palettes are most conveniently accessed through a Palette object. @@ -47,7 +49,7 @@ Colors = { 'l': QtGui.QColor(200,200,200,255), 's': QtGui.QColor(100,100,150,255) } -NAMED_COLOR_MANAGER = NamedColorManager( Colors ) +COLOR_REGISTRY = ColorRegistry( Colors ) SI_PREFIXES = asUnicode('yzafpnµm kMGTPEZY') SI_PREFIXES_ASCII = 'yzafpnum kMGTPEZY' @@ -70,19 +72,15 @@ def siScale(x, minVal=1e-25, allowUnicode=True): if isinstance(x, decimal.Decimal): x = float(x) - try: - if np.isnan(x) or np.isinf(x): + if not math.isfinite(x): return(1, '') except: - print(x, type(x)) raise if abs(x) < minVal: m = 0 - x = 0 else: - m = int(np.clip(np.floor(np.log(abs(x))/np.log(1000)), -9.0, 9.0)) - + m = int(clip_scalar(math.floor(math.log(abs(x))/math.log(1000)), -9.0, 9.0)) if m == 0: pref = '' elif m < -8 or m > 8: @@ -94,7 +92,6 @@ def siScale(x, minVal=1e-25, allowUnicode=True): pref = SI_PREFIXES_ASCII[m+8] m1 = -3*m p = 10.**m1 - return (p, pref) @@ -227,7 +224,6 @@ def parseNamedColorSpecification(*args): check if args specify a NamedColor, looking for 'name' or ('name', alpha) information. Returns: - None if invalid ('name', alpha) if a valid name and alpha value is given ('name', None) if no alpha value is available ('', None) if an empty name is given, indicating a blank color @@ -251,47 +247,59 @@ def parseNamedColorSpecification(*args): return None #numerical values not handled as NamedColor if len(args) == 2: if isinstance(arg[0], str): + alpha = arg[1] + if isinstance(alpha, float): + alpha = int(alpha*255) # convert to 0-255 integer return (arg[0], arg[1]) # return ('name', alpha) tuple return None # all other cases not handled as NamedColor - def mkColor(*args): """ - Convenience function for constructing QColor from a variety of argument types. Accepted arguments are: + Convenience function for constructing QColor from a variety of argument + types. Accepted arguments are: - ================ ================================================ + ================ =========================================================== 'name' any color name specifed in palette + ('name', alpha) color name from palette with specified opacity 0-255 R, G, B, [A] integers 0-255 (R, G, B, [A]) tuple of integers 0-255 float greyscale, 0.0-1.0 int see :func:`intColor() ` (int, hues) see :func:`intColor() ` - "#RGB" hexadecimal strings; should begin with '#' - "#RGBA" - "#RRGGBB" + "#RGB" hexadecimal strings prefixed with '#' + "#RGBA" previously allowed use without prefix is deprecated and + "#RRGGBB" will be removed in 0.13 "#RRGGBBAA" QColor QColor instance; makes a copy. - ================ ================================================ + ================ =========================================================== """ err = 'Not sure how to make a color from "%s"' % str(args) result = parseNamedColorSpecification(args) # check if this is a named palette color - if result is not None: # make a return palette color + if result is not None: # return palette color name, alpha = result - if name == '': + if name is None: return QtGui.QColor(0,0,0,0) # empty string means "no color" qcol = Colors[name] - if alpha is not None: qcol.setAlpha( alpha ) + if alpha is not None: + qcol = QtGui.QColor(qcol) # copy to avoid changing alpha of cached color + qcol.setAlphaF( alpha ) return qcol if len(args) == 1: - if isinstance(args[0], basestring): + if isinstance(args[0], str): c = args[0] - if c[0] == '#': - c = c[1:] if len(c) == 1: try: return Colors[c] except KeyError: raise ValueError('No color named "%s"' % c) + if c[0] == '#': + c = c[1:] + else: + warnings.warn( + "Parsing of hex strings that do not start with '#' is" + "deprecated and support will be removed in 0.13", + DeprecationWarning, stacklevel=2 + ) if len(c) == 3: r = int(c[0]*2, 16) g = int(c[1]*2, 16) @@ -312,6 +320,8 @@ def mkColor(*args): g = int(c[2:4], 16) b = int(c[4:6], 16) a = int(c[6:8], 16) + else: + raise ValueError(f"Unknown how to convert string {c} to color") elif isinstance(args[0], QtGui.QColor): return QtGui.QColor(args[0]) elif np.issubdtype(type(args[0]), np.floating): @@ -319,10 +329,10 @@ def mkColor(*args): a = 255 elif hasattr(args[0], '__len__'): if len(args[0]) == 3: - (r, g, b) = args[0] + r, g, b = args[0] a = 255 elif len(args[0]) == 4: - (r, g, b, a) = args[0] + r, g, b, a = args[0] elif len(args[0]) == 2: return intColor(*args[0]) else: @@ -332,16 +342,13 @@ def mkColor(*args): else: raise TypeError(err) elif len(args) == 3: - (r, g, b) = args + r, g, b = args a = 255 elif len(args) == 4: - (r, g, b, a) = args + r, g, b, a = args else: raise TypeError(err) - - args = [r,g,b,a] - args = [0 if np.isnan(a) or np.isinf(a) else a for a in args] - args = list(map(int, args)) + args = [int(a) if np.isfinite(a) else 0 for a in (r, g, b, a)] return QtGui.QColor(*args) @@ -353,53 +360,61 @@ def mkBrush(*args, **kargs): """ while ( # unravel single element sublists isinstance(args, (tuple, list) ) - and len(args) == 1 + and len(args) == 1 ): args = args[0] # now args is either a non-list entity, or a multi-element tuple # short-circuits: - if isinstance(args, NamedBrush): - return args # pass through predefined NamedPen directly - elif isinstance(args, QtGui.QBrush): - return QtGui.QBrush(args) ## return a copy of this brush - elif isinstance(args, dict): + if isinstance(args, QtGui.QBrush): + if hasattr(args,'registration'): + return args # pass through registered brush directly + return QtGui.QBrush(args) # return a copy of an uregistered brush + + if isinstance(args, dict): return mkBrush(**args) # retry with kwargs assigned from dictionary - elif args is None: - return QtGui.QBrush( QtCore.Qt.NoBrush ) # explicit None means "no brush" + + # if args is None: + # return QtGui.QBrush( QtCore.Qt.NoBrush ) # explicit None means "no brush" + # no short-circuit, continue parsing to construct QPen or NamedPen if 'hsv' in kargs: # hsv argument takes precedence qcol = hsvColor( *kargs['hsv'] ) - qbrush = QtGui.QBrush(qcol) - else: - if 'color' in kargs: - args = kargs['color'] # 'color' KW-argument overrides unnamed arguments - if args is None: - return QtGui.QBrush( QtCore.Qt.NoBrush ) # explicit None means "no brush" - if args == () or args == []: - print(' functions: returning default color NamedBrush') - qbrush = NamedBrush( 'gr_fg', manager=NAMED_COLOR_MANAGER ) # default foreground color - else: - result = parseNamedColorSpecification(args) - if result is not None: # make a NamedBrush - name, alpha = result - if name == '': - return QtGui.QBrush( QtCore.Qt.NoBrush ) # empty string means "no brush" - qbrush = NamedBrush(name, manager=NAMED_COLOR_MANAGER, alpha=alpha) - else: # make a QBrush - qcol = mkColor(args) - qbrush = QtGui.QBrush(qcol) - # here we would apply additional style based on KW-arguments - return qbrush + return QtGui.QBrush(qcol) + + if 'color' in kargs: # 'color' KW-argument overrides unnamed arguments + args = kargs['color'] + + if args is None: # explicit None means "no brush" + return QtGui.QBrush( QtCore.Qt.NoBrush ) + + if args == () or args == []: + # print(' functions: returning default color registered brush') + return COLOR_REGISTRY.getRegisteredBrush('gr_fg') + # return NamedBrush( 'gr_fg', manager=NAMED_COLOR_MANAGER ) # default foreground color + + # result = parseNamedColorSpecification(args) + # Do the the arguments make a suitable brush descriptor? + result = COLOR_REGISTRY.getRegisteredBrush(args) + if result is not None: + # print( result, type(result) ) + # print('setting hex:', result.color().name(), result.color().alpha() ) + return result + + # Otherwise make a QBrush + qcol = mkColor(args) + return QtGui.QBrush(qcol) def mkPen(*args, **kargs): """ Convenience function for constructing QPen. Examples:: + mkPen(QPen) + mkPen( ('r', width, alpha) ) mkPen(color) mkPen(color, width=2) mkPen(cosmetic=False, width=4.5, color='r') - mkPen({'color': "FF0", width: 2}) + mkPen({'color': "#FF0", width: 2}) mkPen(None) # (no pen) mkPen() # default color @@ -407,19 +422,22 @@ def mkPen(*args, **kargs): # print('mkPen called:',args,kargs) while ( # unravel single element sublists isinstance(args, (tuple, list) ) - and len(args) == 1 + and len(args) == 1 ): args = args[0] # now args is either a non-list entity, or a multi-element tuple # short-circuits: - if isinstance(args, NamedPen): - return args # pass through predefined NamedPen directly - elif isinstance(args, QtGui.QPen): + if isinstance(args, QtGui.QPen): + if hasattr(args,'registration'): + return args # pass through registered pen directly return QtGui.QPen(args) ## return a copy of this pen + elif isinstance(args, dict): return mkPen(**args) # retry with kwargs assigned from dictionary - elif args is None: - return QtGui.QPen( QtCore.Qt.NoPen ) # explicit None means "no pen" + + # if args is None: + # return QtGui.QPen( QtCore.Qt.NoPen ) # explicit None means "no pen" + # no short-circuit, continue parsing to construct QPen or NamedPen width = kargs.get('width', 1) # width 1 unless specified otherwise if 'hsv' in kargs: # hsv argument takes precedence @@ -431,14 +449,18 @@ def mkPen(*args, **kargs): if args is None: return QtGui.QPen( QtCore.Qt.NoPen ) # explicit None means "no pen" if args == () or args == []: - qpen = NamedPen( 'gr_fg', manager=NAMED_COLOR_MANAGER, width=width ) # default foreground color + # print(' functions: returning default color registered brush') + qpen = COLOR_REGISTRY.getRegisteredPen( ('gr_fg', width) ) # default foreground color + # return COLOR_REGISTRY.getRegisteredBrush('gr_fg') else: - result = parseNamedColorSpecification(args) + # result = parseNamedColorSpecification(args) + result = COLOR_REGISTRY.getRegisteredPen(args) if result is not None: # make a NamedPen - name, alpha = result - if name == '': - return QtGui.QPen( QtCore.Qt.NoPen ) # empty string means "no pen" - qpen = NamedPen( name, manager=NAMED_COLOR_MANAGER, alpha=alpha, width=width ) + qpen = result + # name, alpha = result + # if name == '': + # return QtGui.QPen( QtCore.Qt.NoPen ) # empty string means "no pen" + # qpen = NamedPen( name, manager=NAMED_COLOR_MANAGER, alpha=alpha, width=width ) else: # make a QPen qcol = mkColor(args) qpen = QtGui.QPen(QtGui.QBrush(qcol), width) @@ -516,16 +538,16 @@ def makeArrowPath(headLen=20, headWidth=None, tipAngle=20, tailLen=20, tailWidth If *tailLen* is None, no tail will be drawn. """ if headWidth is None: - headWidth = headLen * np.tan(tipAngle * 0.5 * np.pi/180.) + headWidth = headLen * math.tan(math.radians(tipAngle * 0.5)) path = QtGui.QPainterPath() path.moveTo(0,0) path.lineTo(headLen, -headWidth) if tailLen is None: - innerY = headLen - headWidth * np.tan(baseAngle*np.pi/180.) + innerY = headLen - headWidth * math.tan(math.radians(baseAngle)) path.lineTo(innerY, 0) else: tailWidth *= 0.5 - innerY = headLen - (headWidth-tailWidth) * np.tan(baseAngle*np.pi/180.) + innerY = headLen - (headWidth-tailWidth) * math.tan(math.radians(baseAngle)) path.lineTo(innerY, -tailWidth) path.lineTo(headLen + tailLen, -tailWidth) path.lineTo(headLen + tailLen, tailWidth) @@ -542,7 +564,7 @@ def eq(a, b): 1. Returns True if a IS b, even if a==b still evaluates to False. 2. While a is b will catch the case with np.nan values, special handling is done for distinct - float('nan') instances using np.isnan. + float('nan') instances using math.isnan. 3. Tests for equivalence using ==, but silently ignores some common exceptions that can occur (AtrtibuteError, ValueError). 4. When comparing arrays, returns False if the array shapes are not the same. @@ -556,7 +578,7 @@ def eq(a, b): # The above catches np.nan, but not float('nan') if isinstance(a, float) and isinstance(b, float): - if np.isnan(a) and np.isnan(b): + if math.isnan(a) and math.isnan(b): return True # Avoid comparing large arrays against scalars; this is expensive and we know it should return False. @@ -1089,7 +1111,79 @@ def solveBilinearTransform(points1, points2): matrix[i] = numpy.linalg.solve(A, B[:,i]) ## solve Ax = B; x is one row of the desired transformation matrix return matrix - + +def clip_scalar(val, vmin, vmax): + """ convenience function to avoid using np.clip for scalar values """ + return vmin if val < vmin else vmax if val > vmax else val + +def clip_array(arr, vmin, vmax, out=None): + # replacement for np.clip due to regression in + # performance since numpy 1.17 + # https://github.com/numpy/numpy/issues/14281 + + if vmin is None and vmax is None: + # let np.clip handle the error + return np.clip(arr, vmin, vmax, out=out) + + if vmin is None: + return np.core.umath.minimum(arr, vmax, out=out) + elif vmax is None: + return np.core.umath.maximum(arr, vmin, out=out) + elif sys.platform == 'win32': + # Windows umath.clip is slower than umath.maximum(umath.minimum) + if out is None: + out = np.empty_like(arr) + out = np.core.umath.minimum(arr, vmax, out=out) + return np.core.umath.maximum(out, vmin, out=out) + else: + return np.core.umath.clip(arr, vmin, vmax, out=out) + + +def _rescaleData_nditer(data_in, scale, offset, work_dtype, out_dtype, clip): + """Refer to documentation for rescaleData()""" + data_out = np.empty_like(data_in, dtype=out_dtype) + + # integer clip operations are faster than float clip operations + # so test to see if we can perform integer clipping + fits_int32 = False + if data_in.dtype.kind in 'ui' and out_dtype.kind in 'ui': + # estimate whether data range after rescale will fit within an int32. + # this means that the input dtype should be an 8-bit or 16-bit integer type. + # casting to an int32 will lose the fractional part, therefore the + # output dtype must be an integer kind. + lim_in = np.iinfo(data_in.dtype) + # convert numpy scalar to python scalar to avoid overflow warnings + lo = offset.item(0) if isinstance(offset, np.number) else offset + dst_bounds = scale * (lim_in.min - lo), scale * (lim_in.max - lo) + if dst_bounds[1] < dst_bounds[0]: + dst_bounds = dst_bounds[1], dst_bounds[0] + lim32 = np.iinfo(np.int32) + fits_int32 = lim32.min < dst_bounds[0] and dst_bounds[1] < lim32.max + + it = np.nditer([data_in, data_out], + flags=['external_loop', 'buffered'], + op_flags=[['readonly'], ['writeonly', 'no_broadcast']], + op_dtypes=[None, work_dtype], + casting='unsafe', + buffersize=32768) + + with it: + for x, y in it: + y[...] = x + y -= offset + y *= scale + + # Clip before converting dtype to avoid overflow + if clip is not None: + if fits_int32: + # converts to int32, clips back to float32 + np.core.umath.clip(y.astype(np.int32), clip[0], clip[1], out=y) + else: + clip_array(y, clip[0], clip[1], out=y) + + return data_out + + def rescaleData(data, scale, offset, dtype=None, clip=None): """Return data rescaled and optionally cast to a new dtype. @@ -1098,32 +1192,48 @@ def rescaleData(data, scale, offset, dtype=None, clip=None): data => (data-offset) * scale """ if dtype is None: - dtype = data.dtype + out_dtype = data.dtype else: - dtype = np.dtype(dtype) - + out_dtype = np.dtype(dtype) + + if out_dtype.kind in 'ui': + lim = np.iinfo(out_dtype) + if clip is None: + # don't let rescale cause integer overflow + clip = lim.min, lim.max + clip = max(clip[0], lim.min), min(clip[1], lim.max) + + # make clip limits integer-valued (no need to cast to int) + # this improves performance, especially on Windows + clip = [math.trunc(x) for x in clip] + if np.can_cast(data, np.float32): work_dtype = np.float32 else: work_dtype = np.float64 - d2 = data.astype(work_dtype, copy=True) - d2 -= offset - d2 *= scale - # Clip before converting dtype to avoid overflow - if dtype.kind in 'ui': - lim = np.iinfo(dtype) - if clip is None: - # don't let rescale cause integer overflow - np.clip(d2, lim.min, lim.max, out=d2) - else: - np.clip(d2, max(clip[0], lim.min), min(clip[1], lim.max), out=d2) - else: + cp = getCupy() + if cp and cp.get_array_module(data) == cp: + # Cupy does not support nditer + # https://github.com/cupy/cupy/issues/5021 + + data_out = data.astype(work_dtype, copy=True) + data_out -= offset + data_out *= scale + + # Clip before converting dtype to avoid overflow if clip is not None: - np.clip(d2, *clip, out=d2) - # don't copy if no change in dtype - data = d2.astype(dtype, copy=False) - return data + clip_array(data_out, clip[0], clip[1], out=data_out) + + # don't copy if no change in dtype + return data_out.astype(out_dtype, copy=False) + + numba_fn = getNumbaFunctions() + if numba_fn and clip is not None: + # if we got here by makeARGB(), clip will not be None at this point + return numba_fn.rescaleData(data, scale, offset, out_dtype, clip) + + return _rescaleData_nditer(data, scale, offset, work_dtype, out_dtype, clip) def applyLookupTable(data, lut): @@ -1300,46 +1410,97 @@ def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False, output=None # decide channel order if useRGBA: - order = [0,1,2,3] # array comes out RGBA + dst_order = [0, 1, 2, 3] # R,G,B,A + elif sys.byteorder == 'little': + dst_order = [2, 1, 0, 3] # B,G,R,A (ARGB32 little endian) else: - order = [2,1,0,3] # for some reason, the colors line up as BGR in the final image. + dst_order = [1, 2, 3, 0] # A,R,G,B (ARGB32 big endian) # copy data into image array - if data.ndim == 2: + fastpath = try_fastpath_argb(xp, data, imgData, useRGBA) + + if fastpath: + pass + elif data.ndim == 2: # This is tempting: # imgData[..., :3] = data[..., xp.newaxis] # ..but it turns out this is faster: for i in range(3): - imgData[..., i] = data + imgData[..., dst_order[i]] = data elif data.shape[2] == 1: for i in range(3): - imgData[..., i] = data[..., 0] + imgData[..., dst_order[i]] = data[..., 0] else: for i in range(0, data.shape[2]): - imgData[..., i] = data[..., order[i]] + imgData[..., dst_order[i]] = data[..., i] profile('reorder channels') # add opaque alpha channel if needed - if data.ndim == 2 or data.shape[2] == 3: - alpha = False - imgData[..., 3] = 255 - else: + if data.ndim == 3 and data.shape[2] == 4: alpha = True + else: + alpha = False + if not fastpath: # fastpath has already filled it in + imgData[..., dst_order[3]] = 255 # apply nan mask through alpha channel if nanMask is not None: alpha = True # Workaround for https://github.com/cupy/cupy/issues/4693 if xp == cp: - imgData[nanMask, :, 3] = 0 + imgData[nanMask, :, dst_order[3]] = 0 else: - imgData[nanMask, 3] = 0 + imgData[nanMask, dst_order[3]] = 0 profile('alpha channel') return imgData, alpha +def try_fastpath_argb(xp, ain, aout, useRGBA): + # we only optimize for certain cases + # return False if we did not handle it + can_handle = xp is np and ain.dtype == xp.ubyte and ain.flags['C_CONTIGUOUS'] + if not can_handle: + return False + + nrows, ncols = ain.shape[:2] + nchans = 1 if ain.ndim == 2 else ain.shape[2] + + Format = QtGui.QImage.Format + + if nchans == 1: + in_fmt = Format.Format_Grayscale8 + elif nchans == 3: + in_fmt = Format.Format_RGB888 + else: + in_fmt = Format.Format_RGBA8888 + + if useRGBA: + out_fmt = Format.Format_RGBA8888 + else: + out_fmt = Format.Format_ARGB32 + + if in_fmt == out_fmt: + aout[:] = ain + return True + + npixels_chunk = 512*1024 + batch = int(npixels_chunk / ncols / nchans) + batch = max(1, batch) + row_beg = 0 + while row_beg < nrows: + row_end = min(row_beg + batch, nrows) + ain_view = ain[row_beg:row_end, ...] + aout_view = aout[row_beg:row_end, ...] + qimg = QtGui.QImage(ain_view, ncols, ain_view.shape[0], ain.strides[0], in_fmt) + qimg = qimg.convertToFormat(out_fmt) + aout_view[:] = imageToArray(qimg, copy=False, transpose=False) + row_beg = row_end + + return True + + def makeQImage(imgData, alpha=None, copy=True, transpose=True): """ Turn an ARGB array into QImage. diff --git a/pyqtgraph/functions_numba.py b/pyqtgraph/functions_numba.py new file mode 100644 index 00000000..d9e2f232 --- /dev/null +++ b/pyqtgraph/functions_numba.py @@ -0,0 +1,22 @@ +import numpy as np +import numba + +rescale_functions = {} + +def rescale_clip_source(xx, scale, offset, vmin, vmax, yy): + for i in range(xx.size): + val = (xx[i] - offset) * scale + yy[i] = min(max(val, vmin), vmax) + +def rescaleData(data, scale, offset, dtype, clip): + data_out = np.empty_like(data, dtype=dtype) + key = (data.dtype.name, data_out.dtype.name) + func = rescale_functions.get(key) + if func is None: + func = numba.guvectorize( + [f'{key[0]}[:],f8,f8,f8,f8,{key[1]}[:]'], + '(n),(),(),(),()->(n)', + nopython=True)(rescale_clip_source) + rescale_functions[key] = func + func(data, scale, offset, clip[0], clip[1], out=data_out) + return data_out diff --git a/pyqtgraph/graphicsItems/ArrowItem.py b/pyqtgraph/graphicsItems/ArrowItem.py index 77b6c44c..2d79d360 100644 --- a/pyqtgraph/graphicsItems/ArrowItem.py +++ b/pyqtgraph/graphicsItems/ArrowItem.py @@ -1,3 +1,4 @@ +from math import hypot from ..Qt import QtGui, QtCore from .. import functions as fn import numpy as np @@ -135,7 +136,7 @@ class ArrowItem(QtGui.QGraphicsPathItem): pad = 0 if self.opts['pxMode']: br = self.boundingRect() - pad += (br.width()**2 + br.height()**2) ** 0.5 + pad += hypot(br.width(), br.height()) pen = self.pen() if pen.isCosmetic(): pad += max(1, pen.width()) * 0.7072 diff --git a/pyqtgraph/graphicsItems/AxisItem.py b/pyqtgraph/graphicsItems/AxisItem.py index e3ef1066..13e71628 100644 --- a/pyqtgraph/graphicsItems/AxisItem.py +++ b/pyqtgraph/graphicsItems/AxisItem.py @@ -4,6 +4,7 @@ from ..python2_3 import asUnicode import numpy as np from ..Point import Point from .. import debug as debug +from math import ceil, floor, log, log10, isfinite import sys import weakref from .. import functions as fn @@ -156,7 +157,7 @@ class AxisItem(GraphicsWidget): tickAlpha (float or int or None) If None, pyqtgraph will draw the ticks with the alpha it deems appropriate. Otherwise, the alpha will be fixed at the value passed. With int, - accepted values are [0..255]. With vaule of type + accepted values are [0..255]. With value of type float, accepted values are from [0..1]. =================== ======================================================= @@ -513,7 +514,7 @@ class AxisItem(GraphicsWidget): def setRange(self, mn, mx): """Set the range of values displayed by the axis. Usually this is handled automatically by linking the axis to a ViewBox with :func:`linkToView `""" - if any(np.isinf((mn, mx))) or any(np.isnan((mn, mx))): + if not isfinite(mn) or not isfinite(mx): raise Exception("Not setting range to [%s, %s]" % (str(mn), str(mx))) self.range = [mn, mx] if self.autoSIPrefix: @@ -682,13 +683,13 @@ class AxisItem(GraphicsWidget): return [] ## decide optimal minor tick spacing in pixels (this is just aesthetics) - optimalTickCount = max(2., np.log(size)) + optimalTickCount = max(2., log(size)) ## optimal minor tick spacing optimalSpacing = dif / optimalTickCount ## the largest power-of-10 spacing which is smaller than optimal - p10unit = 10 ** np.floor(np.log10(optimalSpacing)) + p10unit = 10 ** floor(log10(optimalSpacing)) ## Determine major/minor tick spacings which flank the optimal spacing. intervals = np.array([1., 2., 10., 20., 100.]) * p10unit @@ -760,7 +761,7 @@ class AxisItem(GraphicsWidget): spacing, offset = tickLevels[i] ## determine starting tick - start = (np.ceil((minVal-offset) / spacing) * spacing) + offset + start = (ceil((minVal-offset) / spacing) * spacing) + offset ## determine number of ticks num = int((maxVal-start) / spacing) + 1 @@ -768,7 +769,7 @@ class AxisItem(GraphicsWidget): ## remove any ticks that were present in higher levels ## we assume here that if the difference between a tick value and a previously seen tick value ## is less than spacing/100, then they are 'equal' and we can ignore the new tick. - values = list(filter(lambda x: all(np.abs(allValues-x) > spacing/self.scale*0.01), values)) + values = list(filter(lambda x: np.all(np.abs(allValues-x) > spacing/self.scale*0.01), values)) allValues = np.concatenate([allValues, values]) ticks.append((spacing/self.scale, values)) @@ -797,8 +798,8 @@ class AxisItem(GraphicsWidget): ticks.append((spacing, t)) if len(ticks) < 3: - v1 = int(np.floor(minVal)) - v2 = int(np.ceil(maxVal)) + v1 = int(floor(minVal)) + v2 = int(ceil(maxVal)) #major = list(range(v1+1, v2)) minor = [] @@ -824,7 +825,7 @@ class AxisItem(GraphicsWidget): if self.logMode: return self.logTickStrings(values, scale, spacing) - places = max(0, np.ceil(-np.log10(spacing*scale))) + places = max(0, ceil(-log10(spacing*scale))) strings = [] for v in values: vs = v * scale @@ -960,8 +961,6 @@ class AxisItem(GraphicsWidget): ## compute coordinates to draw ticks ## draw three different intervals, long ticks first tickSpecs = [] - tickPen = self.pen() # generate alpha-adjusted pen for gridlines - orig_color = tickPen.color() for i in range(len(tickLevels)): tickPositions.append([]) ticks = tickLevels[i][1] @@ -971,9 +970,9 @@ class AxisItem(GraphicsWidget): lineAlpha = self.style["tickAlpha"] if lineAlpha is None: - lineAlpha = 255 / (i+1) - if self.grid is not False: - lineAlpha *= self.grid/255. * np.clip((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.) + lineAlpha = (255 * 2) // (i+2) # (was 1/(i+1) falling too quickly over first steps + # if self.grid is not False: + # lineAlpha *= self.grid/255. * fn.clip_scalar((0.05 * lengthInPixels / (len(ticks)+1)), 0., 1.) elif isinstance(lineAlpha, float): lineAlpha *= 255 lineAlpha = max(0, int(round(lineAlpha))) @@ -984,9 +983,11 @@ class AxisItem(GraphicsWidget): else: raise TypeError("Line Alpha should be of type None, float or int") - color = tickPen.color() # this copy is independent from orig_color - color.setAlpha(int(lineAlpha)) - tickPen.setColor(color) + # tickColor.setAlpha(int(lineAlpha)) # independent copy of color + tickPen = QtGui.QPen ( self.pen() ) + tickColor = tickPen.color() + tickColor.setAlpha( int(lineAlpha) ) + tickPen.setColor( tickColor ) for v in ticks: ## determine actual position to draw this tick x = (v * xScale) - offset @@ -1002,10 +1003,8 @@ class AxisItem(GraphicsWidget): if self.grid is False: p2[axis] += tickLength*tickDir tickSpecs.append((tickPen, Point(p1), Point(p2))) - tickPen.setColor(orig_color) # restore profiler('compute ticks') - if self.style['stopAxisAtTick'][0] is True: minTickPosition = min(map(min, tickPositions)) if axis == 0: @@ -1024,7 +1023,6 @@ class AxisItem(GraphicsWidget): span[1].setX(stop) axisSpec = (self.pen(), span[0], span[1]) - textOffset = self.style['tickTextOffset'][axis] ## spacing between axis and text #if self.style['autoExpandTextSpace'] is True: #textWidth = self.textWidth diff --git a/pyqtgraph/graphicsItems/ColorBarItem.py b/pyqtgraph/graphicsItems/ColorBarItem.py new file mode 100644 index 00000000..86b7e9c8 --- /dev/null +++ b/pyqtgraph/graphicsItems/ColorBarItem.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +from ..Qt import QtCore +from .. import functions as fn +from .PlotItem import PlotItem +from .ImageItem import ImageItem +from .LinearRegionItem import LinearRegionItem + +import weakref +import math +import numpy as np + +__all__ = ['ColorBarItem'] + +class ColorBarItem(PlotItem): + """ + **Bases:** :class:`PlotItem ` + + ColorBarItem is a simpler, compact alternative to HistogramLUTItem, without histogram + or the option to adjust the look-up table. + + A labeled axis is displayed directly next to the gradient to help identify values. + Handles included in the color bar allow for interactive adjustment. + + A ColorBarItem can be assigned one or more ImageItems that will be displayed according + to the selected color map and levels. The ColorBarItem can be used as a separate + element in a GraphicsLayout or added to the layout of a PlotItem used to display image + data with coordinate axes. + + ============================= ============================================= + **Signals:** + sigLevelsChanged(self) Emitted when the range sliders are moved + sigLevelsChangeFinished(self) Emitted when the range sliders are released + ============================= ============================================= + """ + sigLevelsChanged = QtCore.Signal(object) + sigLevelsChangeFinished = QtCore.Signal(object) + + def __init__(self, values=(0,1), width=25, cmap=None, label=None, + interactive=True, limits=None, rounding=1, + orientation='vertical', pen='w', hoverPen='r', hoverBrush='#FF000080' ): + """ + Create a new ColorBarItem. + + ============== =========================================================================== + **Arguments:** + values The range of values as tuple (min, max) + width (default=25) The width of the displayed color bar + cmap ColorMap object, look-up table is also applied to assigned ImageItem(s) + label (default=None) Label applied to color bar axis + interactive (default=True) Handles are displayed to interactively adjust level range + limits Limits to adjustment range as (low, high) tuple, None disables limit + rounding (default=1) Adjusted range values are rounded to multiples of this values + orientation (default='vertical') 'horizontal' gives a horizontal color bar + pen color of adjustement handles in interactive mode + hoverPen color of adjustement handles when hovered over + hoverBrush color of movable center region when hovered over + ============== =========================================================================== + """ + super().__init__() + self.img_list = [] # list of controlled ImageItems + self.values = values + self.cmap = cmap + self.rounding = rounding + self.horizontal = bool(orientation == 'horizontal') + + self.lo_prv, self.hi_prv = self.values # remember previous values while adjusting range + if limits is None: + self.lo_lim = None + self.hi_lim = None + else: + self.lo_lim, self.hi_lim = limits + + self.disableAutoRange() + self.hideButtons() + self.setMouseEnabled(x=False, y=False) + self.setMenuEnabled( False) + + if self.horizontal: + self.setRange( xRange=(0,256), yRange=(0,1), padding=0 ) + self.layout.setRowFixedHeight(2, width) + else: + self.setRange( xRange=(0,1), yRange=(0,256), padding=0 ) + self.layout.setColumnFixedWidth(1, width) # width of color bar + + for key in ['left','right','top','bottom']: + self.showAxis(key) + axis = self.getAxis(key) + axis.setZValue(1) + # select main axis: + if self.horizontal and key == 'bottom': + self.axis = axis + elif not self.horizontal and key == 'right': + self.axis = axis + self.axis.setWidth(45) + else: # show other axes to create frame + axis.setStyle( showValues=False, tickLength=0 ) + self.axis.setStyle( showValues=True ) + self.axis.unlinkFromView() + self.axis.setRange( self.values[0], self.values[1] ) + + self.bar = ImageItem(axisOrder='col-major') + if self.horizontal: + self.bar.setImage( np.linspace(0, 1, 256).reshape( (-1,1) ) ) + if label is not None: self.getAxis('bottom').setLabel(label) + else: + self.bar.setImage( np.linspace(0, 1, 256).reshape( (1,-1) ) ) + if label is not None: self.getAxis('left').setLabel(label) + self.addItem(self.bar) + if cmap is not None: self.setCmap(cmap) + + if interactive: + if self.horizontal: + align = 'vertical' + else: + align = 'horizontal' + self.region = LinearRegionItem( + [63, 191], align, swapMode='block', + # span=(0.15, 0.85), # limited span looks better, but disables grabbing the region + pen=pen, brush=fn.mkBrush(None), hoverPen=hoverPen, hoverBrush=hoverBrush ) + self.region.setZValue(1000) + self.region.lines[0].addMarker('<|>', size=6) + self.region.lines[1].addMarker('<|>', size=6) + self.region.sigRegionChanged.connect(self._regionChanging) + self.region.sigRegionChangeFinished.connect(self._regionChanged) + self.addItem(self.region) + self.region_changed_enable = True + self.region.setRegion( (63, 191) ) # place handles at 25% and 75% locations + else: + self.region = None + self.region_changed_enable = False + + def setImageItem(self, img, insert_in=None): + """ + assign ImageItem or list of ImageItems to be controlled + + ============== ========================================================================== + **Arguments:** + image ImageItem or list of [ImageItem, ImageItem, ...] that will be set to the + color map of the ColorBarItem. In interactive mode, the levels of all + assigned ImageItems will be controlled simultaneously. + insert_in If a PlotItem is given, the color bar is inserted on the right or bottom + of the plot + ============== ========================================================================== + """ + try: + self.img_list = [ weakref.ref(item) for item in img ] + except TypeError: # failed to iterate, make a single-item list + self.img_list = [ weakref.ref( img ) ] + if insert_in is not None: + if self.horizontal: + insert_in.layout.addItem( self, 5, 1 ) # insert in layout below bottom axis + insert_in.layout.setRowFixedHeight(4, 10) # enforce some space to axis above + else: + insert_in.layout.addItem( self, 2, 5 ) # insert in layout after right-hand axis + insert_in.layout.setColumnFixedWidth(4, 5) # enforce some space to axis on the left + self._update_items( update_cmap = True ) + + def setCmap(self, cmap): + """ + sets a ColorMap object to determine the ColorBarItem's look-up table. The same + look-up table is applied to any assigned ImageItem. + """ + self.cmap = cmap + self._update_items( update_cmap = True ) + + def setLevels(self, values=None, low=None, high=None ): + """ + Sets the displayed range of levels as specified. + + ============== =========================================================================== + **Arguments:** + values Specify levels by tuple (low, high). Either value can be None to leave + to previous value unchanged. Takes precedence over low and high parameters. + low new low level to be applied to color bar and assigned images + high new high level to be applied to color bar and assigned images + ============== =========================================================================== + """ + if values is not None: # values setting takes precendence + low, high = values + lo_new, hi_new = low, high + lo_cur, hi_cur = self.values + # allow None values to preserve original values: + if lo_new is None: lo_new = lo_cur + if hi_new is None: hi_new = hi_cur + if lo_new > hi_new: # prevent reversal + lo_new = hi_new = (lo_new + hi_new) / 2 + # clip to limits if set: + if self.lo_lim is not None and lo_new < self.lo_lim: lo_new = self.lo_lim + if self.hi_lim is not None and hi_new > self.hi_lim: hi_new = self.hi_lim + self.values = self.lo_prv, self.hi_prv = (lo_new, hi_new) + self._update_items() + + def levels(self): + """ returns the currently set levels as the tuple (low, high). """ + return self.values + + def _update_items(self, update_cmap=False): + """ internal: update color maps for bar and assigned ImageItems """ + # update color bar: + self.axis.setRange( self.values[0], self.values[1] ) + if update_cmap and self.cmap is not None: + self.bar.setLookupTable( self.cmap.getLookupTable(nPts=256) ) + # update assigned ImageItems, too: + for img_weakref in self.img_list: + img = img_weakref() + if img is None: continue # dereference weakref + img.setLevels( self.values ) # (min,max) tuple + if update_cmap and self.cmap is not None: + img.setLookupTable( self.cmap.getLookupTable(nPts=256) ) + + def _regionChanged(self): + """ internal: snap adjusters back to default positions on release """ + self.lo_prv, self.hi_prv = self.values + self.region_changed_enable = False # otherwise this affects the region again + self.region.setRegion( (63, 191) ) + self.region_changed_enable = True + self.sigLevelsChangeFinished.emit(self) + + def _regionChanging(self): + """ internal: recalculate levels based on new position of adjusters """ + if not self.region_changed_enable: return + bot, top = self.region.getRegion() + bot = ( (bot - 63) / 64 ) # -1 to +1 over half-bar range + top = ( (top - 191) / 64 ) # -1 to +1 over half-bar range + bot = math.copysign( bot**2, bot ) # quadratic behaviour for sensitivity to small changes + top = math.copysign( top**2, top ) + # These are the new values if adjuster is released now, rate of change depends on original separation + span_prv = self.hi_prv - self.lo_prv # previous span of values + hi_new = self.hi_prv + (span_prv + 2*self.rounding) * top # make sure that we can always + lo_new = self.lo_prv + (span_prv + 2*self.rounding) * bot # reach 2x the minimal step + # Alternative model with speed depending on level magnitude: + # mean_val = abs(self.lo_prv) + abs(self.hi_prv) / 2 + # hi_new = self.hi_prv + (mean_val + 2*self.rounding) * top # make sure that we can always + # lo_new = self.lo_prv + (mean_val + 2*self.rounding) * bot # reach 2x the minimal step + + if self.hi_lim is not None and hi_new > self.hi_lim: # limit maximum value + # print('lim +') + hi_new = self.hi_lim + if lo_new > hi_new - span_prv: # avoid collapsing the span against top or bottom limits + lo_new = hi_new - span_prv + if self.lo_lim is not None and lo_new < self.lo_lim: # limit minimum value + # print('lim -') + lo_new = self.lo_lim + if hi_new < lo_new + span_prv: # avoid collapsing the span against top or bottom limits + hi_new = lo_new + span_prv + if lo_new + self.rounding > hi_new: # do not allow less than one "rounding" unit of span + # print('lim X') + if bot == 0: hi_new = lo_new + self.rounding + elif top == 0: lo_new = hi_new - self.rounding + else: + lo_new = (lo_new + hi_new - self.rounding) / 2 + hi_new = lo_new + self.rounding + lo_new = self.rounding * round( lo_new/self.rounding ) + hi_new = self.rounding * round( hi_new/self.rounding ) + # if hi_new == lo_new: hi_new = lo_new + self.rounding # hack solution if axis span still collapses + self.values = (lo_new, hi_new) + self._update_items() + self.sigLevelsChanged.emit(self) diff --git a/pyqtgraph/graphicsItems/CurvePoint.py b/pyqtgraph/graphicsItems/CurvePoint.py index 368a0a82..c7052ddb 100644 --- a/pyqtgraph/graphicsItems/CurvePoint.py +++ b/pyqtgraph/graphicsItems/CurvePoint.py @@ -1,6 +1,7 @@ +from math import atan2, degrees from ..Qt import QtGui, QtCore from . import ArrowItem -import numpy as np +from ..functions import clip_scalar from ..Point import Point import weakref from .GraphicsObject import GraphicsObject @@ -61,26 +62,26 @@ class CurvePoint(GraphicsObject): pos = self.property('position') if 'QVariant' in repr(pos): ## need to support 2 APIs :( pos = pos.toDouble()[0] - index = (len(x)-1) * np.clip(pos, 0.0, 1.0) + index = (len(x)-1) * clip_scalar(pos, 0.0, 1.0) if index != int(index): ## interpolate floating-point values i1 = int(index) - i2 = np.clip(i1+1, 0, len(x)-1) + i2 = clip_scalar(i1+1, 0, len(x)-1) s2 = index-i1 s1 = 1.0-s2 newPos = (x[i1]*s1+x[i2]*s2, y[i1]*s1+y[i2]*s2) else: index = int(index) - i1 = np.clip(index-1, 0, len(x)-1) - i2 = np.clip(index+1, 0, len(x)-1) + i1 = clip_scalar(index-1, 0, len(x)-1) + i2 = clip_scalar(index+1, 0, len(x)-1) newPos = (x[index], y[index]) p1 = self.parentItem().mapToScene(QtCore.QPointF(x[i1], y[i1])) p2 = self.parentItem().mapToScene(QtCore.QPointF(x[i2], y[i2])) - ang = np.arctan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians + rads = atan2(p2.y()-p1.y(), p2.x()-p1.x()) ## returns radians self.resetTransform() if self._rotate: - self.setRotation(180 + np.rad2deg(ang)) ## takes degrees + self.setRotation(180 + degrees(rads)) QtGui.QGraphicsItem.setPos(self, *newPos) return True diff --git a/pyqtgraph/graphicsItems/DateAxisItem.py b/pyqtgraph/graphicsItems/DateAxisItem.py index 3f77cef2..7cfdefff 100644 --- a/pyqtgraph/graphicsItems/DateAxisItem.py +++ b/pyqtgraph/graphicsItems/DateAxisItem.py @@ -185,6 +185,17 @@ MS_ZOOM_LEVEL = ZoomLevel([ autoSkip=[1, 5, 10, 25]) ], "99:99:99") + +def getOffsetFromUtc(): + """Retrieve the utc offset respecting the daylight saving time""" + ts = time.localtime() + if ts.tm_isdst: + utc_offset = time.altzone + else: + utc_offset = time.timezone + return utc_offset + + class DateAxisItem(AxisItem): """ **Bases:** :class:`AxisItem ` @@ -200,7 +211,7 @@ class DateAxisItem(AxisItem): """ - def __init__(self, orientation='bottom', utcOffset=time.timezone, **kwargs): + def __init__(self, orientation='bottom', utcOffset=None, **kwargs): """ Create a new DateAxisItem. @@ -211,6 +222,8 @@ class DateAxisItem(AxisItem): super(DateAxisItem, self).__init__(orientation, **kwargs) # Set the zoom level to use depending on the time density on the axis + if utcOffset is None: + utcOffset = getOffsetFromUtc() self.utcOffset = utcOffset self.zoomLevels = OrderedDict([ diff --git a/pyqtgraph/graphicsItems/FillBetweenItem.py b/pyqtgraph/graphicsItems/FillBetweenItem.py index b16be853..02c5feca 100644 --- a/pyqtgraph/graphicsItems/FillBetweenItem.py +++ b/pyqtgraph/graphicsItems/FillBetweenItem.py @@ -21,6 +21,7 @@ class FillBetweenItem(QtGui.QGraphicsPathItem): self.updatePath() def setBrush(self, *args, **kwds): + """Change the fill brush. Acceps the same arguments as pg.mkBrush()""" QtGui.QGraphicsPathItem.setBrush(self, fn.mkBrush(*args, **kwds)) def setPen(self, *args, **kwds): @@ -50,10 +51,6 @@ class FillBetweenItem(QtGui.QGraphicsPathItem): self.setZValue(min(curve1.zValue(), curve2.zValue())-1) self.curveChanged() - def setBrush(self, *args, **kwds): - """Change the fill brush. Acceps the same arguments as pg.mkBrush()""" - QtGui.QGraphicsPathItem.setBrush(self, fn.mkBrush(*args, **kwds)) - def curveChanged(self): self.updatePath() diff --git a/pyqtgraph/graphicsItems/GradientLegend.py b/pyqtgraph/graphicsItems/GradientLegend.py index 28c2cd63..649a23bc 100644 --- a/pyqtgraph/graphicsItems/GradientLegend.py +++ b/pyqtgraph/graphicsItems/GradientLegend.py @@ -6,7 +6,7 @@ __all__ = ['GradientLegend'] class GradientLegend(UIGraphicsItem): """ - Draws a color gradient rectangle along with text labels denoting the value at specific + Draws a color gradient rectangle along with text labels denoting the value at specific points along the gradient. """ @@ -50,6 +50,9 @@ class GradientLegend(UIGraphicsItem): if unit[0] is None: return + ## Have to scale painter so that text and gradients are correct size and not upside down + p.scale(unit[0], -unit[1]) + ## determine max width of all labels labelWidth = 0 labelHeight = 0 @@ -58,57 +61,54 @@ class GradientLegend(UIGraphicsItem): labelWidth = max(labelWidth, b.width()) labelHeight = max(labelHeight, b.height()) - labelWidth *= unit[0] - labelHeight *= unit[1] - textPadding = 2 # in px + xR = rect.right() / unit[0] + xL = rect.left() / unit[0] + yB = -(rect.top() / unit[1]) + yT = -(rect.bottom() / unit[1]) + + # coordinates describe edges of text and bar, additional margins will be added for background if self.offset[0] < 0: - x3 = rect.right() + unit[0] * self.offset[0] - x2 = x3 - labelWidth - unit[0]*textPadding*2 - x1 = x2 - unit[0] * self.size[0] + x3 = xR + self.offset[0] # right edge from right edge of view, offset is negative! + x2 = x3 - labelWidth - 2*textPadding # right side of color bar + x1 = x2 - self.size[0] # left side of color bar else: - x1 = rect.left() + unit[0] * self.offset[0] - x2 = x1 + unit[0] * self.size[0] - x3 = x2 + labelWidth + unit[0]*textPadding*2 + x1 = xL + self.offset[0] # left edge from left edge of view + x2 = x1 + self.size[0] + x3 = x2 + labelWidth + 2*textPadding # leave room for 2x textpadding between bar and text if self.offset[1] < 0: - y2 = rect.top() - unit[1] * self.offset[1] - y1 = y2 + unit[1] * self.size[1] + y2 = yB + self.offset[1] # bottom edge from bottom of view, offset is negative! + y1 = y2 - self.size[1] else: - y1 = rect.bottom() - unit[1] * self.offset[1] - y2 = y1 - unit[1] * self.size[1] + y1 = yT + self.offset[1] # top edge from top of view + y2 = y1 + self.size[1] self.b = [x1,x2,x3,y1,y2,labelWidth] - + ## Draw background p.setPen(self.pen) p.setBrush(QtGui.QBrush(QtGui.QColor(255,255,255,100))) rect = QtCore.QRectF( - QtCore.QPointF(x1 - unit[0]*textPadding, y1 + labelHeight/2 + unit[1]*textPadding), - QtCore.QPointF(x3, y2 - labelHeight/2 - unit[1]*textPadding) + QtCore.QPointF(x1 - textPadding, y1-labelHeight/2 - textPadding), # extra left/top padding + QtCore.QPointF(x3 + textPadding, y2+labelHeight/2 + textPadding) # extra bottom/right padding ) p.drawRect(rect) - - - ## Have to scale painter so that text and gradients are correct size. Bleh. - p.scale(unit[0], unit[1]) - + ## Draw color bar - self.gradient.setStart(0, y1/unit[1]) - self.gradient.setFinalStop(0, y2/unit[1]) + self.gradient.setStart(0, y1) + self.gradient.setFinalStop(0, y2) p.setBrush(self.gradient) rect = QtCore.QRectF( - QtCore.QPointF(x1/unit[0], y1/unit[1]), - QtCore.QPointF(x2/unit[0], y2/unit[1]) + QtCore.QPointF(x1, y1), + QtCore.QPointF(x2, y2) ) p.drawRect(rect) - - + ## draw labels p.setPen(QtGui.QPen(QtGui.QColor(0,0,0))) - tx = x2 + unit[0]*textPadding - lh = labelHeight/unit[1] + tx = x2 + 2 * textPadding # margin between bar and text + lh = labelHeight + lw = labelWidth for k in self.labels: y = y1 + self.labels[k] * (y2-y1) - p.drawText(QtCore.QRectF(tx/unit[0], y/unit[1] - lh/2.0, 1000, lh), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(k)) - - + p.drawText(QtCore.QRectF(tx, y - lh/2, lw, lh), QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter, str(k)) diff --git a/pyqtgraph/graphicsItems/GraphItem.py b/pyqtgraph/graphicsItems/GraphItem.py index 0c279b30..9c4976fd 100644 --- a/pyqtgraph/graphicsItems/GraphItem.py +++ b/pyqtgraph/graphicsItems/GraphItem.py @@ -55,7 +55,9 @@ class GraphItem(GraphicsObject): """ if 'adj' in kwds: self.adjacency = kwds.pop('adj') - if self.adjacency is not None and self.adjacency.dtype.kind not in 'iu': + if hasattr(self.adjacency, '__len__') and len(self.adjacency) == 0: + self.adjacency = None + elif self.adjacency is not None and self.adjacency.dtype.kind not in 'iu': raise Exception("adjacency must be None or an array of either int or unsigned type.") self._update() if 'pos' in kwds: @@ -140,8 +142,3 @@ class GraphItem(GraphicsObject): def pixelPadding(self): return self.scatter.pixelPadding() - - - - - diff --git a/pyqtgraph/graphicsItems/GraphicsItem.py b/pyqtgraph/graphicsItems/GraphicsItem.py index 61f48a45..5ba261b0 100644 --- a/pyqtgraph/graphicsItems/GraphicsItem.py +++ b/pyqtgraph/graphicsItems/GraphicsItem.py @@ -1,4 +1,5 @@ import warnings +from math import hypot from collections import OrderedDict from functools import reduce from ..Qt import QtGui, QtCore, isQObjectAlive @@ -308,7 +309,7 @@ class GraphicsItem(object): v = self.pixelVectors() if v == (None, None): return None, None - return (v[0].x()**2+v[0].y()**2)**0.5, (v[1].x()**2+v[1].y()**2)**0.5 + return (hypot(v[0].x(), v[0].y()), hypot(v[1].x(), v[1].y())) # lengths def pixelWidth(self): ## deprecated @@ -437,14 +438,11 @@ class GraphicsItem(object): """ if relativeItem is None: relativeItem = self.parentItem() - tr = self.itemTransform(relativeItem) if isinstance(tr, tuple): ## difference between pyside and pyqt tr = tr[0] - #vec = tr.map(Point(1,0)) - tr.map(Point(0,0)) vec = tr.map(QtCore.QLineF(0,0,1,0)) - #return Point(vec).angle(Point(1,0)) return vec.angleTo(QtCore.QLineF(vec.p1(), vec.p1()+QtCore.QPointF(1,0))) #def itemChange(self, change, value): @@ -555,6 +553,8 @@ class GraphicsItem(object): """ Called whenever the view coordinates of the ViewBox containing this item have changed. """ + # when this is called, _cachedView is not invalidated. + # this means that for functions overriding viewRangeChanged, viewRect() may be stale. pass def viewTransformChanged(self): diff --git a/pyqtgraph/graphicsItems/GraphicsObject.py b/pyqtgraph/graphicsItems/GraphicsObject.py index 743bd755..67d85450 100644 --- a/pyqtgraph/graphicsItems/GraphicsObject.py +++ b/pyqtgraph/graphicsItems/GraphicsObject.py @@ -20,7 +20,7 @@ class GraphicsObject(GraphicsItem, QtGui.QGraphicsObject): self.setFlag(self.ItemSendsGeometryChanges) GraphicsItem.__init__(self) # fn.NAMED_COLOR_MANAGER.paletteChangeSignal.connect(self.styleChange) - fn.NAMED_COLOR_MANAGER.paletteHasChangedSignal.connect(self.styleHasChanged) + fn.COLOR_REGISTRY.paletteHasChangedSignal.connect(self.styleHasChanged) def itemChange(self, change, value): ret = super().itemChange(change, value) diff --git a/pyqtgraph/graphicsItems/GraphicsWidget.py b/pyqtgraph/graphicsItems/GraphicsWidget.py index 90588945..6d3c10f9 100644 --- a/pyqtgraph/graphicsItems/GraphicsWidget.py +++ b/pyqtgraph/graphicsItems/GraphicsWidget.py @@ -17,7 +17,7 @@ class GraphicsWidget(GraphicsItem, QtGui.QGraphicsWidget): """ QtGui.QGraphicsWidget.__init__(self, *args, **kargs) GraphicsItem.__init__(self) - fn.NAMED_COLOR_MANAGER.paletteHasChangedSignal.connect(self.styleHasChanged) + fn.COLOR_REGISTRY.paletteHasChangedSignal.connect(self.styleHasChanged) ## done by GraphicsItem init #GraphicsScene.registerObject(self) ## workaround for pyqt bug in graphicsscene.items() diff --git a/pyqtgraph/graphicsItems/GridItem.py b/pyqtgraph/graphicsItems/GridItem.py index 4cb30bf8..312d7b62 100644 --- a/pyqtgraph/graphicsItems/GridItem.py +++ b/pyqtgraph/graphicsItems/GridItem.py @@ -126,8 +126,7 @@ class GridItem(UIGraphicsItem): for i in range(self.grid_depth - 1, -1, -1): dist = br-ul nlTarget = 10.**i - - d = 10. ** np.floor(np.log10(abs(dist/nlTarget))+0.5) + d = 10. ** np.floor(np.log10(np.abs(dist/nlTarget))+0.5) for ax in range(0,2): ts = self.opts['tickSpacing'][ax] try: @@ -141,11 +140,6 @@ class GridItem(UIGraphicsItem): br1 = np.ceil(br / d) * d dist = br1-ul1 nl = (dist / d) + 0.5 - #print "level", i - #print " dim", dim - #print " dist", dist - #print " d", d - #print " nl", nl for ax in range(0,2): ## Draw grid for both axes if i >= len(self.opts['tickSpacing'][ax]): continue @@ -153,7 +147,7 @@ class GridItem(UIGraphicsItem): continue ppl = dim[ax] / nl[ax] - c = np.clip(5 * (ppl-3), 0., 50.).astype(int) + c = int(fn.clip_scalar(5 * (ppl-3), 0, 50)) linePen = self.opts['pen'] lineColor = self.opts['pen'].color() diff --git a/pyqtgraph/graphicsItems/HistogramLUTItem.py b/pyqtgraph/graphicsItems/HistogramLUTItem.py index 89c45568..20ed2d6f 100644 --- a/pyqtgraph/graphicsItems/HistogramLUTItem.py +++ b/pyqtgraph/graphicsItems/HistogramLUTItem.py @@ -46,6 +46,8 @@ class HistogramLUTItem(GraphicsWidget): black/white level lines is drawn, and the levels apply to all channels in the image. If 'rgba', then one set of levels is drawn for each channel. + gradientPosition 'right' (default) OR 'left'. Which side of the histogram to + put the LUT gradient. ================ =========================================================== """ @@ -53,12 +55,13 @@ class HistogramLUTItem(GraphicsWidget): sigLevelsChanged = QtCore.Signal(object) sigLevelChangeFinished = QtCore.Signal(object) - def __init__(self, image=None, fillHistogram=True, rgbHistogram=False, levelMode='mono'): + def __init__(self, image=None, fillHistogram=True, rgbHistogram=False, levelMode='mono', gradientPosition='right'): GraphicsWidget.__init__(self) self.lut = None self.imageItem = lambda: None # fake a dead weakref self.levelMode = levelMode self.rgbHistogram = rgbHistogram + self.gradientPosition = gradientPosition self.layout = QtGui.QGraphicsGridLayout() self.setLayout(self.layout) @@ -69,7 +72,7 @@ class HistogramLUTItem(GraphicsWidget): self.vb.setMinimumWidth(45) self.vb.setMouseEnabled(x=False, y=True) self.gradient = GradientEditorItem() - self.gradient.setOrientation('right') + self.gradient.setOrientation(gradientPosition) self.gradient.loadPreset('grey') self.regions = [ LinearRegionItem([0, 1], 'horizontal', swapMode='block'), @@ -94,7 +97,9 @@ class HistogramLUTItem(GraphicsWidget): self.axis = AxisItem('left', linkView=self.vb, maxTickLength=-10, parent=self) self.layout.addItem(self.axis, 0, 0) self.layout.addItem(self.vb, 0, 1) - self.layout.addItem(self.gradient, 0, 2) + pos = (0, 2) if gradientPosition == 'right' else (2, 0) + self.layout.addItem(self.axis, 0, pos[0]) + self.layout.addItem(self.gradient, 0, pos[1]) self.range = None self.gradient.setFlag(self.gradient.ItemStacksBehindParent) self.vb.setFlag(self.gradient.ItemStacksBehindParent) @@ -134,7 +139,7 @@ class HistogramLUTItem(GraphicsWidget): plot.setFillLevel(None) def paint(self, p, *args): - if self.levelMode != 'mono': + if self.levelMode != 'mono' or not self.region.isVisible(): return pen = self.region.lines[0].pen @@ -145,11 +150,15 @@ class HistogramLUTItem(GraphicsWidget): p.setRenderHint(QtGui.QPainter.Antialiasing) for pen in [fn.mkPen((0, 0, 0, 100), width=3), pen]: p.setPen(pen) - p.drawLine(p1 + Point(0, 5), gradRect.bottomLeft()) - p.drawLine(p2 - Point(0, 5), gradRect.topLeft()) + if self.gradientPosition == 'right': + p.drawLine(p1 + Point(0, 5), gradRect.bottomLeft()) + p.drawLine(p2 - Point(0, 5), gradRect.topLeft()) + else: + p.drawLine(p1 + Point(0, 5), gradRect.bottomRight()) + p.drawLine(p2 - Point(0, 5), gradRect.topRight()) p.drawLine(gradRect.topLeft(), gradRect.topRight()) p.drawLine(gradRect.bottomLeft(), gradRect.bottomRight()) - + def setHistogramRange(self, mn, mx, padding=0.1): """Set the Y range on the histogram plot. This disables auto-scaling.""" self.vb.enableAutoRange(self.vb.YAxis, False) diff --git a/pyqtgraph/graphicsItems/ImageItem.py b/pyqtgraph/graphicsItems/ImageItem.py index 14b44bec..107334bf 100644 --- a/pyqtgraph/graphicsItems/ImageItem.py +++ b/pyqtgraph/graphicsItems/ImageItem.py @@ -60,7 +60,6 @@ class ImageItem(GraphicsObject): self._displayBuffer = None self._renderRequired = True self._unrenderable = False - self._cupy = getCupy() self._xp = None # either numpy or cupy, to match the image data self._defferedLevels = None @@ -216,7 +215,7 @@ class ImageItem(GraphicsObject): def _buildQImageBuffer(self, shape): self._displayBuffer = numpy.empty(shape[:2] + (4,), dtype=numpy.ubyte) - if self._xp == self._cupy: + if self._xp == getCupy(): self._processingBuffer = self._xp.empty(shape[:2] + (4,), dtype=self._xp.ubyte) else: self._processingBuffer = self._displayBuffer @@ -273,7 +272,8 @@ class ImageItem(GraphicsObject): return else: old_xp = self._xp - self._xp = self._cupy.get_array_module(image) if self._cupy else numpy + cp = getCupy() + self._xp = cp.get_array_module(image) if cp else numpy gotNewData = True processingSubstrateChanged = old_xp != self._xp if processingSubstrateChanged: @@ -419,21 +419,18 @@ class ImageItem(GraphicsObject): # if the image data is a small int, then we can combine levels + lut # into a single lut for better performance levels = self.levels - if levels is not None and levels.ndim == 1 and image.dtype in (self._xp.ubyte, self._xp.uint16): + if levels is not None and lut is not None and levels.ndim == 1 and \ + image.dtype in (self._xp.ubyte, self._xp.uint16): if self._effectiveLut is None: eflsize = 2**(image.itemsize*8) ind = self._xp.arange(eflsize) minlev, maxlev = levels levdiff = maxlev - minlev levdiff = 1 if levdiff == 0 else levdiff # don't allow division by 0 - if lut is None: - efflut = fn.rescaleData(ind, scale=255./levdiff, - offset=minlev, dtype=self._xp.ubyte) - else: - lutdtype = self._xp.min_scalar_type(lut.shape[0] - 1) - efflut = fn.rescaleData(ind, scale=(lut.shape[0]-1)/levdiff, - offset=minlev, dtype=lutdtype, clip=(0, lut.shape[0]-1)) - efflut = lut[efflut] + lutdtype = self._xp.min_scalar_type(lut.shape[0] - 1) + efflut = fn.rescaleData(ind, scale=(lut.shape[0]-1)/levdiff, + offset=minlev, dtype=lutdtype, clip=(0, lut.shape[0]-1)) + efflut = lut[efflut] self._effectiveLut = efflut lut = self._effectiveLut @@ -452,7 +449,7 @@ class ImageItem(GraphicsObject): self._buildQImageBuffer(image.shape) fn.makeARGB(image, lut=lut, levels=levels, output=self._processingBuffer) - if self._xp == self._cupy: + if self._xp == getCupy(): self._processingBuffer.get(out=self._displayBuffer) self._renderRequired = False self._unrenderable = False @@ -538,22 +535,23 @@ class ImageItem(GraphicsObject): kwds['bins'] = bins + cp = getCupy() if perChannel: hist = [] for i in range(stepData.shape[-1]): stepChan = stepData[..., i] stepChan = stepChan[self._xp.isfinite(stepChan)] h = self._xp.histogram(stepChan, **kwds) - if self._cupy: - hist.append((self._cupy.asnumpy(h[1][:-1]), self._cupy.asnumpy(h[0]))) + if cp: + hist.append((cp.asnumpy(h[1][:-1]), cp.asnumpy(h[0]))) else: hist.append((h[1][:-1], h[0])) return hist else: stepData = stepData[self._xp.isfinite(stepData)] hist = self._xp.histogram(stepData, **kwds) - if self._cupy: - return self._cupy.asnumpy(hist[1][:-1]), self._cupy.asnumpy(hist[0]) + if cp: + return cp.asnumpy(hist[1][:-1]), cp.asnumpy(hist[0]) else: return hist[1][:-1], hist[0] diff --git a/pyqtgraph/graphicsItems/InfiniteLine.py b/pyqtgraph/graphicsItems/InfiniteLine.py index 74a8da95..c64a4684 100644 --- a/pyqtgraph/graphicsItems/InfiniteLine.py +++ b/pyqtgraph/graphicsItems/InfiniteLine.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from math import atan2, degrees from ..Qt import QtGui, QtCore from ..Point import Point from .GraphicsObject import GraphicsObject @@ -359,7 +360,7 @@ class InfiniteLine(GraphicsObject): up = tr.map(Point(left, 1)) dif = end - start length = Point(dif).length() - angle = np.arctan2(dif.y(), dif.x()) * 180 / np.pi + angle = degrees(atan2(dif.y(), dif.x())) p.translate(start) p.rotate(angle) @@ -592,7 +593,7 @@ class InfLineLabel(TextItem): return rel = self._posToRel(ev.pos()) - self.orthoPos = np.clip(self._startPosition + rel - self._cursorOffset, 0, 1) + self.orthoPos = fn.clip_scalar(self._startPosition + rel - self._cursorOffset, 0., 1.) self.updatePosition() if ev.isFinish(): self._moving = False diff --git a/pyqtgraph/graphicsItems/LabelItem.py b/pyqtgraph/graphicsItems/LabelItem.py index cda3e27f..931249d7 100644 --- a/pyqtgraph/graphicsItems/LabelItem.py +++ b/pyqtgraph/graphicsItems/LabelItem.py @@ -45,7 +45,7 @@ class LabelItem(GraphicsWidget, GraphicsWidgetAnchor): ==================== ============================== **Style Arguments:** - color (str) example: 'CCFF00' + color (str) example: '#CCFF00' size (str) example: '8pt' bold (bool) italic (bool) diff --git a/pyqtgraph/graphicsItems/LegendItem.py b/pyqtgraph/graphicsItems/LegendItem.py index 7b468498..9c25c78e 100644 --- a/pyqtgraph/graphicsItems/LegendItem.py +++ b/pyqtgraph/graphicsItems/LegendItem.py @@ -364,7 +364,7 @@ class ItemSample(GraphicsWidget): if (opts.get('fillLevel', None) is not None and opts.get('fillBrush', None) is not None): p.setBrush(fn.mkBrush(opts['fillBrush'])) - p.setPen(fn.mkPen(opts['fillBrush'])) + p.setPen(fn.mkPen(opts['pen'])) p.drawPolygon(QtGui.QPolygonF( [QtCore.QPointF(2, 18), QtCore.QPointF(18, 2), QtCore.QPointF(18, 18)])) diff --git a/pyqtgraph/graphicsItems/LinearRegionItem.py b/pyqtgraph/graphicsItems/LinearRegionItem.py index 6f888613..ab92a0a1 100644 --- a/pyqtgraph/graphicsItems/LinearRegionItem.py +++ b/pyqtgraph/graphicsItems/LinearRegionItem.py @@ -43,8 +43,9 @@ class LinearRegionItem(GraphicsObject): **Arguments:** values A list of the positions of the lines in the region. These are not limits; limits can be set by specifying bounds. - orientation Options are 'vertical' or 'horizontal', indicating the - The default is 'vertical', indicating that the + orientation Options are 'vertical' or 'horizontal' + The default is 'vertical', indicating that the region is bounded + by vertical lines. brush Defines the brush that fills the region. Can be any arguments that are valid for :func:`mkBrush `. Default is transparent blue. @@ -114,13 +115,15 @@ class LinearRegionItem(GraphicsObject): self.lines[1].sigPositionChanged.connect(self._line1Moved) if brush is None: - brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 50)) + # brush = QtGui.QBrush(QtGui.QColor(0, 0, 255, 50)) + brush = ('gr_reg') self.setBrush(brush) if hoverBrush is None: - c = self.brush.color() - c.setAlpha(min(c.alpha() * 2, 255)) - hoverBrush = fn.mkBrush(c) + hoverBrush = ('gr_reg', 200) + # c = self.brush.color() + # c.setAlpha(min(c.alpha() * 2, 255)) + # hoverBrush = fn.mkBrush(c) self.setHoverBrush(hoverBrush) self.setMovable(movable) diff --git a/pyqtgraph/graphicsItems/NonUniformImage.py b/pyqtgraph/graphicsItems/NonUniformImage.py index 2b697767..fc42bf27 100644 --- a/pyqtgraph/graphicsItems/NonUniformImage.py +++ b/pyqtgraph/graphicsItems/NonUniformImage.py @@ -1,4 +1,5 @@ from ..Qt import QtGui, QtCore +import math import numpy as np from ..colormap import ColorMap from .GraphicsObject import GraphicsObject @@ -94,7 +95,7 @@ class NonUniformImage(GraphicsObject): value = 0.0 elif np.isposinf(value): value = 1.0 - elif np.isnan(value): + elif math.isnan(value): continue # ignore NaN else: value = (value - mn) / (mx - mn) # normalize diff --git a/pyqtgraph/graphicsItems/PlotCurveItem.py b/pyqtgraph/graphicsItems/PlotCurveItem.py index e344ef2e..2afdad57 100644 --- a/pyqtgraph/graphicsItems/PlotCurveItem.py +++ b/pyqtgraph/graphicsItems/PlotCurveItem.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from ..Qt import QtCore, QtGui, QtWidgets HAVE_OPENGL = hasattr(QtWidgets, 'QOpenGLWidget') - +import math import warnings import numpy as np from .GraphicsObject import GraphicsObject @@ -131,6 +131,8 @@ class PlotCurveItem(GraphicsObject): elif ax == 1: d = y d2 = x + else: + raise ValueError("Invalid axis value") ## If an orthogonal range is specified, mask the data now if orthoRange is not None: @@ -149,7 +151,7 @@ class PlotCurveItem(GraphicsObject): # All-NaN data is acceptable; Explicit numpy warning is not needed. warnings.simplefilter("ignore") b = (np.nanmin(d), np.nanmax(d)) - if any(np.isinf(b)): + if math.isinf(b[0]) or math.isinf(b[1]): mask = np.isfinite(d) d = d[mask] if len(d) == 0: diff --git a/pyqtgraph/graphicsItems/PlotDataItem.py b/pyqtgraph/graphicsItems/PlotDataItem.py index 8bf1534c..c36f4ed1 100644 --- a/pyqtgraph/graphicsItems/PlotDataItem.py +++ b/pyqtgraph/graphicsItems/PlotDataItem.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import warnings +import math import numpy as np from .. import metaarray as metaarray from ..Qt import QtCore @@ -154,9 +155,6 @@ class PlotDataItem(GraphicsObject): self.yData = None self.xDisp = None self.yDisp = None - #self.dataMask = None - #self.curves = [] - #self.scatters = [] self.curve = PlotCurveItem() self.scatter = ScatterPlotItem() self.curve.setParentItem(self) @@ -166,8 +164,15 @@ class PlotDataItem(GraphicsObject): self.scatter.sigClicked.connect(self.scatterClicked) self.scatter.sigHovered.connect(self.scatterHovered) - self._viewRangeWasChanged = False - self._styleWasChanged = True # force initial update + # self._xViewRangeWasChanged = False + # self._yViewRangeWasChanged = False + # self._styleWasChanged = True # force initial update + + # update-required notifications are handled through properties to allow future management through + # the QDynamicPropertyChangeEvent sent on any change. + self.setProperty('xViewRangeWasChanged', False) + self.setProperty('yViewRangeWasChanged', False) + self.setProperty('styleWasChanged', True) # force initial update self._dataRect = None self._drlLastClip = (0.0, 0.0) # holds last clipping points of dynamic range limiter @@ -208,6 +213,7 @@ class PlotDataItem(GraphicsObject): 'data': None, } + self.setCurveClickable(kargs.get('clickable', False)) self.setData(*args, **kargs) def implements(self, interface=None): @@ -219,6 +225,12 @@ class PlotDataItem(GraphicsObject): def name(self): return self.opts.get('name', None) + def setCurveClickable(self, s, width=None): + self.curve.setClickable(s, width) + + def curveClickable(self): + return self.curve.clickable + def boundingRect(self): return QtCore.QRectF() ## let child items handle this @@ -242,7 +254,7 @@ class PlotDataItem(GraphicsObject): return self.opts['fftMode'] = mode self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) self.informViewBoundsChanged() def setLogMode(self, xMode, yMode): @@ -260,7 +272,7 @@ class PlotDataItem(GraphicsObject): return self.opts['logMode'] = [xMode, yMode] self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) self.informViewBoundsChanged() @@ -269,7 +281,7 @@ class PlotDataItem(GraphicsObject): return self.opts['derivativeMode'] = mode self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) self.informViewBoundsChanged() def setPhasemapMode(self, mode): @@ -277,7 +289,7 @@ class PlotDataItem(GraphicsObject): return self.opts['phasemapMode'] = mode self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) self.informViewBoundsChanged() def setPointMode(self, mode): @@ -297,7 +309,7 @@ class PlotDataItem(GraphicsObject): #for c in self.curves: #c.setPen(pen) #self.update() - self.updateItems() + self.updateItems(styleUpdate=True) def setShadowPen(self, *args, **kargs): """ @@ -312,14 +324,14 @@ class PlotDataItem(GraphicsObject): #for c in self.curves: #c.setPen(pen) #self.update() - self.updateItems() + self.updateItems(styleUpdate=True) def setFillBrush(self, *args, **kargs): brush = fn.mkBrush(*args, **kargs) if self.opts['fillBrush'] == brush: return self.opts['fillBrush'] = brush - self.updateItems() + self.updateItems(styleUpdate=True) def setBrush(self, *args, **kargs): return self.setFillBrush(*args, **kargs) @@ -328,14 +340,14 @@ class PlotDataItem(GraphicsObject): if self.opts['fillLevel'] == level: return self.opts['fillLevel'] = level - self.updateItems() + self.updateItems(styleUpdate=True) def setSymbol(self, symbol): if self.opts['symbol'] == symbol: return self.opts['symbol'] = symbol #self.scatter.setSymbol(symbol) - self.updateItems() + self.updateItems(styleUpdate=True) def setSymbolPen(self, *args, **kargs): pen = fn.mkPen(*args, **kargs) @@ -343,7 +355,7 @@ class PlotDataItem(GraphicsObject): return self.opts['symbolPen'] = pen #self.scatter.setSymbolPen(pen) - self.updateItems() + self.updateItems(styleUpdate=True) def setSymbolBrush(self, *args, **kargs): brush = fn.mkBrush(*args, **kargs) @@ -351,7 +363,7 @@ class PlotDataItem(GraphicsObject): return self.opts['symbolBrush'] = brush #self.scatter.setSymbolBrush(brush) - self.updateItems() + self.updateItems(styleUpdate=True) def setSymbolSize(self, size): @@ -359,7 +371,7 @@ class PlotDataItem(GraphicsObject): return self.opts['symbolSize'] = size #self.scatter.setSymbolSize(symbolSize) - self.updateItems() + self.updateItems(styleUpdate=True) def setDownsampling(self, ds=None, auto=None, method=None): """ @@ -396,14 +408,14 @@ class PlotDataItem(GraphicsObject): if changed: self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) def setClipToView(self, clip): if self.opts['clipToView'] == clip: return self.opts['clipToView'] = clip self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) def setDynamicRangeLimit(self, limit=1e06, hysteresis=3.): """ @@ -431,7 +443,7 @@ class PlotDataItem(GraphicsObject): return # avoid update if there is no change self.opts['dynamicRangeLimit'] = limit # can be None self.xDisp = self.yDisp = None - self.updateItems() + self.updateItems(styleUpdate=False) def setData(self, *args, **kargs): """ @@ -524,11 +536,11 @@ class PlotDataItem(GraphicsObject): if 'name' in kargs: self.opts['name'] = kargs['name'] - self._styleWasChanged = True + self.setProperty('styleWasChanged', True) if 'connect' in kargs: self.opts['connect'] = kargs['connect'] - self._styleWasChanged = True + self.setProperty('styleWasChanged', True) ## if symbol pen/brush are given with no previously set symbol, then assume symbol is 'o' if 'symbol' not in kargs and ('symbolPen' in kargs or 'symbolBrush' in kargs or 'symbolSize' in kargs): @@ -541,8 +553,7 @@ class PlotDataItem(GraphicsObject): for k in list(self.opts.keys()): if k in kargs: self.opts[k] = kargs[k] - self._styleWasChanged = True - + self.setProperty('styleWasChanged', True) #curveArgs = {} #for k in ['pen', 'shadowPen', 'fillLevel', 'brush']: #if k in kargs: @@ -575,22 +586,25 @@ class PlotDataItem(GraphicsObject): self.yDisp = None profiler('set data') - self.updateItems( update_style = self._styleWasChanged ) - self._styleWasChanged = False # items have been updated + self.updateItems( styleUpdate = self.property('styleWasChanged') ) + self.setProperty('styleWasChanged', False) # items have been updated profiler('update items') self.informViewBoundsChanged() - #view = self.getViewBox() - #if view is not None: - #view.itemBoundsChanged(self) ## inform view so it can update its range if it wants self.sigPlotChanged.emit(self) profiler('emit') - def updateItems(self, update_style=False): + def updateItems(self, styleUpdate=True): + # override styleUpdate request and always enforce update until we have a better solution for + # - ScatterPlotItem losing per-point style information + # - PlotDataItem performing multiple unnecessary setData call on initialization + styleUpdate=True + curveArgs = {} scatterArgs = {} - if update_style: # repeat style arguments only when changed + + if styleUpdate: # repeat style arguments only when changed for k,v in [('pen','pen'), ('shadowPen','shadowPen'), ('fillLevel','fillLevel'), ('fillOutline', 'fillOutline'), ('fillBrush', 'brush'), ('antialias', 'antialias'), ('connect', 'connect'), ('stepMode', 'stepMode')]: if k in self.opts: curveArgs[v] = self.opts[k] @@ -622,136 +636,161 @@ class PlotDataItem(GraphicsObject): if self.xData is None: return (None, None) - if self.xDisp is None or self._viewRangeWasChanged: - x = self.xData - y = self.yData + if( self.xDisp is not None and + not (self.property('xViewRangeWasChanged') and self.opts['clipToView']) and + not (self.property('xViewRangeWasChanged') and self.opts['autoDownsample']) and + not (self.property('yViewRangeWasChanged') and self.opts['dynamicRangeLimit'] is not None) + ): + return self.xDisp, self.yDisp + x = self.xData + y = self.yData + if y.dtype == bool: + y = y.astype(np.uint8) + if x.dtype == bool: + x = x.astype(np.uint8) + view = self.getViewBox() + if view is None: + view_range = None + else: + view_range = self.getViewBox().viewRect() # this is always up-to-date + if view_range is None: + view_range = self.viewRect() - if self.opts['fftMode']: - x,y = self._fourierTransform(x, y) - # Ignore the first bin for fft data if we have a logx scale - if self.opts['logMode'][0]: - x=x[1:] - y=y[1:] + if self.opts['fftMode']: + x,y = self._fourierTransform(x, y) + # Ignore the first bin for fft data if we have a logx scale + if self.opts['logMode'][0]: + x=x[1:] + y=y[1:] - if self.opts['derivativeMode']: # plot dV/dt - y = np.diff(self.yData)/np.diff(self.xData) - x = x[:-1] - if self.opts['phasemapMode']: # plot dV/dt vs V - x = self.yData[:-1] - y = np.diff(self.yData)/np.diff(self.xData) - - with np.errstate(divide='ignore'): - if self.opts['logMode'][0]: - x = np.log10(x) - if self.opts['logMode'][1]: - if np.issubdtype(y.dtype, np.floating): - eps = np.finfo(y.dtype).eps - else: - eps = 1 - y = np.sign(y) * np.log10(np.abs(y)+eps) + if self.opts['derivativeMode']: # plot dV/dt + y = np.diff(self.yData)/np.diff(self.xData) + x = x[:-1] + if self.opts['phasemapMode']: # plot dV/dt vs V + x = self.yData[:-1] + y = np.diff(self.yData)/np.diff(self.xData) + + with np.errstate(divide='ignore'): + if self.opts['logMode'][0]: + x = np.log10(x) + if self.opts['logMode'][1]: + if np.issubdtype(y.dtype, np.floating): + eps = np.finfo(y.dtype).eps + else: + eps = 1 + y = np.copysign(np.log10(np.abs(y)+eps), y) - ds = self.opts['downsample'] - if not isinstance(ds, int): - ds = 1 + ds = self.opts['downsample'] + if not isinstance(ds, int): + ds = 1 - if self.opts['autoDownsample']: - # this option presumes that x-values have uniform spacing - range = self.viewRect() - if range is not None and len(x) > 1: - dx = float(x[-1]-x[0]) / (len(x)-1) - if dx != 0.0: - x0 = (range.left()-x[0]) / dx - x1 = (range.right()-x[0]) / dx - width = self.getViewBox().width() - if width != 0.0: - ds = int(max(1, int((x1-x0) / (width*self.opts['autoDownsampleFactor'])))) - ## downsampling is expensive; delay until after clipping. + if self.opts['autoDownsample']: + # this option presumes that x-values have uniform spacing + if view_range is not None and len(x) > 1: + dx = float(x[-1]-x[0]) / (len(x)-1) + if dx != 0.0: + x0 = (view_range.left()-x[0]) / dx + x1 = (view_range.right()-x[0]) / dx + width = self.getViewBox().width() + if width != 0.0: + ds = int(max(1, int((x1-x0) / (width*self.opts['autoDownsampleFactor'])))) + ## downsampling is expensive; delay until after clipping. - if self.opts['clipToView']: - view = self.getViewBox() - if view is None or not view.autoRangeEnabled()[0]: - # this option presumes that x-values are in increasing order - range = self.viewRect() - if range is not None and len(x) > 1: - # clip to visible region extended by downsampling value, assuming - # uniform spacing of x-values, has O(1) performance - dx = float(x[-1]-x[0]) / (len(x)-1) - x0 = np.clip(int((range.left()-x[0])/dx) - 1*ds, 0, len(x)-1) - x1 = np.clip(int((range.right()-x[0])/dx) + 2*ds, 0, len(x)-1) + if self.opts['clipToView']: + if view is None or view.autoRangeEnabled()[0]: + pass # no ViewBox to clip to, or view will autoscale to data range. + else: + # clip-to-view always presumes that x-values are in increasing order + if view_range is not None and len(x) > 1: + # print('search:', view_range.left(),'-',view_range.right() ) + # find first in-view value (left edge) and first out-of-view value (right edge) + # since we want the curve to go to the edge of the screen, we need to preserve + # one down-sampled point on the left and one of the right, so we extend the interval + x0 = np.searchsorted(x, view_range.left()) - ds + x0 = fn.clip_scalar(x0, 0, len(x)) # workaround + # x0 = np.clip(x0, 0, len(x)) - # if data has been clipped too strongly (in case of non-uniform - # spacing of x-values), refine the clipping region as required - # worst case performance: O(log(n)) - # best case performance: O(1) - if x[x0] > range.left(): - x0 = np.searchsorted(x, range.left()) - 1*ds - x0 = np.clip(x0, a_min=0, a_max=len(x)) - if x[x1] < range.right(): - x1 = np.searchsorted(x, range.right()) + 2*ds - x1 = np.clip(x1, a_min=0, a_max=len(x)) - x = x[x0:x1] - y = y[x0:x1] + x1 = np.searchsorted(x, view_range.right()) + ds + x1 = fn.clip_scalar(x1, x0, len(x)) + # x1 = np.clip(x1, 0, len(x)) + x = x[x0:x1] + y = y[x0:x1] - if ds > 1: - if self.opts['downsampleMethod'] == 'subsample': - x = x[::ds] - y = y[::ds] - elif self.opts['downsampleMethod'] == 'mean': - n = len(x) // ds - x = x[:n*ds:ds] - y = y[:n*ds].reshape(n,ds).mean(axis=1) - elif self.opts['downsampleMethod'] == 'peak': - n = len(x) // ds - x1 = np.empty((n,2)) - x1[:] = x[:n*ds:ds,np.newaxis] - x = x1.reshape(n*2) - y1 = np.empty((n,2)) - y2 = y[:n*ds].reshape((n, ds)) - y1[:,0] = y2.max(axis=1) - y1[:,1] = y2.min(axis=1) - y = y1.reshape(n*2) + if ds > 1: + if self.opts['downsampleMethod'] == 'subsample': + x = x[::ds] + y = y[::ds] + elif self.opts['downsampleMethod'] == 'mean': + n = len(x) // ds + # x = x[:n*ds:ds] + stx = ds//2 # start of x-values; try to select a somewhat centered point + x = x[stx:stx+n*ds:ds] + y = y[:n*ds].reshape(n,ds).mean(axis=1) + elif self.opts['downsampleMethod'] == 'peak': + n = len(x) // ds + x1 = np.empty((n,2)) + stx = ds//2 # start of x-values; try to select a somewhat centered point + x1[:] = x[stx:stx+n*ds:ds,np.newaxis] + x = x1.reshape(n*2) + y1 = np.empty((n,2)) + y2 = y[:n*ds].reshape((n, ds)) + y1[:,0] = y2.max(axis=1) + y1[:,1] = y2.min(axis=1) + y = y1.reshape(n*2) - if self.opts['dynamicRangeLimit'] is not None: - view_range = self.viewRect() - if view_range is not None: - data_range = self.dataRect() - if data_range is not None: - view_height = view_range.height() - limit = self.opts['dynamicRangeLimit'] - hyst = self.opts['dynamicRangeHyst'] - # never clip data if it fits into +/- (extended) limit * view height - if data_range.height() > 2 * hyst * limit * view_height: - # check if cached display data can be reused: - if self.yDisp is not None: # top is minimum value, bottom is maximum value - # how many multiples of the current view height does the clipped plot extend to the top and bottom? - top_exc =-(self._drlLastClip[0]-view_range.bottom()) / view_height - bot_exc = (self._drlLastClip[1]-view_range.top() ) / view_height - # print(top_exc, bot_exc, hyst) - if ( top_exc >= limit / hyst and top_exc <= limit * hyst - and bot_exc >= limit / hyst and bot_exc <= limit * hyst ): - # restore cached values - x = self.xDisp - y = self.yDisp - else: - min_val = view_range.bottom() - limit * view_height - max_val = view_range.top() + limit * view_height - if( min_val >= self._drlLastClip[0] - and max_val <= self._drlLastClip[1] ): - # if we need to clip further, we can work in-place on the output buffer - # print('in-place:', end='') - np.clip(self.yDisp, out=self.yDisp, a_min=min_val, a_max=max_val) - x = self.xDisp - y = self.yDisp - else: - # otherwise we need to recopy from the full data - # print('alloc:', end='') - y = np.clip(y, a_min=min_val, a_max=max_val) - # print('{:.1e}<->{:.1e}'.format( min_val, max_val )) - self._drlLastClip = (min_val, max_val) - - self.xDisp = x - self.yDisp = y - self._viewRangeWasChanged = False + if self.opts['dynamicRangeLimit'] is not None: + if view_range is not None: + data_range = self.dataRect() + if data_range is not None: + view_height = view_range.height() + limit = self.opts['dynamicRangeLimit'] + hyst = self.opts['dynamicRangeHyst'] + # never clip data if it fits into +/- (extended) limit * view height + if ( # note that "bottom" is the larger number, and "top" is the smaller one. + not data_range.bottom() < view_range.top() # never clip if all data is too small to see + and not data_range.top() > view_range.bottom() # never clip if all data is too large to see + and data_range.height() > 2 * hyst * limit * view_height + ): + cache_is_good = False + # check if cached display data can be reused: + if self.yDisp is not None: # top is minimum value, bottom is maximum value + # how many multiples of the current view height does the clipped plot extend to the top and bottom? + top_exc =-(self._drlLastClip[0]-view_range.bottom()) / view_height + bot_exc = (self._drlLastClip[1]-view_range.top() ) / view_height + # print(top_exc, bot_exc, hyst) + if ( top_exc >= limit / hyst and top_exc <= limit * hyst + and bot_exc >= limit / hyst and bot_exc <= limit * hyst ): + # restore cached values + x = self.xDisp + y = self.yDisp + cache_is_good = True + if not cache_is_good: + min_val = view_range.bottom() - limit * view_height + max_val = view_range.top() + limit * view_height + if( self.yDisp is not None # Do we have an existing cache? + and min_val >= self._drlLastClip[0] # Are we reducing it further? + and max_val <= self._drlLastClip[1] ): + # if we need to clip further, we can work in-place on the output buffer + # print('in-place:', end='') + # workaround for slowdown from numpy deprecation issues in 1.17 to 1.20+ : + # np.clip(self.yDisp, out=self.yDisp, a_min=min_val, a_max=max_val) + fn.clip_array(self.yDisp, min_val, max_val, out=self.yDisp) + self._drlLastClip = (min_val, max_val) + # print('{:.1e}<->{:.1e}'.format( min_val, max_val )) + x = self.xDisp + y = self.yDisp + else: + # if none of the shortcuts worked, we need to recopy from the full data + # print('alloc:', end='') + # workaround for slowdown from numpy deprecation issues in 1.17 to 1.20+ : + # y = np.clip(y, a_min=min_val, a_max=max_val) + y = fn.clip_array(y, min_val, max_val) + self._drlLastClip = (min_val, max_val) + # print('{:.1e}<->{:.1e}'.format( min_val, max_val )) + self.xDisp = x + self.yDisp = y + self.setProperty('xViewRangeWasChanged', False) + self.setProperty('yViewRangeWasChanged', False) return self.xDisp, self.yDisp def dataRect(self): @@ -769,10 +808,10 @@ class PlotDataItem(GraphicsObject): # All-NaN data is handled by returning None; Explicit numpy warning is not needed. warnings.simplefilter("ignore") ymin = np.nanmin(self.yData) - if np.isnan( ymin ): + if math.isnan( ymin ): return None # most likely case for all-NaN data xmin = np.nanmin(self.xData) - if np.isnan( xmin ): + if math.isnan( xmin ): return None # less likely case for all-NaN data ymax = np.nanmax(self.yData) xmax = np.nanmax(self.xData) @@ -826,11 +865,6 @@ class PlotDataItem(GraphicsObject): def clear(self): - #for i in self.curves+self.scatters: - #if i.scene() is not None: - #i.scene().removeItem(i) - #self.curves = [] - #self.scatters = [] self.xData = None self.yData = None self.xDisp = None @@ -852,20 +886,37 @@ class PlotDataItem(GraphicsObject): def scatterHovered(self, plt, points, ev): self.sigPointsHovered.emit(self, points, ev) - def viewRangeChanged(self): - # view range has changed; re-plot if needed - self._viewRangeWasChanged = True - if( self.opts['clipToView'] - or self.opts['autoDownsample'] - ): - self.xDisp = self.yDisp = None - self.updateItems() - elif self.opts['dynamicRangeLimit'] is not None: - # do not discard cached display data - self.updateItems() + # def viewTransformChanged(self): + # """ view transform (and thus range) has changed, replot if needed """ + # viewTransformChanged is only called when the cached viewRect of GraphicsItem + # has already been invalidated. However, responding here will make PlotDataItem + # update curve and scatter later than intended. + # super().viewTransformChanged() # this invalidates the viewRect() cache! + + def viewRangeChanged(self, vb=None, ranges=None, changed=None): + """ view range has changed; re-plot if needed """ + update_needed = False + if changed is None or changed[0]: + # if ranges is not None: + # print('hor:', ranges[0]) + self.setProperty('xViewRangeWasChanged', True) + if( self.opts['clipToView'] + or self.opts['autoDownsample'] + ): + self.xDisp = self.yDisp = None + update_needed = True + if changed is None or changed[1]: + # if ranges is not None: + # print('ver:', ranges[1]) + self.setProperty('yViewRangeWasChanged', True) + if self.opts['dynamicRangeLimit'] is not None: + # update, but do not discard cached display data + update_needed = True + if update_needed: + self.updateItems(styleUpdate=False) def _fourierTransform(self, x, y): - ## Perform fourier transform. If x values are not sampled uniformly, + ## Perform Fourier transform. If x values are not sampled uniformly, ## then use np.interp to resample before taking fft. dx = np.diff(x) uniform = not np.any(np.abs(dx-dx[0]) > (abs(dx[0]) / 1000.)) diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py index a2affcf7..8da9ba39 100644 --- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py +++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py @@ -372,7 +372,7 @@ class PlotItem(GraphicsWidget): if y is not None: self.ctrl.yGridCheck.setChecked(y) if alpha is not None: - v = np.clip(alpha, 0, 1)*self.ctrl.gridAlphaSlider.maximum() + v = fn.clip_scalar(alpha, 0., 1.)*self.ctrl.gridAlphaSlider.maximum() self.ctrl.gridAlphaSlider.setValue(v) def close(self): diff --git a/pyqtgraph/graphicsItems/ROI.py b/pyqtgraph/graphicsItems/ROI.py index 99b2882d..b6e74095 100644 --- a/pyqtgraph/graphicsItems/ROI.py +++ b/pyqtgraph/graphicsItems/ROI.py @@ -15,9 +15,9 @@ of how to build an ROI at the bottom of the file. from ..Qt import QtCore, QtGui import numpy as np #from numpy.linalg import norm -from ..Point import * +from ..Point import Point from ..SRTTransform import SRTTransform -from math import cos, sin +from math import atan2, cos, degrees, sin, hypot from .. import functions as fn from .GraphicsObject import GraphicsObject from .UIGraphicsItem import UIGraphicsItem @@ -141,12 +141,13 @@ class ROI(GraphicsObject): maxBounds=None, snapSize=1.0, scaleSnap=False, translateSnap=False, rotateSnap=False, parent=None, pen=None, hoverPen=None, handlePen=None, handleHoverPen=None, - movable=True, rotatable=True, resizable=True, removable=False): + movable=True, rotatable=True, resizable=True, removable=False, + aspectLocked=False): GraphicsObject.__init__(self, parent) self.setAcceptedMouseButtons(QtCore.Qt.NoButton) pos = Point(pos) size = Point(size) - self.aspectLocked = False + self.aspectLocked = aspectLocked self.translatable = movable self.rotatable = rotatable self.resizable = resizable @@ -678,7 +679,7 @@ class ROI(GraphicsObject): The format returned is a list of (name, pos) tuples. """ - if index == None: + if index is None: positions = [] for h in self.handles: positions.append((h['name'], h['pos'])) @@ -691,7 +692,7 @@ class ROI(GraphicsObject): The format returned is a list of (name, pos) tuples. """ - if index == None: + if index is None: positions = [] for h in self.handles: positions.append((h['name'], h['item'].scenePos())) @@ -800,7 +801,7 @@ class ROI(GraphicsObject): if ev.button() == QtCore.Qt.RightButton and self.contextMenuEnabled(): self.raiseContextMenu(ev) ev.accept() - elif ev.button() & self.acceptedMouseButtons(): + elif ev.button() in self.acceptedMouseButtons(): ev.accept() self.sigClicked.emit(self, ev) else: @@ -826,10 +827,17 @@ class ROI(GraphicsObject): """ return True - def movePoint(self, handle, pos, modifiers=QtCore.Qt.KeyboardModifiers(0), finish=True, coords='parent'): + def movePoint(self, handle, pos, modifiers=None, finish=True, coords='parent'): ## called by Handles when they are moved. ## pos is the new position of the handle in scene coords, as requested by the handle. - + if modifiers is None: + try: + # this works for PyQt6 6.1 and other bindings + modifiers = QtCore.Qt.KeyboardModifier.NoModifier + except AttributeError: + # this works for PyQt6 6.0 and other bindings + modifiers = QtCore.Qt.KeyboardModifiers(0) + newState = self.stateCopy() index = self.indexOfHandle(handle) h = self.handles[index] @@ -928,7 +936,7 @@ class ROI(GraphicsObject): return ## determine new rotation angle, constrained if necessary - ang = newState['angle'] - lp0.angle(lp1) + ang = newState['angle'] - lp1.angle(lp0) if ang is None: ## this should never happen.. return if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier): @@ -964,7 +972,7 @@ class ROI(GraphicsObject): except OverflowError: return - ang = newState['angle'] - lp0.angle(lp1) + ang = newState['angle'] - lp1.angle(lp0) if ang is None: return if self.rotateSnap or (modifiers & QtCore.Qt.ControlModifier): @@ -1185,22 +1193,25 @@ class ROI(GraphicsObject): mapped = fn.transformCoordinates(img.transform(), coords) return result, mapped - def _getArrayRegionForArbitraryShape(self, data, img, axes=(0,1), **kwds): + def _getArrayRegionForArbitraryShape(self, data, img, axes=(0,1), returnMappedCoords=False, **kwds): """ Return the result of :meth:`~pyqtgraph.ROI.getArrayRegion`, masked by the shape of the ROI. Values outside the ROI shape are set to 0. See :meth:`~pyqtgraph.ROI.getArrayRegion` for a description of the arguments. - - Note: ``returnMappedCoords`` is not yet supported for this ROI type. """ br = self.boundingRect() if br.width() > 1000: raise Exception() - sliced = ROI.getArrayRegion(self, data, img, axes=axes, fromBoundingRect=True, **kwds) + if returnMappedCoords: + sliced, mappedCoords = ROI.getArrayRegion( + self, data, img, axes, returnMappedCoords, fromBoundingRect=True, **kwds) + else: + sliced = ROI.getArrayRegion( + self, data, img, axes, returnMappedCoords, fromBoundingRect=True, **kwds) - if img.axisOrder == "col-major": + if img.axisOrder == 'col-major': mask = self.renderShapeMask(sliced.shape[axes[0]], sliced.shape[axes[1]]) else: mask = self.renderShapeMask(sliced.shape[axes[1]], sliced.shape[axes[0]]) @@ -1212,7 +1223,10 @@ class ROI(GraphicsObject): shape[axes[1]] = sliced.shape[axes[1]] mask = mask.reshape(shape) - return sliced * mask + if returnMappedCoords: + return sliced * mask, mappedCoords + else: + return sliced * mask def getAffineSliceParams(self, data, img, axes=(0,1), fromBoundingRect=False): """ @@ -1234,8 +1248,8 @@ class ROI(GraphicsObject): vx = img.mapToData(self.mapToItem(img, QtCore.QPointF(1, 0))) - origin vy = img.mapToData(self.mapToItem(img, QtCore.QPointF(0, 1))) - origin - lvx = np.sqrt(vx.x()**2 + vx.y()**2) - lvy = np.sqrt(vy.x()**2 + vy.y()**2) + lvx = hypot(vx.x(), vx.y()) # length + lvy = hypot(vy.x(), vy.y()) # length ##img.width is number of pixels, not width of item. ##need pxWidth and pxHeight instead of pxLen ? sx = 1.0 / lvx @@ -1285,7 +1299,7 @@ class ROI(GraphicsObject): """Return global transformation (rotation angle+translation) required to move from relative state to current state. If relative state isn't specified, then we use the state of the ROI when mouse is pressed.""" - if relativeTo == None: + if relativeTo is None: relativeTo = self.preMoveState st = self.getState() @@ -1412,7 +1426,7 @@ class Handle(UIGraphicsItem): menu = self.scene().addParentContextMenus(self, self.getMenu(), ev) ## Make sure it is still ok to remove this handle - removeAllowed = all([r.checkRemoveHandle(self) for r in self.rois]) + removeAllowed = all(r.checkRemoveHandle(self) for r in self.rois) self.removeAction.setEnabled(removeAllowed) pos = ev.screenPos() menu.popup(QtCore.QPoint(pos.x(), pos.y())) @@ -1448,7 +1462,14 @@ class Handle(UIGraphicsItem): self.currentPen = self.hoverPen self.movePoint(pos, ev.modifiers(), finish=False) - def movePoint(self, pos, modifiers=QtCore.Qt.KeyboardModifiers(0), finish=True): + def movePoint(self, pos, modifiers=None, finish=True): + if modifiers is None: + try: + # this works for PyQt6 6.1 and other bindings + modifiers = QtCore.Qt.KeyboardModifier.NoModifier + except AttributeError: + # this works for PyQt6 6.0 and other bindings + modifiers = QtCore.Qt.KeyboardModifiers(0) for r in self.rois: if not r.checkPointMove(self, pos, modifiers): return @@ -1461,7 +1482,7 @@ class Handle(UIGraphicsItem): size = self.radius self.path = QtGui.QPainterPath() ang = self.startAng - dt = 2*np.pi / self.sides + dt = 2 * np.pi / self.sides for i in range(0, self.sides+1): x = size * cos(ang) y = size * sin(ang) @@ -1498,13 +1519,13 @@ class Handle(UIGraphicsItem): return None v = dt.map(QtCore.QPointF(1, 0)) - dt.map(QtCore.QPointF(0, 0)) - va = np.arctan2(v.y(), v.x()) + va = atan2(v.y(), v.x()) dti = fn.invertQTransform(dt) devPos = dt.map(QtCore.QPointF(0,0)) tr = QtGui.QTransform() tr.translate(devPos.x(), devPos.y()) - tr.rotate(va * 180. / 3.1415926) + tr.rotateRadians(va) return dti.map(tr.map(self.path)) @@ -1642,18 +1663,14 @@ class LineROI(ROI): pos2 = Point(pos2) d = pos2-pos1 l = d.length() - ang = Point(1, 0).angle(d) - ra = ang * np.pi / 180. + ra = d.angle(Point(1, 0), units="radians") c = Point(-width/2. * sin(ra), -width/2. * cos(ra)) pos1 = pos1 + c - ROI.__init__(self, pos1, size=Point(l, width), angle=ang, **args) + ROI.__init__(self, pos1, size=Point(l, width), angle=degrees(ra), **args) self.addScaleRotateHandle([0, 0.5], [1, 0.5]) self.addScaleRotateHandle([1, 0.5], [0, 0.5]) self.addScaleHandle([0.5, 1], [0.5, 0.5]) - - - class MultiRectROI(QtGui.QGraphicsObject): @@ -1840,7 +1857,7 @@ class EllipseROI(ROI): p.drawEllipse(r) - def getArrayRegion(self, arr, img=None, axes=(0, 1), **kwds): + def getArrayRegion(self, arr, img=None, axes=(0, 1), returnMappedCoords=False, **kwds): """ Return the result of :meth:`~pyqtgraph.ROI.getArrayRegion` masked by the elliptical shape of the ROI. Regions outside the ellipse are set to 0. @@ -1852,14 +1869,22 @@ class EllipseROI(ROI): """ # Note: we could use the same method as used by PolyLineROI, but this # implementation produces a nicer mask. - arr = ROI.getArrayRegion(self, arr, img, axes, **kwds) + if returnMappedCoords: + arr, mappedCoords = ROI.getArrayRegion(self, arr, img, axes, + returnMappedCoords, **kwds) + else: + arr = ROI.getArrayRegion(self, arr, img, axes, + returnMappedCoords, **kwds) if arr is None or arr.shape[axes[0]] == 0 or arr.shape[axes[1]] == 0: - return arr + if returnMappedCoords: + return arr, mappedCoords + else: + return arr w = arr.shape[axes[0]] h = arr.shape[axes[1]] ## generate an ellipsoidal mask - mask = np.fromfunction(lambda x,y: (((x+0.5)/(w/2.)-1)**2+ ((y+0.5)/(h/2.)-1)**2)**0.5 < 1, (w, h)) + mask = np.fromfunction(lambda x,y: np.hypot(((x+0.5)/(w/2.)-1), ((y+0.5)/(h/2.)-1)) < 1, (w, h)) # reshape to match array axes if axes[0] > axes[1]: @@ -1867,7 +1892,10 @@ class EllipseROI(ROI): shape = [(n if i in axes else 1) for i,n in enumerate(arr.shape)] mask = mask.reshape(shape) - return arr * mask + if returnMappedCoords: + return arr * mask, mappedCoords + else: + return arr * mask def shape(self): if self.path is None: @@ -1883,7 +1911,7 @@ class EllipseROI(ROI): center = br.center() r1 = br.width() / 2. r2 = br.height() / 2. - theta = np.linspace(0, 2*np.pi, 24) + theta = np.linspace(0, 2 * np.pi, 24) x = center.x() + r1 * np.cos(theta) y = center.y() + r2 * np.sin(theta) path.moveTo(x[0], y[0]) @@ -1913,8 +1941,7 @@ class CircleROI(EllipseROI): if radius is None: raise TypeError("Must provide either size or radius.") size = (radius*2, radius*2) - EllipseROI.__init__(self, pos, size, **args) - self.aspectLocked = True + EllipseROI.__init__(self, pos, size, aspectLocked=True, **args) def _addHandles(self): self.addScaleHandle([0.5*2.**-0.5 + 0.5, 0.5*2.**-0.5 + 0.5], [0.5, 0.5]) @@ -2235,7 +2262,7 @@ class LineSegmentROI(ROI): Since this pulls 1D data from a 2D coordinate system, the return value will have ndim = data.ndim-1 - See :meth:`~pytqgraph.ROI.getArrayRegion` for a description of the + See :meth:`~pyqtgraph.ROI.getArrayRegion` for a description of the arguments. """ imgPts = [self.mapToItem(img, h.pos()) for h in self.endpoints] @@ -2279,16 +2306,15 @@ class CrosshairROI(ROI): """A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable.""" def __init__(self, pos=None, size=None, **kargs): - if size == None: + if size is None: size=[1,1] - if pos == None: + if pos is None: pos = [0,0] self._shape = None - ROI.__init__(self, pos, size, **kargs) + ROI.__init__(self, pos, size, aspectLocked=True, **kargs) self.sigRegionChanged.connect(self.invalidate) self.addScaleRotateHandle(Point(1, 0), Point(0, 0)) - self.aspectLocked = True def invalidate(self): self._shape = None @@ -2332,7 +2358,7 @@ class RulerROI(LineSegmentROI): vec = Point(h2) - Point(h1) length = vec.length() - angle = vec.angle(Point(1, 0)) + angle = Point(1, 0).angle(vec) pvec = p2 - p1 pvecT = Point(pvec.y(), -pvec.x()) @@ -2353,7 +2379,7 @@ class RulerROI(LineSegmentROI): class TriangleROI(ROI): - """ + r""" Equilateral triangle ROI subclass with one scale handle and one rotation handle. Arguments pos (length-2 sequence) The position of the ROI's origin. @@ -2363,8 +2389,7 @@ class TriangleROI(ROI): """ def __init__(self, pos, size, **args): - ROI.__init__(self, pos, [size, size], **args) - self.aspectLocked = True + ROI.__init__(self, pos, [size, size], aspectLocked=True, **args) angles = np.linspace(0, np.pi * 4 / 3, 3) verticies = (np.array((np.sin(angles), np.cos(angles))).T + 1.0) / 2.0 self.poly = QtGui.QPolygonF() diff --git a/pyqtgraph/graphicsItems/ScatterPlotItem.py b/pyqtgraph/graphicsItems/ScatterPlotItem.py index 40627811..d711c066 100644 --- a/pyqtgraph/graphicsItems/ScatterPlotItem.py +++ b/pyqtgraph/graphicsItems/ScatterPlotItem.py @@ -6,6 +6,7 @@ try: except ImportError: imap = map import itertools +import math import numpy as np import weakref from ..Qt import QtGui, QtCore, QT_LIB @@ -40,10 +41,22 @@ _USE_QRECT = QT_LIB not in ['PySide2', 'PySide6'] ## Build all symbol paths name_list = ['o', 's', 't', 't1', 't2', 't3', 'd', '+', 'x', 'p', 'h', 'star', - 'arrow_up', 'arrow_right', 'arrow_down', 'arrow_left'] + 'arrow_up', 'arrow_right', 'arrow_down', 'arrow_left', 'crosshair'] Symbols = OrderedDict([(name, QtGui.QPainterPath()) for name in name_list]) Symbols['o'].addEllipse(QtCore.QRectF(-0.5, -0.5, 1, 1)) Symbols['s'].addRect(QtCore.QRectF(-0.5, -0.5, 1, 1)) + +def makeCrosshair(r=0.5, w=1, h=1): + path = QtGui.QPainterPath() + rect = QtCore.QRectF(-r, -r, r * 2, r * 2) + path.addEllipse(rect) + path.moveTo(-w, 0) + path.lineTo(w, 0) + path.moveTo(0, -h) + path.lineTo(0, h) + return path +Symbols['crosshair'] = makeCrosshair() + coords = { 't': [(-0.5, -0.5), (0, 0.5), (0.5, -0.5)], 't1': [(-0.5, 0.5), (0, -0.5), (0.5, 0.5)], @@ -63,7 +76,7 @@ coords = { (-0.1816, 0.059), (-0.2939, 0.4045), (0, 0.1910), (0.2939, 0.4045), (0.1816, 0.059), (0.4755, -0.1545), (0.1123, -0.1545)], - 'arrow_down': [ + 'arrow_up': [ (-0.125, 0.125), (0, 0), (0.125, 0.125), (0.05, 0.125), (0.05, 0.5), (-0.05, 0.5), (-0.05, 0.125) ] @@ -77,9 +90,9 @@ tr = QtGui.QTransform() tr.rotate(45) Symbols['x'] = tr.map(Symbols['+']) tr.rotate(45) -Symbols['arrow_right'] = tr.map(Symbols['arrow_down']) -Symbols['arrow_up'] = tr.map(Symbols['arrow_right']) -Symbols['arrow_left'] = tr.map(Symbols['arrow_up']) +Symbols['arrow_right'] = tr.map(Symbols['arrow_up']) +Symbols['arrow_down'] = tr.map(Symbols['arrow_right']) +Symbols['arrow_left'] = tr.map(Symbols['arrow_down']) _DEFAULT_STYLE = {'symbol': None, 'size': -1, 'pen': None, 'brush': None, 'visible': True} @@ -105,7 +118,7 @@ def renderSymbol(symbol, size, pen, brush, device=None): for more information). """ ## Render a spot with the given parameters to a pixmap - penPxWidth = max(np.ceil(pen.widthF()), 1) + penPxWidth = max(math.ceil(pen.widthF()), 1) if device is None: device = QtGui.QImage(int(size+penPxWidth), int(size+penPxWidth), QtGui.QImage.Format_ARGB32) device.fill(0) @@ -445,15 +458,11 @@ class ScatterPlotItem(GraphicsObject): Otherwise, size is in scene coordinates and the spots scale with the view. To ensure effective caching, QPen and QBrush objects should be reused as much as possible. Default is True - *symbol* can be one (or a list) of: - * 'o' circle (default) - * 's' square - * 't' triangle - * 'd' diamond - * '+' plus - * any QPainterPath to specify custom symbol shapes. To properly obey the position and size, - custom symbols should be centered at (0,0) and width and height of 1.0. Note that it is also - possible to 'install' custom shapes by setting ScatterPlotItem.Symbols[key] = shape. + *symbol* can be one (or a list) of symbols. For a list of supported symbols, see + :func:`~ScatterPlotItem.setSymbol`. QPainterPath is also supported to specify custom symbol + shapes. To properly obey the position and size, custom symbols should be centered at (0,0) and + width and height of 1.0. Note that it is also possible to 'install' custom shapes by setting + ScatterPlotItem.Symbols[key] = shape. *pen* The pen (or list of pens) to use for drawing spot outlines. *brush* The brush (or list of brushes) to use for filling spots. *size* The size (or list of sizes) of spots. If *pxMode* is True, this value is in pixels. Otherwise, @@ -681,7 +690,30 @@ class ScatterPlotItem(GraphicsObject): """Set the symbol(s) used to draw each spot. If a list or array is provided, then the symbol for each spot will be set separately. Otherwise, the argument will be used as the default symbol for - all spots which do not have a symbol explicitly set.""" + all spots which do not have a symbol explicitly set. + + **Supported symbols:** + + * 'o' circle (default) + * 's' square + * 't' triangle + * 'd' diamond + * '+' plus + * 't1' triangle pointing upwards + * 't2' triangle pointing right side + * 't3' triangle pointing left side + * 'p' pentagon + * 'h' hexagon + * 'star' + * 'x' cross + * 'arrow_up' + * 'arrow_right' + * 'arrow_down' + * 'arrow_left' + * 'crosshair' + * any QPainterPath to specify custom symbol shapes. + + """ if dataSet is None: dataSet = self.data @@ -941,6 +973,8 @@ class ScatterPlotItem(GraphicsObject): elif ax == 1: d = self.data['y'] d2 = self.data['x'] + else: + raise ValueError("Invalid axis value") if orthoRange is not None: mask = (d2 >= orthoRange[0]) * (d2 <= orthoRange[1]) @@ -1064,7 +1098,7 @@ class ScatterPlotItem(GraphicsObject): # Map points using painter's world transform so they are drawn with pixel-valued sizes pts = np.vstack([self.data['x'], self.data['y']]) pts = fn.transformCoordinates(p.transform(), pts) - pts = np.clip(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault. + pts = fn.clip_array(pts, -2 ** 30, 2 ** 30) # prevent Qt segmentation fault. p.resetTransform() if self.opts['useCache'] and self._exportOpts is False: diff --git a/pyqtgraph/graphicsItems/TargetItem.py b/pyqtgraph/graphicsItems/TargetItem.py index 782e8a3b..1a4d5144 100644 --- a/pyqtgraph/graphicsItems/TargetItem.py +++ b/pyqtgraph/graphicsItems/TargetItem.py @@ -1,132 +1,477 @@ +from math import atan2 from ..Qt import QtGui, QtCore -import numpy as np from ..Point import Point from .. import functions as fn from .GraphicsObject import GraphicsObject +from .UIGraphicsItem import UIGraphicsItem from .TextItem import TextItem +from .ScatterPlotItem import Symbols, makeCrosshair +from .ViewBox import ViewBox +import string +import warnings -class TargetItem(GraphicsObject): +class TargetItem(UIGraphicsItem): """Draws a draggable target symbol (circle plus crosshair). The size of TargetItem will remain fixed on screen even as the view is zoomed. Includes an optional text label. """ - sigDragged = QtCore.Signal(object) - def __init__(self, movable=True, radii=(5, 10, 10), pen=(255, 255, 0), brush=(0, 0, 255, 100)): - GraphicsObject.__init__(self) - self._bounds = None - self._radii = radii - self._picture = None + sigPositionChanged = QtCore.Signal(object) + sigPositionChangeFinished = QtCore.Signal(object) + + def __init__( + self, + pos=None, + size=10, + radii=None, + symbol="crosshair", + pen=None, + hoverPen=None, + brush=None, + hoverBrush=None, + movable=True, + label=None, + labelOpts=None, + ): + r""" + Parameters + ---------- + pos : list, tuple, QPointF, QPoint, Optional + Initial position of the symbol. Default is (0, 0) + size : int + Size of the symbol in pixels. Default is 10. + radii : tuple of int + Deprecated. Gives size of crosshair in screen pixels. + pen : QPen, tuple, list or str + Pen to use when drawing line. Can be any arguments that are valid + for :func:`~pyqtgraph.mkPen`. Default pen is transparent yellow. + brush : QBrush, tuple, list, or str + Defines the brush that fill the symbol. Can be any arguments that + is valid for :func:`~pyqtgraph.mkBrush`. Default is transparent + blue. + movable : bool + If True, the symbol can be dragged to a new position by the user. + hoverPen : QPen, tuple, list, or str + Pen to use when drawing symbol when hovering over it. Can be any + arguments that are valid for :func:`~pyqtgraph.mkPen`. Default pen + is red. + hoverBrush : QBrush, tuple, list or str + Brush to use to fill the symbol when hovering over it. Can be any + arguments that is valid for :func:`~pyqtgraph.mkBrush`. Default is + transparent blue. + symbol : QPainterPath or str + QPainterPath to use for drawing the target, should be centered at + ``(0, 0)`` with ``max(width, height) == 1.0``. Alternatively a string + which can be any symbol accepted by + :func:`~pyqtgraph.ScatterPlotItem.setData` + label : bool, str or callable, optional + Text to be displayed in a label attached to the symbol, or None to + show no label (default is None). May optionally include formatting + strings to display the symbol value, or a callable that accepts x + and y as inputs. If True, the label is ``x = {: >.3n}\ny = {: >.3n}`` + False or None will result in no text being displayed + labelOpts : dict + A dict of keyword arguments to use when constructing the text + label. See :class:`TargetLabel` and :class:`~pyqtgraph.TextItem` + """ + super().__init__(self) self.movable = movable self.moving = False - self.label = None - self.labelAngle = 0 - self.pen = fn.mkPen(pen) - self.brush = fn.mkBrush(brush) + self._label = None + self.mouseHovering = False - def setLabel(self, label): - if label is None: - if self.label is not None: - self.label.scene().removeItem(self.label) - self.label = None + if radii is not None: + warnings.warn( + "'radii' is now deprecated, and will be removed in 0.13.0. Use 'size' " + "parameter instead", + DeprecationWarning, + stacklevel=2, + ) + symbol = makeCrosshair(*radii) + size = 1 + + if pen is None: + pen = (255, 255, 0) + self.setPen(pen) + + if hoverPen is None: + hoverPen = (255, 0, 255) + self.setHoverPen(hoverPen) + + if brush is None: + brush = (0, 0, 255, 50) + self.setBrush(brush) + + if hoverBrush is None: + hoverBrush = (0, 255, 255, 100) + self.setHoverBrush(hoverBrush) + + self.currentPen = self.pen + self.currentBrush = self.brush + + self._shape = None + + self._pos = Point(0, 0) + if pos is None: + pos = Point(0, 0) + self.setPos(pos) + + if isinstance(symbol, str): + try: + self._path = Symbols[symbol] + except KeyError: + raise KeyError("symbol name found in available Symbols") + elif isinstance(symbol, QtGui.QPainterPath): + self._path = symbol else: - if self.label is None: - self.label = TextItem() - self.label.setParentItem(self) - self.label.setText(label) - self._updateLabel() + raise TypeError("Unknown type provided as symbol") - def setLabelAngle(self, angle): - if self.labelAngle != angle: - self.labelAngle = angle - self._updateLabel() + self.scale = size + self.setPath(self._path) + self.setLabel(label, labelOpts) + + @property + def sigDragged(self): + warnings.warn( + "'sigDragged' has been deprecated and will be removed in 0.13.0. Use " + "`sigPositionChanged` instead", + DeprecationWarning, + stacklevel=2, + ) + return self.sigPositionChangeFinished + + def setPos(self, pos): + """Method to set the position to ``(x, y)`` within the plot view + + Parameters + ---------- + pos : tuple, list, QPointF, QPoint, or pg.Point + Container that consists of ``(x, y)`` representation of where the + TargetItem should be placed + + Raises + ------ + TypeError + If the type of ``pos`` does not match the known types to extract + coordinate info from, a TypeError is raised + """ + if isinstance(pos, Point): + newPos = pos + elif isinstance(pos, (tuple, list)): + newPos = Point(pos) + elif isinstance(pos, (QtCore.QPointF, QtCore.QPoint)): + newPos = Point(pos.x(), pos.y()) + else: + raise TypeError + if self._pos != newPos: + self._pos = newPos + super().setPos(self._pos) + self.sigPositionChanged.emit(self) + + def setBrush(self, *args, **kwargs): + """Set the brush that fills the symbol. Allowable arguments are any that + are valid for :func:`~pyqtgraph.mkBrush`. + """ + self.brush = fn.mkBrush(*args, **kwargs) + if not self.mouseHovering: + self.currentBrush = self.brush + self.update() + + def setHoverBrush(self, *args, **kwargs): + """Set the brush that fills the symbol when hovering over it. Allowable + arguments are any that are valid for :func:`~pyqtgraph.mkBrush`. + """ + self.hoverBrush = fn.mkBrush(*args, **kwargs) + if self.mouseHovering: + self.currentBrush = self.hoverBrush + self.update() + + def setPen(self, *args, **kwargs): + """Set the pen for drawing the symbol. Allowable arguments are any that + are valid for :func:`~pyqtgraph.mkPen`.""" + self.pen = fn.mkPen(*args, **kwargs) + if not self.mouseHovering: + self.currentPen = self.pen + self.update() + + def setHoverPen(self, *args, **kwargs): + """Set the pen for drawing the symbol when hovering over it. Allowable + arguments are any that are valid for + :func:`~pyqtgraph.mkPen`.""" + self.hoverPen = fn.mkPen(*args, **kwargs) + if self.mouseHovering: + self.currentPen = self.hoverPen + self.update() def boundingRect(self): - if self._picture is None: - self._drawPicture() - return self._bounds - - def dataBounds(self, axis, frac=1.0, orthoRange=None): - return [0, 0] + return self.shape().boundingRect() - def viewTransformChanged(self): - self._picture = None - self.prepareGeometryChange() - self._updateLabel() + def paint(self, p, *_): + p.setPen(self.currentPen) + p.setBrush(self.currentBrush) + p.drawPath(self.shape()) - def _updateLabel(self): - if self.label is None: - return + def setPath(self, path): + if path != self._path: + self._path = path + self._shape = None + return None - # find an optimal location for text at the given angle - angle = self.labelAngle * np.pi / 180. - lbr = self.label.boundingRect() - center = lbr.center() - a = abs(np.sin(angle) * lbr.height()*0.5) - b = abs(np.cos(angle) * lbr.width()*0.5) - r = max(self._radii) + 2 + max(a, b) - pos = self.mapFromScene(self.mapToScene(QtCore.QPointF(0, 0)) + r * QtCore.QPointF(np.cos(angle), -np.sin(angle)) - center) - self.label.setPos(pos) + def shape(self): + if self._shape is None: + s = self.generateShape() + if s is None: + return self._path + self._shape = s - def paint(self, p, *args): - if self._picture is None: - self._drawPicture() - self._picture.play(p) + # beware--this can cause the view to adjust + # which would immediately invalidate the shape. + self.prepareGeometryChange() + return self._shape - def _drawPicture(self): - self._picture = QtGui.QPicture() - p = QtGui.QPainter(self._picture) - p.setRenderHint(p.Antialiasing) - - # Note: could do this with self.pixelLength, but this is faster. - o = self.mapToScene(QtCore.QPointF(0, 0)) - dx = (self.mapToScene(QtCore.QPointF(1, 0)) - o).x() - dy = (self.mapToScene(QtCore.QPointF(0, 1)) - o).y() - if dx == 0 or dy == 0: - p.end() - self._bounds = QtCore.QRectF() - return - px = abs(1.0 / dx) - py = abs(1.0 / dy) - - r, w, h = self._radii - w = w * px - h = h * py - rx = r * px - ry = r * py - rect = QtCore.QRectF(-rx, -ry, rx*2, ry*2) - p.setPen(self.pen) - p.setBrush(self.brush) - p.drawEllipse(rect) - p.drawLine(Point(-w, 0), Point(w, 0)) - p.drawLine(Point(0, -h), Point(0, h)) - p.end() - - bx = max(w, rx) - by = max(h, ry) - self._bounds = QtCore.QRectF(-bx, -by, bx*2, by*2) + def generateShape(self): + dt = self.deviceTransform() + if dt is None: + self._shape = self._path + return None + v = dt.map(QtCore.QPointF(1, 0)) - dt.map(QtCore.QPointF(0, 0)) + dti = fn.invertQTransform(dt) + devPos = dt.map(QtCore.QPointF(0, 0)) + tr = QtGui.QTransform() + tr.translate(devPos.x(), devPos.y()) + va = atan2(v.y(), v.x()) + tr.rotateRadians(va) + tr.scale(self.scale, self.scale) + return dti.map(tr.map(self._path)) def mouseDragEvent(self, ev): - if not self.movable: + if not self.movable or int(ev.button() & QtCore.Qt.LeftButton) == 0: return - if ev.button() == QtCore.Qt.LeftButton: - if ev.isStart(): - self.moving = True - self.cursorOffset = self.pos() - self.mapToParent(ev.buttonDownPos()) - self.startPosition = self.pos() + ev.accept() + if ev.isStart(): + self.symbolOffset = self.pos() - self.mapToView(ev.buttonDownPos()) + self.moving = True + + if not self.moving: + return + self.setPos(self.symbolOffset + self.mapToView(ev.pos())) + + if ev.isFinish(): + self.moving = False + self.sigPositionChangeFinished.emit(self) + + def mouseClickEvent(self, ev): + if self.moving and ev.button() == QtCore.Qt.RightButton: ev.accept() - - if not self.moving: - return - - self.setPos(self.cursorOffset + self.mapToParent(ev.pos())) - if ev.isFinish(): - self.moving = False - self.sigDragged.emit(self) + self.moving = False + self.sigPositionChanged.emit(self) + self.sigPositionChangeFinished.emit(self) + + def setMouseHover(self, hover): + # Inform the item that the mouse is(not) hovering over it + if self.mouseHovering is hover: + return + self.mouseHovering = hover + if hover: + self.currentBrush = self.hoverBrush + self.currentPen = self.hoverPen + else: + self.currentBrush = self.brush + self.currentPen = self.pen + self.update() def hoverEvent(self, ev): - if self.movable: - ev.acceptDrags(QtCore.Qt.LeftButton) + if self.movable and (not ev.isExit()) and ev.acceptDrags(QtCore.Qt.LeftButton): + self.setMouseHover(True) + else: + self.setMouseHover(False) + def viewTransformChanged(self): + GraphicsObject.viewTransformChanged(self) + self._shape = None # invalidate shape, recompute later if requested. + self.update() + + def pos(self): + """Provides the current position of the TargetItem + + Returns + ------- + Point + pg.Point of the current position of the TargetItem + """ + return self._pos + + def label(self): + """Provides the TargetLabel if it exists + + Returns + ------- + TargetLabel or None + If a TargetLabel exists for this TargetItem, return that, otherwise + return None + """ + return self._label + + def setLabel(self, text=None, labelOpts=None): + """Method to call to enable or disable the TargetLabel for displaying text + + Parameters + ---------- + text : Callable or str, optional + Details how to format the text, by default None + If None, do not show any text next to the TargetItem + If Callable, then the label will display the result of ``text(x, y)`` + If a fromatted string, then the output of ``text.format(x, y)`` will be + displayed + If a non-formatted string, then the text label will display ``text``, by + default None + labelOpts : dictionary, optional + These arguments are passed on to :class:`~pyqtgraph.TextItem` + """ + if not text: + if self._label is not None and self._label.scene() is not None: + # remove the label if it's already added + self._label.scene().removeItem(self._label) + self._label = None + else: + # provide default text if text is True + if text is True: + # convert to default value or empty string + text = "x = {: .3n}\ny = {: .3n}" + + labelOpts = {} if labelOpts is None else labelOpts + if self._label is not None: + self._label.scene().removeItem(self._label) + self._label = TargetLabel(self, text=text, **labelOpts) + + def setLabelAngle(self, angle): + warnings.warn( + "TargetItem.setLabelAngle is deprecated and will be removed in 0.13.0." + "Use TargetItem.label().setAngle() instead", + DeprecationWarning, + stacklevel=2, + ) + if self.label() is not None and angle != self.label().angle: + self.label().setAngle(angle) + return None + + +class TargetLabel(TextItem): + """A TextItem that attaches itself to a TargetItem. + + This class extends TextItem with the following features : + * Automatically positions adjacent to the symbol at a fixed position. + * Automatically reformats text when the symbol location has changed. + + Parameters + ---------- + target : TargetItem + The TargetItem to which this label will be attached to. + text : str or callable, Optional + Governs the text displayed, can be a fixed string or a format string + that accepts the x, and y position of the target item; or be a callable + method that accepts a tuple (x, y) and returns a string to be displayed. + If None, an empty string is used. Default is None + offset : tuple or list or QPointF or QPoint + Position to set the anchor of the TargetLabel away from the center of + the target in pixels, by default it is (20, 0). + anchor : tuple, list, QPointF or QPoint + Position to rotate the TargetLabel about, and position to set the + offset value to see :class:`~pyqtgraph.TextItem` for more inforation. + kwargs : dict of arguments that are passed on to + :class:`~pyqtgraph.TextItem` constructor, excluding text parameter + """ + + def __init__( + self, + target, + text="", + offset=(20, 0), + anchor=(0, 0.5), + **kwargs, + ): + if isinstance(offset, Point): + self.offset = offset + elif isinstance(offset, (tuple, list)): + self.offset = Point(*offset) + elif isinstance(offset, (QtCore.QPoint, QtCore.QPointF)): + self.offset = Point(offset.x(), offset.y()) + else: + raise TypeError("Offset parameter is the wrong data type") + + super().__init__(anchor=anchor, **kwargs) + self.setParentItem(target) + self.target = target + self.setFormat(text) + + self.target.sigPositionChanged.connect(self.valueChanged) + self.valueChanged() + + def format(self): + return self._format + + def setFormat(self, text): + """Method to set how the TargetLabel should display the text. This + method should be called from TargetItem.setLabel directly. + + Parameters + ---------- + text : Callable or str + Details how to format the text. + If Callable, then the label will display the result of ``text(x, y)`` + If a fromatted string, then the output of ``text.format(x, y)`` will be + displayed + If a non-formatted string, then the text label will display ``text`` + """ + if not callable(text): + parsed = list(string.Formatter().parse(text)) + if parsed and parsed[0][1] is not None: + self.setProperty("formattableText", True) + else: + self.setText(text) + self.setProperty("formattableText", False) + else: + self.setProperty("formattableText", False) + self._format = text + self.valueChanged() + + def valueChanged(self): + x, y = self.target.pos() + if self.property("formattableText"): + self.setText(self._format.format(float(x), float(y))) + elif callable(self._format): + self.setText(self._format(x, y)) + + def viewTransformChanged(self): + viewbox = self.getViewBox() + if isinstance(viewbox, ViewBox): + viewPixelSize = viewbox.viewPixelSize() + scaledOffset = QtCore.QPointF( + self.offset.x() * viewPixelSize[0], self.offset.y() * viewPixelSize[1] + ) + self.setPos(scaledOffset) + return super().viewTransformChanged() + + def mouseClickEvent(self, ev): + return self.parentItem().mouseClickEvent(ev) + + def mouseDragEvent(self, ev): + targetItem = self.parentItem() + if not targetItem.movable or int(ev.button() & QtCore.Qt.LeftButton) == 0: + return + ev.accept() + if ev.isStart(): + targetItem.symbolOffset = targetItem.pos() - self.mapToView( + ev.buttonDownPos() + ) + targetItem.moving = True + + if not targetItem.moving: + return + targetItem.setPos(targetItem.symbolOffset + self.mapToView(ev.pos())) + + if ev.isFinish(): + targetItem.moving = False + targetItem.sigPositionChangeFinished.emit(self) diff --git a/pyqtgraph/graphicsItems/TextItem.py b/pyqtgraph/graphicsItems/TextItem.py index 8dcb04f6..2af87d5d 100644 --- a/pyqtgraph/graphicsItems/TextItem.py +++ b/pyqtgraph/graphicsItems/TextItem.py @@ -1,4 +1,4 @@ -import numpy as np +from math import atan2, degrees from ..Qt import QtCore, QtGui from ..Point import Point from .. import functions as fn @@ -208,7 +208,7 @@ class TextItem(GraphicsObject): angle = -self.angle if self.rotateAxis is not None: d = pt.map(self.rotateAxis) - pt.map(Point(0, 0)) - a = np.arctan2(d.y(), d.x()) * 180 / np.pi + a = degrees(atan2(d.y(), d.x())) angle += a t.rotate(angle) self.setTransform(t) diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index 83d095b7..f1987ced 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import weakref import sys +import math from copy import deepcopy import numpy as np from ...Qt import QtGui, QtCore @@ -87,7 +88,7 @@ class ViewBox(GraphicsWidget): sigYRangeChanged = QtCore.Signal(object, object) sigXRangeChanged = QtCore.Signal(object, object) sigRangeChangedManually = QtCore.Signal(object) - sigRangeChanged = QtCore.Signal(object, object) + sigRangeChanged = QtCore.Signal(object, object, object) sigStateChanged = QtCore.Signal(object) sigTransformChanged = QtCore.Signal(object) sigResized = QtCore.Signal(object) @@ -549,10 +550,9 @@ class ViewBox(GraphicsWidget): dy = 1 mn -= dy*0.5 mx += dy*0.5 - xpad = 0.0 # Make sure no nan/inf get through - if not all(np.isfinite([mn, mx])): + if not math.isfinite(mn) or not math.isfinite(mx): raise Exception("Cannot set range [%s, %s]" % (str(mn), str(mx))) # Apply padding @@ -666,7 +666,7 @@ class ViewBox(GraphicsWidget): def suggestPadding(self, axis): l = self.width() if axis==0 else self.height() if l > 0: - padding = np.clip(1./(l**0.5), 0.02, 0.1) + padding = fn.clip_scalar(1./(l**0.5), 0.02, 0.1) else: padding = 0.02 return padding @@ -903,11 +903,11 @@ class ViewBox(GraphicsWidget): targetRect[ax] = childRange[ax] args['xRange' if ax == 0 else 'yRange'] = targetRect[ax] - # check for and ignore bad ranges + # check for and ignore bad ranges for k in ['xRange', 'yRange']: if k in args: - if not np.all(np.isfinite(args[k])): - r = args.pop(k) + if not math.isfinite(args[k][0]) or not math.isfinite(args[k][1]): + _ = args.pop(k) #print("Warning: %s is invalid: %s" % (k, str(r)) if len(args) == 0: @@ -1243,7 +1243,7 @@ class ViewBox(GraphicsWidget): mask[1-axis] = 0.0 ## Scale or translate based on mouse button - if ev.button() & (QtCore.Qt.LeftButton | QtCore.Qt.MiddleButton): + if ev.button() in [QtCore.Qt.LeftButton, QtCore.Qt.MiddleButton]: if self.state['mouseMode'] == ViewBox.RectMode and axis is None: if ev.isFinish(): ## This is the final move in the drag; change the view scale now #print "finish" @@ -1369,10 +1369,20 @@ class ViewBox(GraphicsWidget): xr = item.dataBounds(0, frac=frac[0], orthoRange=orthoRange[0]) yr = item.dataBounds(1, frac=frac[1], orthoRange=orthoRange[1]) pxPad = 0 if not hasattr(item, 'pixelPadding') else item.pixelPadding() - if xr is None or (xr[0] is None and xr[1] is None) or np.isnan(xr).any() or np.isinf(xr).any(): + if ( + xr is None or + (xr[0] is None and xr[1] is None) or + not math.isfinite(xr[0]) or + not math.isfinite(xr[1]) + ): useX = False xr = (0,0) - if yr is None or (yr[0] is None and yr[1] is None) or np.isnan(yr).any() or np.isinf(yr).any(): + if ( + yr is None or + (yr[0] is None and yr[1] is None) or + not math.isfinite(yr[0]) or + not math.isfinite(yr[1]) + ): useY = False yr = (0,0) @@ -1543,11 +1553,12 @@ class ViewBox(GraphicsWidget): link.linkedViewChanged(self, ax) # emit range change signals + # print('announcing view range changes:',self.state['viewRange'] ) if changed[0]: self.sigXRangeChanged.emit(self, tuple(self.state['viewRange'][0])) if changed[1]: self.sigYRangeChanged.emit(self, tuple(self.state['viewRange'][1])) - self.sigRangeChanged.emit(self, self.state['viewRange']) + self.sigRangeChanged.emit(self, self.state['viewRange'], changed) def updateMatrix(self, changed=None): if not self._matrixNeedsUpdate: diff --git a/pyqtgraph/graphicsItems/ViewBox/tests/test_ViewBoxZoom.py b/pyqtgraph/graphicsItems/ViewBox/tests/test_ViewBoxZoom.py index 4bad9ee1..5a8aa65b 100644 --- a/pyqtgraph/graphicsItems/ViewBox/tests/test_ViewBoxZoom.py +++ b/pyqtgraph/graphicsItems/ViewBox/tests/test_ViewBoxZoom.py @@ -193,8 +193,3 @@ def test_zoom_ratio_with_limits_out_of_range(): assert viewRange[1][0] >= -5 assert viewRange[1][1] <= 5 assert viewWidth == 2 * viewHeight - - -if __name__ == "__main__": - setup_module(None) - test_zoom_ratio() diff --git a/pyqtgraph/graphicsItems/tests/test_ErrorBarItem.py b/pyqtgraph/graphicsItems/tests/test_ErrorBarItem.py index f28c681a..ae5a9606 100644 --- a/pyqtgraph/graphicsItems/tests/test_ErrorBarItem.py +++ b/pyqtgraph/graphicsItems/tests/test_ErrorBarItem.py @@ -13,18 +13,21 @@ def test_ErrorBarItem_defer_data(): curve = pg.PlotCurveItem(x=x, y=x) plot.addItem(curve) app.processEvents() + app.processEvents() r_no_ebi = plot.viewRect() # ErrorBarItem with no data shouldn't affect the view rect err = pg.ErrorBarItem() plot.addItem(err) app.processEvents() + app.processEvents() r_empty_ebi = plot.viewRect() assert r_no_ebi.height() == r_empty_ebi.height() err.setData(x=x, y=x, bottom=x, top=x) app.processEvents() + app.processEvents() r_ebi = plot.viewRect() assert r_ebi.height() > r_empty_ebi.height() @@ -32,6 +35,7 @@ def test_ErrorBarItem_defer_data(): # unset data, ErrorBarItem disappears and view rect goes back to original err.setData(x=None, y=None) app.processEvents() + app.processEvents() r_clear_ebi = plot.viewRect() assert r_clear_ebi.height() == r_empty_ebi.height() diff --git a/pyqtgraph/graphicsItems/tests/test_ImageItem.py b/pyqtgraph/graphicsItems/tests/test_ImageItem.py index 76fa0510..b69ec9c1 100644 --- a/pyqtgraph/graphicsItems/tests/test_ImageItem.py +++ b/pyqtgraph/graphicsItems/tests/test_ImageItem.py @@ -1,15 +1,39 @@ +# -*- coding: utf-8 -*- import time import pytest + from pyqtgraph.Qt import QtCore, QtGui, QtTest import numpy as np import pyqtgraph as pg from pyqtgraph.tests import assertImageApproved, TransposedImageItem - +try: + import cupy +except ImportError: + cupy = None app = pg.mkQApp() +@pytest.mark.skipif(cupy is None, reason="CuPy unavailable to test") +def test_useCupy_can_be_set_after_init(): + prev_setting = pg.getConfigOption("useCupy") + try: + pg.setConfigOption("useCupy", False) + w = pg.GraphicsLayoutWidget() + w.show() + view = pg.ViewBox() + w.setCentralWidget(view) + w.resize(200, 200) + img = cupy.random.randint(0, 255, size=(32, 32)).astype(cupy.uint8) + ii = pg.ImageItem() + view.addItem(ii) + pg.setConfigOption("useCupy", True) + ii.setImage(img) + w.hide() + finally: + pg.setConfigOption("useCupy", prev_setting) + + def test_ImageItem(transpose=False): - w = pg.GraphicsLayoutWidget() w.show() view = pg.ViewBox() @@ -123,6 +147,7 @@ def test_ImageItem(transpose=False): w.hide() + def test_ImageItem_axisorder(): # All image tests pass again using the opposite axis order origMode = pg.getConfigOption('imageAxisOrder') diff --git a/pyqtgraph/graphicsItems/tests/test_NonUniformImage.py b/pyqtgraph/graphicsItems/tests/test_NonUniformImage.py index 2b6bfedf..52b7c989 100644 --- a/pyqtgraph/graphicsItems/tests/test_NonUniformImage.py +++ b/pyqtgraph/graphicsItems/tests/test_NonUniformImage.py @@ -93,7 +93,7 @@ def test_NonUniformImage_colormap(): 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)], mode='rgb') + cmap = ColorMap(pos=[0.0, 1.0], color=[(0.0, 0.0, 0.0, 1.0), (1.0, 1.0, 1.0, 1.0)]) image.setColorMap(cmap) viewbox.addItem(image) diff --git a/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py b/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py index c46d0544..f5e88708 100644 --- a/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py +++ b/pyqtgraph/graphicsItems/tests/test_PlotDataItem.py @@ -1,9 +1,21 @@ +# -*- coding: utf-8 -*- import numpy as np import pyqtgraph as pg +from pyqtgraph.Qt import QtGui pg.mkQApp() +def test_bool(): + truths = np.random.randint(0, 2, size=(100,)).astype(bool) + pdi = pg.PlotDataItem(truths) + bounds = pdi.dataBounds(1) + assert isinstance(bounds[0], np.uint8) + assert isinstance(bounds[1], np.uint8) + xdata, ydata = pdi.getData() + assert ydata.dtype == np.uint8 + + def test_fft(): f = 20. x = np.linspace(0, 1, 1000) @@ -60,6 +72,35 @@ def test_setData(): assert pdi.xData is None assert pdi.yData is None +def test_opts(): + # test that curve and scatter plot properties get updated from PlotDataItem methods + y = list(np.random.normal(size=100)) + x = np.linspace(5, 10, 100) + pdi = pg.PlotDataItem(x, y) + pen = QtGui.QPen( QtGui.QColor('#FF0000') ) + pen2 = QtGui.QPen( QtGui.QColor('#FFFF00') ) + brush = QtGui.QBrush( QtGui.QColor('#00FF00')) + brush2 = QtGui.QBrush( QtGui.QColor('#00FFFF')) + float_value = 1.0 + 20*np.random.random() + pen2.setWidth( int(float_value) ) + pdi.setPen(pen) + assert pdi.curve.opts['pen'] == pen + pdi.setShadowPen(pen2) + assert pdi.curve.opts['shadowPen'] == pen2 + pdi.setFillLevel( float_value ) + assert pdi.curve.opts['fillLevel'] == float_value + pdi.setFillBrush(brush2) + assert pdi.curve.opts['brush'] == brush2 + + pdi.setSymbol('t') + assert pdi.scatter.opts['symbol'] == 't' + pdi.setSymbolPen(pen) + assert pdi.scatter.opts['pen'] == pen + pdi.setSymbolBrush(brush) + assert pdi.scatter.opts['brush'] == brush + pdi.setSymbolSize( float_value ) + assert pdi.scatter.opts['size'] == float_value + def test_clear(): y = list(np.random.normal(size=100)) x = np.linspace(5, 10, 100) @@ -84,14 +125,36 @@ def test_clipping(): w.addItem(c) c.setClipToView(True) - w.setXRange(200, 600) - for x_min in range(100, 2**10 - 100, 100): - w.setXRange(x_min, x_min + 100) - + for x_min in range(-200, 2**10 - 100, 100): + x_max = x_min + 100 + w.setXRange(x_min, x_max, padding=0) xDisp, _ = c.getData() - vr = c.viewRect() + # vr = c.viewRect() + if len(xDisp) > 3: # check that all points except the first and last are on screen + assert( xDisp[ 1] >= x_min and xDisp[ 1] <= x_max ) + assert( xDisp[-2] >= x_min and xDisp[-2] <= x_max ) - assert xDisp[0] <= vr.left() - assert xDisp[-1] >= vr.right() + c.setDownsampling(ds=1) # disable downsampling + for x_min in range(-200, 2**10 - 100, 100): + x_max = x_min + 100 + w.setXRange(x_min, x_max, padding=0) + xDisp, _ = c.getData() + # vr = c.viewRect() # this tends to be out of data, so we check against the range that we set + if len(xDisp) > 3: # check that all points except the first and last are on screen + assert( xDisp[ 0] == x[ 0] or xDisp[ 0] < x_min ) # first point should be unchanged, or off-screen + assert( xDisp[ 1] >= x_min and xDisp[ 1] <= x_max ) + assert( xDisp[-2] >= x_min and xDisp[-2] <= x_max ) + assert( xDisp[-1] == x[-1] or xDisp[-1] > x_max ) # last point should be unchanged, or off-screen + + c.setData(x=np.zeros_like(y), y=y) # test zero width data set: + # test center and expected number of remaining data points + for center, num in ((-100.,1), (100.,1), (0.,len(y)) ): + # when all elements are off-screen, only one will be kept + # when all elelemts are on-screen, all should be kept + # and the code should not crash for zero separation + w.setXRange( center-50, center+50, padding=0 ) + xDisp, yDisp = c.getData() + assert len(xDisp) == num + assert len(yDisp) == num w.close() diff --git a/pyqtgraph/imageview/ImageView.py b/pyqtgraph/imageview/ImageView.py index 2cbafce7..f934b36d 100644 --- a/pyqtgraph/imageview/ImageView.py +++ b/pyqtgraph/imageview/ImageView.py @@ -12,14 +12,16 @@ Widget used for displaying 2D or 3D data. Features: - ROI plotting - Image normalization through a variety of methods """ -import os, sys +import os +from math import log10 import numpy as np from ..Qt import QtCore, QtGui, QT_LIB +from .. import functions as fn import importlib ui_template = importlib.import_module( f'.ImageViewTemplate_{QT_LIB.lower()}', package=__package__) - + from ..graphicsItems.ImageItem import * from ..graphicsItems.ROI import * from ..graphicsItems.LinearRegionItem import * @@ -272,7 +274,7 @@ class ImageView(QtGui.QWidget): if not isinstance(img, np.ndarray): required = ['dtype', 'max', 'min', 'ndim', 'shape', 'size'] - if not all([hasattr(img, attr) for attr in required]): + if not all(hasattr(img, attr) for attr in required): raise TypeError("Image must be NumPy array or any object " "that provides compatible attributes/methods:\n" " %s" % str(required)) @@ -512,7 +514,7 @@ class ImageView(QtGui.QWidget): def setCurrentIndex(self, ind): """Set the currently displayed frame index.""" - index = np.clip(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1) + index = fn.clip_scalar(ind, 0, self.getProcessedImage().shape[self.axes['t']]-1) self.ignorePlaying = True # Implicitly call timeLineChanged self.timeLine.setValue(self.tVals[index]) @@ -563,7 +565,7 @@ class ImageView(QtGui.QWidget): self.roi.show() #self.ui.roiPlot.show() self.ui.roiPlot.setMouseEnabled(True, True) - self.ui.splitter.setSizes([self.height()*0.6, self.height()*0.4]) + self.ui.splitter.setSizes([int(self.height()*0.6), int(self.height()*0.4)]) self.ui.splitter.handle(1).setEnabled(True) self.roiCurve.show() self.roiChanged() @@ -807,7 +809,7 @@ class ImageView(QtGui.QWidget): img = self.getProcessedImage() if self.hasTimeAxis(): base, ext = os.path.splitext(fileName) - fmt = "%%s%%0%dd%%s" % int(np.log10(img.shape[0])+1) + fmt = "%%s%%0%dd%%s" % int(log10(img.shape[0])+1) for i in range(img.shape[0]): self.imageItem.setImage(img[i], autoLevels=False) self.imageItem.save(fmt % (base, i, ext)) diff --git a/pyqtgraph/metaarray/MetaArray.py b/pyqtgraph/metaarray/MetaArray.py index 169ff43c..b07f66ab 100644 --- a/pyqtgraph/metaarray/MetaArray.py +++ b/pyqtgraph/metaarray/MetaArray.py @@ -124,7 +124,7 @@ class MetaArray(object): nameTypes = [basestring, tuple] @staticmethod def isNameType(var): - return any([isinstance(var, t) for t in MetaArray.nameTypes]) + return any(isinstance(var, t) for t in MetaArray.nameTypes) ## methods to wrap from embedded ndarray / HDF5 diff --git a/pyqtgraph/multiprocess/processes.py b/pyqtgraph/multiprocess/processes.py index 24fdebf2..33ac85c5 100644 --- a/pyqtgraph/multiprocess/processes.py +++ b/pyqtgraph/multiprocess/processes.py @@ -489,12 +489,12 @@ class FileForwarder(threading.Thread): while not self.finish.is_set(): line = self.input.readline() with self.lock: - cprint.cout(self.color, line, -1) + cprint.cout(self.color, line.decode('utf8'), -1) elif self.output == 'stderr' and self.color is not False: while not self.finish.is_set(): line = self.input.readline() with self.lock: - cprint.cerr(self.color, line, -1) + cprint.cerr(self.color, line.decode('utf8'), -1) else: if isinstance(self.output, str): self.output = getattr(sys, self.output) diff --git a/pyqtgraph/namedBrush.py b/pyqtgraph/namedBrush.py deleted file mode 100644 index ae944f14..00000000 --- a/pyqtgraph/namedBrush.py +++ /dev/null @@ -1,82 +0,0 @@ -from .Qt import QtGui, QtCore -from .namedColorManager import NamedColorManager - -__all__ = ['NamedBrush'] -DEBUG = False - -class NamedBrush(QtGui.QBrush): - """ Extends QPen to retain a functional color description """ - def __init__(self, name, manager=None, alpha=None ): - """ - Creates a new NamedBrush object. - 'name' should be in 'functions.Colors' - 'manager' is a reference to the controlling NamedColorManager - 'alpha' controls opacity which persists over palette changes - """ - if DEBUG: print(' NamedBrush created as',name,alpha) - super().__init__(QtCore.Qt.SolidPattern) # Initialize QBrush superclass - self._identifier = (name, alpha) - if manager is None or not isinstance(manager, NamedColorManager): - raise ValueError("NamedPen requires NamedColorManager to be provided in 'manager' argument!") - self._manager = manager - self._updateQColor(self._identifier) - self._manager.register( self ) # manually register for callbacks - - def __eq__(self, other): # make this a hashable object - # return other is self - if isinstance(other, self.__class__): - return self._identifier == other._identifier - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __hash__(self): - return id(self) - - def setColor(self, name=None, alpha=None): - """ update color name. This does not trigger a global redraw. """ - if name is None: - name = self._identifier[0] - elif isinstance(name, QtGui.QColor): - # Replicates alpha adjustment workaround in NamedPen, allowing only alpha to be adjusted retroactively - if alpha is None: - alpha = name.alpha() # extract from given QColor - name = self._identifier[0] - if DEBUG: print(' NamedBrush: setColor(QColor) call: set alpha to', alpha) - self._identifier = (name, alpha) - self._updateQColor(self._identifier) - - def setAlpha(self, alpha): - """ update opacity value """ - self._identifier = (self._identifier[0], alpha) - self._updateQColor(self._identifier) - - def identifier(self): - """ return current color identifier """ - return self._identifier - - def _updateQColor(self, identifier, color_dict=None): - """ update super-class QColor """ - name, alpha = identifier - if color_dict is None: - color_dict = self._manager.colors() - try: - qcol = color_dict[name] - except ValueError as exc: - raise ValueError("Color '{:s}' is not in list of defined colors".format(str(name)) ) from exc - if alpha is not None: - qcol.setAlpha( alpha ) - if DEBUG: print(' NamedBrush '+name+' updated to QColor ('+str(qcol.name())+', alpha='+str(alpha)+')') - super().setColor( qcol ) - - def paletteChange(self, color_dict): - """ refresh QColor according to lookup of identifier in functions.Colors """ - if DEBUG: print(' NamedBrush: style change request:', self, type(color_dict)) - self._updateQColor(self._identifier, color_dict=color_dict) - if DEBUG: - qcol = super().color() - name, alpha = self._identifier - print(' NamedBrush: retrieved new QColor ('+str(qcol.name())+') ' - + 'for name '+str(name)+' ('+str(alpha)+')' ) diff --git a/pyqtgraph/namedColorManager.py b/pyqtgraph/namedColorManager.py deleted file mode 100644 index 7aabbd37..00000000 --- a/pyqtgraph/namedColorManager.py +++ /dev/null @@ -1,85 +0,0 @@ -# from .Qt import QtGui, QtCore, QT_LIB, QtVersion -from .Qt import QtCore, QtGui - -import weakref - -__all__ = ['NamedColorManager'] -DEBUG = False - -DEFAULT_COLORS = { - 'b': QtGui.QColor( 0, 0,255,255), - 'g': QtGui.QColor( 0,255, 0,255), - 'r': QtGui.QColor(255, 0, 0,255), - 'c': QtGui.QColor( 0,255,255,255), - 'm': QtGui.QColor(255, 0,255,255), - 'y': QtGui.QColor(255,255, 0,255), - 'k': QtGui.QColor( 0, 0, 0,255), - 'w': QtGui.QColor(255,255,255,255), - 'd': QtGui.QColor(150,150,150,255), - 'l': QtGui.QColor(200,200,200,255), - 's': QtGui.QColor(100,100,150,255), - 'gr_acc':QtGui.QColor(200,200,100,255), # graphical accent color: pastel yellow - 'gr_reg':QtGui.QColor( 0, 0,255, 50) # graphical region marker: translucent blue -} -for key, col in [ # add functional colors - ('gr_fg','d'), # graphical foreground - ('gr_bg','k'), # graphical background - ('gr_txt','d'), # graphical text color - ('gr_hlt','r') # graphical hover color -]: - DEFAULT_COLORS[key] = DEFAULT_COLORS[col] - -for idx, col in enumerate( ( # twelve predefined plot colors - 'l','y','r','m','b','c','g','d' -) ): - key = 'p{:X}'.format(idx) - DEFAULT_COLORS[key] = DEFAULT_COLORS[col] - -# An instantiated QObject is required to emit QSignals. -# functions.py initializes and maintains NAMED_COLOR_MANAGER for this purpose. -class NamedColorManager(QtCore.QObject): - """ - Provides palette change signals and maintains color name dictionary - Typically instantiated by functions.py as NAMED_COLOR_MANAGER - Instantiated by 'functions.py' and retrievable as functions.NAMED_COLOR_MANAGER - """ - paletteHasChangedSignal = QtCore.Signal() # equated to pyqtSignal in qt.py for PyQt - - def __init__(self, color_dic): - """ initialization """ - super().__init__() - self.color_dic = color_dic # this is the imported functions.Colors! - self.color_dic.clear() - self.color_dic.update( DEFAULT_COLORS) - # QPen and others are not QObjects and cannot react to signals: - self.registered_objects = weakref.WeakSet() # set of objects that request paletteChange callbacks - - def colors(self): - """ return current list of colors """ - return self.color_dic # it would be safer (but slower) to provide only a copy - - def register(self, obj): - """ register a function for paletteChange callback """ - self.registered_objects.add( obj ) - # if DEBUG: print(' NamedColorManager: New list', self.registered_objects ) - - def redefinePalette(self, colors=None): - """ - Update list of named colors if 'colors' dictionary is given - Emits paletteHasChanged signals to color objects and widgets, even if color_dict is None """ - if colors is not None: - # self.color_dic.clear() - # self.color_dic.update( DEFAULT_COLORS) - for key in DEFAULT_COLORS: - if key not in colors: - raise ValueError("Palette definition is missing '"+str(key)+"'") - if DEBUG: print(' NCM: confirmed all color definitions are present, setting palette') - self.color_dic.clear() - self.color_dic.update(colors) - - # notifies named color objects of new assignments: - for obj in self.registered_objects: - obj.paletteChange( self.color_dic ) - - # notify all graphics widgets that redraw is required: - self.paletteHasChangedSignal.emit() diff --git a/pyqtgraph/namedPen.py b/pyqtgraph/namedPen.py deleted file mode 100644 index 6a8d1ac1..00000000 --- a/pyqtgraph/namedPen.py +++ /dev/null @@ -1,99 +0,0 @@ -from .Qt import QtGui, QtCore -from .namedColorManager import NamedColorManager - -__all__ = ['NamedPen'] -DEBUG = False - -class NamedPen(QtGui.QPen): - """ Extends QPen to retain a functional color description """ - def __init__(self, name, manager=None, width=1, alpha=None ): - """ - Creates a new NamedPen object. - 'name' should be included in 'functions.Colors' - 'manager' is a reference to the controlling NamedColorManager - 'width' specifies linewidth and defaults to 1 - 'alpha' controls opacity which persists over palette changes - """ - if DEBUG: print(' NamedPen created as',name,alpha) - super().__init__(QtCore.Qt.SolidLine) # Initialize QPen superclass - super().setWidth(width) - super().setCosmetic(True) - self._identifier = (name, alpha) - if manager is None or not isinstance(manager, NamedColorManager): - raise ValueError("NamedPen requires NamedColorManager to be provided in 'manager' argument!") - self._manager = manager - self._updateQColor(self._identifier) - if self._manager is not None: - self._manager.register( self ) # manually register for callbacks - - def __eq__(self, other): # make this a hashable object - # return other is self - if isinstance(other, self.__class__): - return self._identifier == other._identifier - else: - return False - - def __ne__(self, other): - return not self.__eq__(other) - - def __hash__(self): - return id(self) - - def setColor(self, name=None, alpha=None): - """ update color name. This does not trigger a global redraw. """ - if name is None: - name = self._identifier[0] - elif isinstance(name, QtGui.QColor): - # this is a workaround for the alpha adjustements in AxisItem: - # While the color will not change, the alpha value can be adjusted as needed. - if alpha is None: - alpha = name.alpha() # extract from given QColor - name = self._identifier[0] - if DEBUG: print(' NamedPen: setColor(QColor) call: set alpha to', alpha) - self._identifier = (name, alpha) - self._updateQColor(self._identifier) - - def setAlpha(self, alpha): - """ update opacity value """ - self._identifier = (self._identifier[0], alpha) - self._updateQColor(self._identifier) - - def identifier(self): - """ return current color identifier """ - return self._identifier - - def _updateQColor(self, identifier, color_dict=None): - """ update super-class QColor """ - name, alpha = identifier - if color_dict is None: - color_dict = self._manager.colors() - try: - qcol = color_dict[name] - except ValueError as exc: - raise ValueError("Color '{:s}' is not in list of defined colors".format(str(name)) ) from exc - if alpha is not None: - qcol.setAlpha( alpha ) - if DEBUG: print(' NamedPen updated to QColor ('+str(qcol.name())+')') - super().setColor( qcol ) - - def paletteChange(self, color_dict): - """ refresh QColor according to lookup of identifier in functions.Colors """ - if DEBUG: print(' NamedPen: style change request:', self, type(color_dict)) - self._updateQColor(self._identifier, color_dict=color_dict) - if DEBUG: - qcol = super().color() - name, alpha = self._identifier - print(' NamedPen: retrieved new QColor ('+str(qcol.name())+') ' - + 'for name '+str(name)+' ('+str(alpha)+')' ) - - # name, alpha = self._identifier - # if color_dict is None: # manually retrieve color manager palette - # color_dict = self._manager.colors() - # try: - # qcol = color_dict[name] - # if DEBUG: print(' NamedPen: retrieved new QColor (', qcol.getRgb(), ') for name', name) - # except ValueError as exc: - # raise ValueError("Color {:s} is not in list of defined colors".format(str(name)) ) from exc - # if alpha is not None: - # qcol.setAlpha( alpha ) - # super().setColor(qcol) diff --git a/pyqtgraph/opengl/GLViewWidget.py b/pyqtgraph/opengl/GLViewWidget.py index 6dcbf8b1..098d7ce9 100644 --- a/pyqtgraph/opengl/GLViewWidget.py +++ b/pyqtgraph/opengl/GLViewWidget.py @@ -5,6 +5,7 @@ import numpy as np from .. import Vector from .. import functions as fn import warnings +from math import cos, sin, tan, radians ##Vector = QtGui.QVector3D ShareWidget = None @@ -59,10 +60,6 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): self.makeCurrent() - @property - def _dpiRatio(self): - return self.devicePixelRatioF() or 1 - def _updateScreen(self, screen): self._updatePixelRatio() if screen is not None: @@ -79,16 +76,12 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): self._updateScreen(window.screen()) def width(self): - if self._dpiRatio.is_integer(): - return super().width() - else: - return int(super().width() * self._dpiRatio) + dpr = self.devicePixelRatio() + return int(super().width() * dpr) def height(self): - if self._dpiRatio.is_integer(): - return super().height() - else: - return int(super().height() * self._dpiRatio) + dpr = self.devicePixelRatio() + return int(super().height() * dpr) def reset(self): @@ -147,10 +140,10 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): def getViewport(self): vp = self.opts['viewport'] - dpr = self.devicePixelRatio() if vp is None: - return (0, 0, int(self.width() * dpr), int(self.height() * dpr)) + return (0, 0, self.width(), self.height()) else: + dpr = self.devicePixelRatio() return tuple([int(x * dpr) for x in vp]) def devicePixelRatio(self): @@ -158,7 +151,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): if dpr is not None: return dpr - return QtWidgets.QOpenGLWidget.devicePixelRatio(self) + return self.devicePixelRatioF() def resizeGL(self, w, h): pass @@ -174,8 +167,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): def projectionMatrix(self, region=None): if region is None: - dpr = self.devicePixelRatio() - region = (0, 0, self.width() * dpr, self.height() * dpr) + region = (0, 0, self.width(), self.height()) x0, y0, w, h = self.getViewport() dist = self.opts['distance'] @@ -183,7 +175,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): nearClip = dist * 0.001 farClip = dist * 1000. - r = nearClip * np.tan(fov * 0.5 * np.pi / 180.) + r = nearClip * tan(0.5 * radians(fov)) t = r * h / w ## Note that X0 and width in these equations must be the values used in viewport @@ -325,12 +317,12 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): pos = center - self.opts['rotation'].rotatedVector( Vector(0,0,dist) ) else: # using 'euler' rotation method - elev = self.opts['elevation'] * np.pi / 180 - azim = self.opts['azimuth'] * np.pi / 180 + elev = radians(self.opts['elevation']) + azim = radians(self.opts['azimuth']) pos = Vector( - center.x() + dist * np.cos(elev) * np.cos(azim), - center.y() + dist * np.cos(elev) * np.sin(azim), - center.z() + dist * np.sin(elev) + center.x() + dist * cos(elev) * cos(azim), + center.y() + dist * cos(elev) * sin(azim), + center.z() + dist * sin(elev) ) return pos @@ -344,7 +336,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): self.opts['rotation'] = q else: # default euler rotation method self.opts['azimuth'] += azim - self.opts['elevation'] = np.clip(self.opts['elevation'] + elev, -90, 90) + self.opts['elevation'] = fn.clip_scalar(self.opts['elevation'] + elev, -90., 90.) self.update() def pan(self, dx, dy, dz, relative='global'): @@ -387,7 +379,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): cPos = self.cameraPosition() cVec = self.opts['center'] - cPos dist = cVec.length() ## distance from camera to center - xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) ## approx. width of view at distance of center point + xDist = dist * 2. * tan(0.5 * radians(self.opts['fov'])) ## approx. width of view at distance of center point xScale = xDist / self.width() zVec = QtGui.QVector3D(0,0,1) xVec = QtGui.QVector3D.crossProduct(zVec, cVec).normalized() @@ -408,15 +400,15 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): # apply translation self.opts['center'] += scale_factor * (xv*-dx + yv*dy + zv*dz) else: # use default euler rotation method - elev = np.radians(self.opts['elevation']) - azim = np.radians(self.opts['azimuth']) - fov = np.radians(self.opts['fov']) + elev = radians(self.opts['elevation']) + azim = radians(self.opts['azimuth']) + fov = radians(self.opts['fov']) dist = (self.opts['center'] - self.cameraPosition()).length() - fov_factor = np.tan(fov / 2) * 2 + fov_factor = tan(fov / 2) * 2 scale_factor = dist * fov_factor / self.width() - z = scale_factor * np.cos(elev) * dy - x = scale_factor * (np.sin(azim) * dx - np.sin(elev) * np.cos(azim) * dy) - y = scale_factor * (np.cos(azim) * dx + np.sin(elev) * np.sin(azim) * dy) + z = scale_factor * cos(elev) * dy + x = scale_factor * (sin(azim) * dx - sin(elev) * cos(azim) * dy) + y = scale_factor * (cos(azim) * dx + sin(elev) * sin(azim) * dy) self.opts['center'] += QtGui.QVector3D(x, -y, z) else: raise ValueError("relative argument must be global, view, or view-upright") @@ -434,7 +426,7 @@ class GLViewWidget(QtWidgets.QOpenGLWidget): dist = ((pos-cam)**2).sum(axis=-1)**0.5 else: dist = (pos-cam).length() - xDist = dist * 2. * np.tan(0.5 * self.opts['fov'] * np.pi / 180.) + xDist = dist * 2. * tan(0.5 * radians(self.opts['fov'])) return xDist / self.width() def mousePressEvent(self, ev): diff --git a/pyqtgraph/opengl/items/GLScatterPlotItem.py b/pyqtgraph/opengl/items/GLScatterPlotItem.py index 5d81515b..834ede0b 100644 --- a/pyqtgraph/opengl/items/GLScatterPlotItem.py +++ b/pyqtgraph/opengl/items/GLScatterPlotItem.py @@ -3,7 +3,8 @@ from OpenGL.GL import * from OpenGL.arrays import vbo from .. GLGraphicsItem import GLGraphicsItem from .. import shaders -from ... import QtGui +from ... import functions as fn +from ...Qt import QtGui import numpy as np __all__ = ['GLScatterPlotItem'] @@ -61,13 +62,12 @@ class GLScatterPlotItem(GLGraphicsItem): ## Generate texture for rendering points w = 64 - def fn(x,y): - r = ((x-(w-1)/2.)**2 + (y-(w-1)/2.)**2) ** 0.5 - return 255 * (w/2. - np.clip(r, w/2.-1.0, w/2.)) + def genTexture(x,y): + r = np.hypot((x-(w-1)/2.), (y-(w-1)/2.)) + return 255 * (w / 2 - fn.clip_array(r, w / 2 - 1, w / 2)) pData = np.empty((w, w, 4)) pData[:] = 255 - pData[:,:,3] = np.fromfunction(fn, pData.shape[:2]) - #print pData.shape, pData.min(), pData.max() + pData[:,:,3] = np.fromfunction(genTexture, pData.shape[:2]) pData = pData.astype(np.ubyte) if getattr(self, "pointTexture", None) is None: diff --git a/pyqtgraph/palette.py b/pyqtgraph/palette.py index e5f611b2..2ab3c136 100644 --- a/pyqtgraph/palette.py +++ b/pyqtgraph/palette.py @@ -33,8 +33,8 @@ PALETTE_DEFINITIONS = { 'col_gr1':'#19232D', 'col_gr2':'#32414B', 'col_gr3':'#505F69', # match QDarkStyle background colors 'col_gr4':'#787878', 'col_gr5':'#AAAAAA', 'col_gr6':'#F0F0F0', # match QDarkstyle foreground colors # --- functional colors --- - 'gr_fg' : 'col_gr4', 'gr_bg' : 'col_gr1', - 'gr_txt': 'col_gr5', 'gr_acc': '#1464A0', #col_cyan', + 'gr_fg' : 'col_gr4', 'gr_bg' : '#101518', + 'gr_txt': 'col_gr5', 'gr_acc': '#1464A0', 'gr_hlt': 'col_white', 'gr_reg': ('#1464A0',100) # legacy colors: # 'b': 'col_l_blue' , 'c': 'col_l_cyan', 'g': 'col_l_green', @@ -425,4 +425,4 @@ class Palette(object): Applies this palette to the overall PyQtGraph color scheme. This provides the palette to NamedColorManager, which triggers a global refresh of named colors """ - fn.NAMED_COLOR_MANAGER.redefinePalette( colors=self.palette ) + fn.COLOR_REGISTRY.redefinePalette( colors=self.palette ) diff --git a/pyqtgraph/parametertree/Parameter.py b/pyqtgraph/parametertree/Parameter.py index 2d830273..c084634a 100644 --- a/pyqtgraph/parametertree/Parameter.py +++ b/pyqtgraph/parametertree/Parameter.py @@ -201,6 +201,7 @@ class Parameter(QtCore.QObject): if 'default' not in self.opts: self.opts['default'] = None + self.setDefault(self.opts['value']) ## Connect all state changed signals to the general sigStateChanged self.sigValueChanged.connect(self._emitValueChanged) diff --git a/pyqtgraph/parametertree/SystemSolver.py b/pyqtgraph/parametertree/SystemSolver.py index cac42483..d7b6bef8 100644 --- a/pyqtgraph/parametertree/SystemSolver.py +++ b/pyqtgraph/parametertree/SystemSolver.py @@ -1,6 +1,8 @@ from collections import OrderedDict import numpy as np import copy +from math import log2 +from .. import functions as fn class SystemSolver(object): @@ -390,15 +392,12 @@ if __name__ == '__main__': sh = self.shutter # this raises RuntimeError if shutter has not # been specified ap = 4.0 * (sh / (1./60.)) * (iso / 100.) * (2 ** exp) * (2 ** light) - ap = np.clip(ap, 2.0, 16.0) + ap = fn.clip_scalar(ap, 2.0, 16.0) except RuntimeError: # program mode; we can select a suitable shutter # value at the same time. sh = (1./60.) raise - - - return ap def _balance(self): @@ -406,10 +405,8 @@ if __name__ == '__main__': light = self.lightMeter sh = self.shutter ap = self.aperture - fl = self.flash - bal = (4.0 / ap) * (sh / (1./60.)) * (iso / 100.) * (2 ** light) - return np.log2(bal) + return log2(bal) camera = Camera() diff --git a/pyqtgraph/parametertree/tests/test_Parameter.py b/pyqtgraph/parametertree/tests/test_Parameter.py index 70feab5f..e0c5a985 100644 --- a/pyqtgraph/parametertree/tests/test_Parameter.py +++ b/pyqtgraph/parametertree/tests/test_Parameter.py @@ -8,32 +8,23 @@ def test_parameter_hasdefault(): # default unspecified p = Parameter(**opts) - assert not p.hasDefault() - - p.setDefault(1) assert p.hasDefault() - assert p.defaultValue() == 1 + assert p.defaultValue() == opts["value"] + + p.setDefault(2) + assert p.hasDefault() + assert p.defaultValue() == 2 # default specified p = Parameter(default=0, **opts) assert p.hasDefault() assert p.defaultValue() == 0 - -@pytest.mark.parametrize('passdefault', [True, False]) -def test_parameter_hasdefault_none(passdefault): - # test that Parameter essentially ignores defualt=None, same as not passing - # a default at all - opts = {'name': 'param', 'type': int, 'value': 0} - if passdefault: - opts['default'] = None - - p = Parameter(**opts) + # default specified as None + p = Parameter(default=None, **opts) assert not p.hasDefault() - assert p.defaultValue() is None - p.setDefault(None) - assert not p.hasDefault() + def test_unpack_parameter(): # test that **unpacking correctly returns child name/value maps diff --git a/pyqtgraph/parametertree/tests/test_parametertypes.py b/pyqtgraph/parametertree/tests/test_parametertypes.py index 28b10c88..7898a533 100644 --- a/pyqtgraph/parametertree/tests/test_parametertypes.py +++ b/pyqtgraph/parametertree/tests/test_parametertypes.py @@ -46,7 +46,7 @@ def test_types(): all_objs = { 'int0': 0, 'int':7, 'float': -0.35, 'bigfloat': 1e129, 'npfloat': np.float64(5), 'npint': np.int64(5),'npinf': np.inf, 'npnan': np.nan, 'bool': True, - 'complex': 5+3j, 'str': 'xxx', 'unicode': asUnicode('µ'), + 'complex': 5+3j, 'str': '#xxx', 'unicode': asUnicode('µ'), 'list': [1,2,3], 'dict': {'1': 2}, 'color': pg.mkColor('k'), 'brush': pg.mkBrush('k'), 'pen': pg.mkPen('k'), 'none': None } diff --git a/pyqtgraph/reload.py b/pyqtgraph/reload.py index 05ef8f0f..83d78046 100644 --- a/pyqtgraph/reload.py +++ b/pyqtgraph/reload.py @@ -45,7 +45,7 @@ def reloadAll(prefix=None, debug=False): failed = [] changed = [] ret = {} - for modName, mod in list(sys.modules.items()): ## don't use iteritems; size may change during reload + for modName, mod in list(sys.modules.items()): if not inspect.ismodule(mod): ret[modName] = (False, 'not a module') continue @@ -331,10 +331,6 @@ if __name__ == '__main__': btn = Btn() except: raise - print("Error; skipping Qt tests") - doQtTest = False - - import os if not os.path.isdir('test1'): diff --git a/pyqtgraph/tests/conftest.py b/pyqtgraph/tests/conftest.py new file mode 100644 index 00000000..01b08995 --- /dev/null +++ b/pyqtgraph/tests/conftest.py @@ -0,0 +1,10 @@ +import pytest +import os +import sys + +@pytest.fixture +def tmp_module(tmp_path): + module_path = os.fsdecode(tmp_path) + sys.path.insert(0, module_path) + yield module_path + sys.path.remove(module_path) diff --git a/pyqtgraph/tests/image_testing.py b/pyqtgraph/tests/image_testing.py index bfb8dc0f..e64df55e 100644 --- a/pyqtgraph/tests/image_testing.py +++ b/pyqtgraph/tests/image_testing.py @@ -145,8 +145,13 @@ def assertImageApproved(image, standardFile, message=None, **kwargs): image = fn.imageToArray(qimg, copy=False, transpose=False) - # transpose BGRA to RGBA - image = image[..., [2, 1, 0, 3]] + # 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: code = inspect.currentframe().f_back.f_code diff --git a/pyqtgraph/tests/test_Point.py b/pyqtgraph/tests/test_Point.py new file mode 100644 index 00000000..a8252e11 --- /dev/null +++ b/pyqtgraph/tests/test_Point.py @@ -0,0 +1,63 @@ +import pytest +import pyqtgraph as pg +from pyqtgraph.Qt import QtCore +import math + +angles = [ + ((1, 0), (0, 1), 90), + ((0, 1), (1, 0), -90), + ((-1, 0), (-1, 0), 0), + ((0, -1), (0, 1), 180), +] +@pytest.mark.parametrize("p1, p2, angle", angles) +def test_Point_angle(p1, p2, angle): + p1 = pg.Point(*p1) + p2 = pg.Point(*p2) + assert p1.angle(p2) == angle + + +inits = [ + (QtCore.QSizeF(1, 0), (1.0, 0.0)), + ((0, -1), (0.0, -1.0)), + ([1, 1], (1.0, 1.0)), +] +@pytest.mark.parametrize("initArgs, positions", inits) +def test_Point_init(initArgs, positions): + if isinstance(initArgs, QtCore.QSizeF): + point = pg.Point(initArgs) + else: + point = pg.Point(*initArgs) + assert (point.x(), point.y()) == positions + +lengths = [ + ((0, 1), 1), + ((1, 0), 1), + ((0, 0), 0), + ((1, 1), math.sqrt(2)), + ((-1, -1), math.sqrt(2)) +] +@pytest.mark.parametrize("initArgs, length", lengths) +def test_Point_length(initArgs, length): + point = pg.Point(initArgs) + assert point.length() == length + +min_max = [ + ((0, 1), 0, 1), + ((1, 0), 0, 1), + ((-math.inf, 0), -math.inf, 0), + ((0, math.inf), 0, math.inf) +] +@pytest.mark.parametrize("initArgs, min_, max_", min_max) +def test_Point_min_max(initArgs, min_, max_): + point = pg.Point(initArgs) + assert min(point) == min_ + assert max(point) == max_ + +projections = [ + ((0, 1), (1, 0), (1, 1)) +] +@pytest.mark.parametrize("p1_arg, p2_arg, projection", projections) +def test_Point_projection(p1_arg, p2_arg, projection): + p1 = pg.Point(p1_arg) + p2 = pg.Point(p2_arg) + p1.proj(p2) == projection \ No newline at end of file diff --git a/pyqtgraph/tests/test_Vector.py b/pyqtgraph/tests/test_Vector.py index dcae6f9d..3b608f83 100644 --- a/pyqtgraph/tests/test_Vector.py +++ b/pyqtgraph/tests/test_Vector.py @@ -15,7 +15,13 @@ def test_Vector_init(): # separate values with 3 args v = pg.Vector(0, 1, 2) + assert v.x() == 0 + assert v.y() == 1 + assert v.z() == 2 v = pg.Vector(0.0, 1.0, 2.0) + assert v.x() == 0 + assert v.y() == 1 + assert v.z() == 2 # all in a list v = pg.Vector([0, 1]) diff --git a/pyqtgraph/tests/test_functions.py b/pyqtgraph/tests/test_functions.py index ea35cc28..6f90567c 100644 --- a/pyqtgraph/tests/test_functions.py +++ b/pyqtgraph/tests/test_functions.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -import pyqtgraph as pg -import numpy as np -import sys -from copy import deepcopy from collections import OrderedDict -from numpy.testing import assert_array_almost_equal, assert_almost_equal -import pytest +from copy import deepcopy +import numpy as np +import pytest +from numpy.testing import assert_array_almost_equal + +import pyqtgraph as pg np.random.seed(12345) @@ -97,21 +97,8 @@ def check_interpolateArray(order): #[ 82.5 , 110. , 165. ]]) assert_array_almost_equal(r1, r2) - -def test_subArray(): - a = np.array([0, 0, 111, 112, 113, 0, 121, 122, 123, 0, 0, 0, 211, 212, 213, 0, 221, 222, 223, 0, 0, 0, 0]) - b = pg.subArray(a, offset=2, shape=(2,2,3), stride=(10,4,1)) - c = np.array([[[111,112,113], [121,122,123]], [[211,212,213], [221,222,223]]]) - assert np.all(b == c) - - # operate over first axis; broadcast over the rest - aa = np.vstack([a, a/100.]).T - cc = np.empty(c.shape + (2,)) - cc[..., 0] = c - cc[..., 1] = c / 100. - bb = pg.subArray(aa, offset=2, shape=(2,2,3), stride=(10,4,1)) - assert np.all(bb == cc) - + + def test_subArray(): a = np.array([0, 0, 111, 112, 113, 0, 121, 122, 123, 0, 0, 0, 211, 212, 213, 0, 221, 222, 223, 0, 0, 0, 0]) b = pg.subArray(a, offset=2, shape=(2,2,3), stride=(10,4,1)) @@ -147,196 +134,6 @@ def test_rescaleData(): assert np.allclose(s1, s2) -def test_makeARGB(): - # Many parameters to test here: - # * data dtype (ubyte, uint16, float, others) - # * data ndim (2 or 3) - # * levels (None, 1D, or 2D) - # * lut dtype - # * lut size - # * lut ndim (1 or 2) - # * useRGBA argument - # Need to check that all input values map to the correct output values, especially - # at and beyond the edges of the level range. - - def checkArrays(a, b): - # because py.test output is difficult to read for arrays - if not np.all(a == b): - comp = [] - for i in range(a.shape[0]): - if a.shape[1] > 1: - comp.append('[') - for j in range(a.shape[1]): - m = a[i,j] == b[i,j] - comp.append('%d,%d %s %s %s%s' % - (i, j, str(a[i,j]).ljust(15), str(b[i,j]).ljust(15), - m, ' ********' if not np.all(m) else '')) - if a.shape[1] > 1: - comp.append(']') - raise Exception("arrays do not match:\n%s" % '\n'.join(comp)) - - def checkImage(img, check, alpha, alphaCheck): - assert img.dtype == np.ubyte - assert alpha is alphaCheck - if alpha is False: - checkArrays(img[..., 3], 255) - - if np.isscalar(check) or check.ndim == 3: - checkArrays(img[..., :3], check) - elif check.ndim == 2: - checkArrays(img[..., :3], check[..., np.newaxis]) - elif check.ndim == 1: - checkArrays(img[..., :3], check[..., np.newaxis, np.newaxis]) - else: - raise Exception('invalid check array ndim') - - # uint8 data tests - - im1 = np.arange(256).astype('ubyte').reshape(256, 1) - im2, alpha = pg.makeARGB(im1, levels=(0, 255)) - checkImage(im2, im1, alpha, False) - - im3, alpha = pg.makeARGB(im1, levels=(0.0, 255.0)) - checkImage(im3, im1, alpha, False) - - im4, alpha = pg.makeARGB(im1, levels=(255, 0)) - checkImage(im4, 255-im1, alpha, False) - - im5, alpha = pg.makeARGB(np.concatenate([im1]*3, axis=1), levels=[(0, 255), (0.0, 255.0), (255, 0)]) - checkImage(im5, np.concatenate([im1, im1, 255-im1], axis=1), alpha, False) - - - im2, alpha = pg.makeARGB(im1, levels=(128,383)) - checkImage(im2[:128], 0, alpha, False) - checkImage(im2[128:], im1[:128], alpha, False) - - - # uint8 data + uint8 LUT - lut = np.arange(256)[::-1].astype(np.uint8) - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, lut, alpha, False) - - # lut larger than maxint - lut = np.arange(511).astype(np.uint8) - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, lut[::2], alpha, False) - - # lut smaller than maxint - lut = np.arange(128).astype(np.uint8) - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, np.linspace(0, 127.5, 256, dtype='ubyte'), alpha, False) - - # lut + levels - lut = np.arange(256)[::-1].astype(np.uint8) - im2, alpha = pg.makeARGB(im1, lut=lut, levels=[-128, 384]) - checkImage(im2, np.linspace(191.5, 64.5, 256, dtype='ubyte'), alpha, False) - - im2, alpha = pg.makeARGB(im1, lut=lut, levels=[64, 192]) - checkImage(im2, np.clip(np.linspace(384.5, -127.5, 256), 0, 255).astype('ubyte'), alpha, False) - - # uint8 data + uint16 LUT - lut = np.arange(4096)[::-1].astype(np.uint16) // 16 - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, np.arange(256)[::-1].astype('ubyte'), alpha, False) - - # uint8 data + float LUT - lut = np.linspace(10., 137., 256) - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, lut.astype('ubyte'), alpha, False) - - # uint8 data + 2D LUT - lut = np.zeros((256, 3), dtype='ubyte') - lut[:,0] = np.arange(256) - lut[:,1] = np.arange(256)[::-1] - lut[:,2] = 7 - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, lut[:,None,::-1], alpha, False) - - # check useRGBA - im2, alpha = pg.makeARGB(im1, lut=lut, useRGBA=True) - checkImage(im2, lut[:,None,:], alpha, False) - - - # uint16 data tests - im1 = np.arange(0, 2**16, 256).astype('uint16')[:, None] - im2, alpha = pg.makeARGB(im1, levels=(512, 2**16)) - checkImage(im2, np.clip(np.linspace(-2, 253, 256), 0, 255).astype('ubyte'), alpha, False) - - lut = (np.arange(512, 2**16)[::-1] // 256).astype('ubyte') - im2, alpha = pg.makeARGB(im1, lut=lut, levels=(512, 2**16-256)) - checkImage(im2, np.clip(np.linspace(257, 2, 256), 0, 255).astype('ubyte'), alpha, False) - - lut = np.zeros(2**16, dtype='ubyte') - lut[1000:1256] = np.arange(256) - lut[1256:] = 255 - im1 = np.arange(1000, 1256).astype('uint16')[:, None] - im2, alpha = pg.makeARGB(im1, lut=lut) - checkImage(im2, np.arange(256).astype('ubyte'), alpha, False) - - - - # float data tests - im1 = np.linspace(1.0, 17.0, 256)[:, None] - im2, alpha = pg.makeARGB(im1, levels=(5.0, 13.0)) - checkImage(im2, np.clip(np.linspace(-128, 383, 256), 0, 255).astype('ubyte'), alpha, False) - - lut = (np.arange(1280)[::-1] // 10).astype('ubyte') - im2, alpha = pg.makeARGB(im1, lut=lut, levels=(1, 17)) - checkImage(im2, np.linspace(127.5, 0, 256).astype('ubyte'), alpha, False) - - # nans in image - - # 2d input image, one pixel is nan - im1 = np.ones((10, 12)) - im1[3, 5] = np.nan - im2, alpha = pg.makeARGB(im1, levels=(0, 1)) - assert alpha - assert im2[3, 5, 3] == 0 # nan pixel is transparent - assert im2[0, 0, 3] == 255 # doesn't affect other pixels - - # 3d RGB input image, any color channel of a pixel is nan - im1 = np.ones((10, 12, 3)) - im1[3, 5, 1] = np.nan - im2, alpha = pg.makeARGB(im1, levels=(0, 1)) - assert alpha - assert im2[3, 5, 3] == 0 # nan pixel is transparent - assert im2[0, 0, 3] == 255 # doesn't affect other pixels - - # 3d RGBA input image, any color channel of a pixel is nan - im1 = np.ones((10, 12, 4)) - im1[3, 5, 1] = np.nan - im2, alpha = pg.makeARGB(im1, levels=(0, 1), useRGBA=True) - assert alpha - assert im2[3, 5, 3] == 0 # nan pixel is transparent - - # test sanity checks - class AssertExc(object): - def __init__(self, exc=Exception): - self.exc = exc - def __enter__(self): - return self - def __exit__(self, *args): - assert args[0] is self.exc, "Should have raised %s (got %s)" % (self.exc, args[0]) - return True - - with AssertExc(TypeError): # invalid image shape - pg.makeARGB(np.zeros((2,), dtype='float')) - with AssertExc(TypeError): # invalid image shape - pg.makeARGB(np.zeros((2,2,7), dtype='float')) - with AssertExc(): # float images require levels arg - pg.makeARGB(np.zeros((2,2), dtype='float')) - with AssertExc(): # bad levels arg - pg.makeARGB(np.zeros((2,2), dtype='float'), levels=[1]) - with AssertExc(): # bad levels arg - pg.makeARGB(np.zeros((2,2), dtype='float'), levels=[1,2,3]) - with AssertExc(): # can't mix 3-channel levels and LUT - pg.makeARGB(np.zeros((2,2)), lut=np.zeros((10,3), dtype='ubyte'), levels=[(0,1)]*3) - with AssertExc(): # multichannel levels must have same number of channels as image - pg.makeARGB(np.zeros((2,2,3), dtype='float'), levels=[(1,2)]*4) - with AssertExc(): # 3d levels not allowed - pg.makeARGB(np.zeros((2,2,3), dtype='float'), levels=np.zeros([3, 2, 2])) - - def test_eq(): eq = pg.functions.eq diff --git a/pyqtgraph/tests/test_makeARGB.py b/pyqtgraph/tests/test_makeARGB.py new file mode 100644 index 00000000..e11938ae --- /dev/null +++ b/pyqtgraph/tests/test_makeARGB.py @@ -0,0 +1,4532 @@ +# -*- coding: utf-8 -*- +import numpy as np +import pytest +import sys +from typing import Dict, Any, Union, Type + +from pyqtgraph import getCupy, getConfigOption, setConfigOption +from pyqtgraph.functions import makeARGB as real_makeARGB +try: + import cupy +except ImportError: + cupy = None + +IN_2D_INT8 = np.array([[173, 48, 122, 41], [210, 192, 0, 5], [104, 56, 102, 115], [78, 19, 255, 6]], dtype=np.uint8) +IN_RGB_INT8 = np.array( + [ + [[16, 69, 62], [66, 132, 135], [220, 80, 36], [53, 34, 68]], + [[140, 23, 113], [0, 63, 206], [96, 255, 100], [226, 182, 155]], + [[28, 237, 223], [215, 232, 209], [17, 16, 50], [96, 187, 93]], + [[220, 193, 232], [134, 168, 150], [55, 64, 221], [96, 108, 227]], + ], + dtype=np.uint8, +) +IN_RGBA_INT8 = np.array( + [ + [[151, 252, 73, 107], [28, 221, 35, 0], [87, 122, 126, 114], [47, 59, 24, 200]], + [[189, 246, 242, 255], [123, 255, 29, 14], [201, 208, 133, 32], [118, 203, 141, 245]], + [[133, 131, 248, 81], [4, 84, 99, 40], [40, 167, 119, 150], [13, 158, 108, 21]], + [[156, 221, 166, 250], [77, 188, 13, 166], [0, 1, 185, 25], [83, 35, 103, 120]], + ], + dtype=np.uint8, +) +IN_2D_INT16 = np.array( + [ + [13364, 55041, 40746, 40937], + [57612, 34247, 34132, 0], + [10109, 56950, 41856, 21479], + [14881, 65535, 48079, 11372], + ], + dtype=np.uint16, +) +IN_RGB_INT16 = np.array( + [ + [[55626, 45263, 0], [19468, 39208, 36391], [33255, 8664, 56991], [37588, 31212, 38295]], + [[58933, 16402, 36905], [9928, 23928, 12418], [16461, 47738, 18189], [17004, 39307, 59941]], + [[43717, 49573, 9843], [35967, 3891, 39618], [53542, 58151, 29112], [53667, 4092, 35267]], + [[15957, 21648, 45238], [65535, 47107, 52049], [6342, 34547, 19902], [43386, 37301, 35095]], + ], + dtype=np.uint16, +) +IN_RGBA_INT16 = np.array( + [ + [ + [13060, 40847, 29621, 46719], + [0, 36509, 33525, 56649], + [48328, 23093, 47186, 26801], + [57336, 12247, 30996, 11691], + ], + [ + [4863, 41121, 32045, 25250], + [27779, 65098, 59921, 47771], + [8906, 18280, 5066, 48587], + [65535, 25758, 27250, 17284], + ], + [ + [52005, 65535, 40746, 65535], + [33, 57630, 27750, 42371], + [50176, 35079, 19220, 63662], + [17702, 5506, 36216, 48303], + ], + [ + [61592, 27692, 37436, 7249], + [54653, 39986, 58441, 12819], + [20887, 56588, 32440, 85], + [13457, 14661, 58972, 48779], + ], + ], + dtype=np.uint16, +) +IN_2D_FLOAT = np.array( + [ + [np.inf, 0.53662884, np.nan, 0.8853132], + [0.8496698, 0.88006145, 1.0, 0.06621328], + [0.99158293, 0.8476984, 0.16672458, 0.9887786], + [0.07076367, 0.66354364, 0.8781082, 0.988832], + ], + dtype=np.float32, +) +IN_RGB_FLOAT = np.array( + [ + [ + [0.23317624, 0.39086635, 0.12795302], + [0.40659714, 0.9079258, 0.28431135], + [0.91651599, 0.46082205, 0.16928465], + [0.29368765, 0.97987488, 0.72257988], + ], + [ + [np.nan, 0.72908475, 0.54018012], + [0.91277435, 0.2842577, 0.73481915], + [0.33844504, 0.22060913, 0.9802894], + [0.13995676, 0.34752838, 0.39652277], + ], + [ + [0.85315026, 0.19330797, 0.0], + [0.48584232, 0.04943356, 0.59906953], + [np.inf, 0.32614581, 0.1483722], + [0.37340863, 0.35432855, 0.08973532], + ], + [ + [0.69666134, 0.52481322, 0.49057305], + [0.93366339, 0.1428689, 0.6845513], + [0.27681383, 0.69472673, 0.06750892], + [0.26349886, 0.25841691, 0.86171104], + ], + ] +) +IN_RGBA_FLOAT = np.array( + [ + [ + [0.97383172, 0.62680971, 0.02285016, np.nan], + [0.85295433, 0.93014834, 0.59482999, np.inf], + [0.4017482, 0.79809183, 0.22407464, 0.17327807], + [0.95953263, 0.69535086, 0.28846483, 0.76970823], + ], + [ + [0.11487603, 0.7447609, 0.06767498, 0.98054729], + [0.66071068, 0.73931366, 0.33155686, 0.81827122], + [0.78035892, 0.52920802, 0.5671388, 0.31783899], + [0.81709002, 0.82204682, 0.82584029, 0.49434749], + ], + [ + [0.03142089, 0.8322058, 0.31646922, 0.94636969], + [0.62381573, 0.60052138, 0.50244611, 0.92886007], + [np.nan, np.nan, 0.02940048, 0.52529675], + [0.9786162, 0.54928697, 0.2409731, 0.34705397], + ], + [ + [0.68922233, np.inf, 0.85027734, 0.35388624], + [0.16489042, 0.29860162, 0.09349833, 0.67714667], + [0.25351483, 0.25596098, 0.80461891, 0.99952403], + [0.0, 1.0, 0.58084746, 0.46211944], + ], + ] +) +INPUTS: Dict[Any, np.ndarray] = { + (np.uint8, "2D"): IN_2D_INT8, + (np.uint8, "RGB"): IN_RGB_INT8, + (np.uint8, "RGBA"): IN_RGBA_INT8, + (np.uint16, "2D"): IN_2D_INT16, + (np.uint16, "RGB"): IN_RGB_INT16, + (np.uint16, "RGBA"): IN_RGBA_INT16, + (np.float32, "2D"): IN_2D_FLOAT, + (np.float32, "RGB"): IN_RGB_FLOAT, + (np.float32, "RGBA"): IN_RGBA_FLOAT, +} +LUT8 = np.zeros((255,), dtype=np.uint8) +LUT8[::2] = 255 +LUT16 = np.zeros((65535,), dtype=np.uint8) +LUT16[::3] = 255 +LUTS: Dict[Any, np.ndarray] = { + np.uint8: LUT8, + np.uint16: LUT16, +} +LEVELS = { + "SIMPLE": (0, 1), + "RGB": ((0, 255), (1, 250), (100, 160)), + "RGBA": ((255, 11111), (100, 10000), (0, 255), (127, 255)), +} + +EXPECTED_OUTPUTS: Dict[tuple, Union[Type[Exception], np.ndarray]] = { + (np.uint8, "2D", None, None, None, True): np.array( + [ + [[173, 173, 173, 255], [48, 48, 48, 255], [122, 122, 122, 255], [41, 41, 41, 255]], + [[210, 210, 210, 255], [192, 192, 192, 255], [0, 0, 0, 255], [5, 5, 5, 255]], + [[104, 104, 104, 255], [56, 56, 56, 255], [102, 102, 102, 255], [115, 115, 115, 255]], + [[78, 78, 78, 255], [19, 19, 19, 255], [255, 255, 255, 255], [6, 6, 6, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, None, None, False): np.array( + [ + [[173, 173, 173, 255], [48, 48, 48, 255], [122, 122, 122, 255], [41, 41, 41, 255]], + [[210, 210, 210, 255], [192, 192, 192, 255], [0, 0, 0, 255], [5, 5, 5, 255]], + [[104, 104, 104, 255], [56, 56, 56, 255], [102, 102, 102, 255], [115, 115, 115, 255]], + [[78, 78, 78, 255], [19, 19, 19, 255], [255, 255, 255, 255], [6, 6, 6, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, None, 232, True): np.array( + [ + [[157, 157, 157, 255], [43, 43, 43, 255], [110, 110, 110, 255], [37, 37, 37, 255]], + [[191, 191, 191, 255], [174, 174, 174, 255], [0, 0, 0, 255], [4, 4, 4, 255]], + [[94, 94, 94, 255], [50, 50, 50, 255], [92, 92, 92, 255], [104, 104, 104, 255]], + [[70, 70, 70, 255], [17, 17, 17, 255], [232, 232, 232, 255], [5, 5, 5, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, None, 232, False): np.array( + [ + [[157, 157, 157, 255], [43, 43, 43, 255], [110, 110, 110, 255], [37, 37, 37, 255]], + [[191, 191, 191, 255], [174, 174, 174, 255], [0, 0, 0, 255], [4, 4, 4, 255]], + [[94, 94, 94, 255], [50, 50, 50, 255], [92, 92, 92, 255], [104, 104, 104, 255]], + [[70, 70, 70, 255], [17, 17, 17, 255], [232, 232, 232, 255], [5, 5, 5, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint8, None, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint8, None, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint8, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint8, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint16, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", None, np.uint16, 13333, False): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGB", None, None, True): Exception, + (np.uint8, "2D", "RGB", None, None, False): Exception, + (np.uint8, "2D", "RGB", None, 232, True): Exception, + (np.uint8, "2D", "RGB", None, 232, False): Exception, + (np.uint8, "2D", "RGB", None, 13333, True): Exception, + (np.uint8, "2D", "RGB", None, 13333, False): Exception, + (np.uint8, "2D", "RGB", np.uint8, None, True): Exception, + (np.uint8, "2D", "RGB", np.uint8, None, False): Exception, + (np.uint8, "2D", "RGB", np.uint8, 232, True): Exception, + (np.uint8, "2D", "RGB", np.uint8, 232, False): Exception, + (np.uint8, "2D", "RGB", np.uint8, 13333, True): Exception, + (np.uint8, "2D", "RGB", np.uint8, 13333, False): Exception, + (np.uint8, "2D", "RGB", np.uint16, None, True): Exception, + (np.uint8, "2D", "RGB", np.uint16, None, False): Exception, + (np.uint8, "2D", "RGB", np.uint16, 232, True): Exception, + (np.uint8, "2D", "RGB", np.uint16, 232, False): Exception, + (np.uint8, "2D", "RGB", np.uint16, 13333, True): Exception, + (np.uint8, "2D", "RGB", np.uint16, 13333, False): Exception, + (np.uint8, "2D", "RGBA", None, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [122, 122, 122, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [2, 2, 2, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [102, 102, 102, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", None, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [122, 122, 122, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [2, 2, 2, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [102, 102, 102, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", None, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [110, 110, 110, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [2, 2, 2, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [92, 92, 92, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [232, 232, 232, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", None, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [110, 110, 110, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [2, 2, 2, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [92, 92, 92, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [232, 232, 232, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", None, 13333, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [123, 123, 123, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", None, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [123, 123, 123, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint16, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint16, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint16, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint16, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint16, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "2D", "RGBA", np.uint16, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, None, None, True): np.array( + [ + [[16, 69, 62, 255], [66, 132, 135, 255], [220, 80, 36, 255], [53, 34, 68, 255]], + [[140, 23, 113, 255], [0, 63, 206, 255], [96, 255, 100, 255], [226, 182, 155, 255]], + [[28, 237, 223, 255], [215, 232, 209, 255], [17, 16, 50, 255], [96, 187, 93, 255]], + [[220, 193, 232, 255], [134, 168, 150, 255], [55, 64, 221, 255], [96, 108, 227, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, None, None, False): np.array( + [ + [[62, 69, 16, 255], [135, 132, 66, 255], [36, 80, 220, 255], [68, 34, 53, 255]], + [[113, 23, 140, 255], [206, 63, 0, 255], [100, 255, 96, 255], [155, 182, 226, 255]], + [[223, 237, 28, 255], [209, 232, 215, 255], [50, 16, 17, 255], [93, 187, 96, 255]], + [[232, 193, 220, 255], [150, 168, 134, 255], [221, 64, 55, 255], [227, 108, 96, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, None, 232, True): np.array( + [ + [[14, 62, 56, 255], [60, 120, 122, 255], [200, 72, 32, 255], [48, 30, 61, 255]], + [[127, 20, 102, 255], [0, 57, 187, 255], [87, 232, 90, 255], [205, 165, 141, 255]], + [[25, 215, 202, 255], [195, 211, 190, 255], [15, 14, 45, 255], [87, 170, 84, 255]], + [[200, 175, 211, 255], [121, 152, 136, 255], [50, 58, 201, 255], [87, 98, 206, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, None, 232, False): np.array( + [ + [[56, 62, 14, 255], [122, 120, 60, 255], [32, 72, 200, 255], [61, 30, 48, 255]], + [[102, 20, 127, 255], [187, 57, 0, 255], [90, 232, 87, 255], [141, 165, 205, 255]], + [[202, 215, 25, 255], [190, 211, 195, 255], [45, 14, 15, 255], [84, 170, 87, 255]], + [[211, 175, 200, 255], [136, 152, 121, 255], [201, 58, 50, 255], [206, 98, 87, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint8, None, True): np.array( + [ + [[255, 0, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[255, 0, 0, 255], [255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[255, 0, 0, 255], [0, 255, 0, 255], [0, 255, 255, 255], [255, 0, 0, 255]], + [[255, 0, 255, 255], [255, 255, 255, 255], [0, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint8, None, False): np.array( + [ + [[255, 0, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[0, 0, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[0, 0, 255, 255], [0, 255, 0, 255], [255, 255, 0, 255], [0, 0, 255, 255]], + [[255, 0, 255, 255], [255, 255, 255, 255], [0, 255, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[0, 255, 255, 255], [255, 0, 0, 255], [0, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 255, 255], [0, 0, 255, 255], [0, 255, 0, 255], [0, 255, 255, 255]], + [[255, 0, 0, 255], [0, 255, 255, 255], [255, 255, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 0, 255], [0, 0, 255, 255], [255, 255, 0, 255], [0, 0, 0, 255]], + [[255, 0, 0, 255], [255, 0, 0, 255], [0, 255, 0, 255], [255, 255, 0, 255]], + [[0, 0, 255, 255], [255, 255, 0, 255], [0, 255, 255, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint16, None, True): np.array( + [ + [[0, 255, 0, 255], [255, 255, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [0, 0, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint16, None, False): np.array( + [ + [[0, 255, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [0, 0, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 0, 255], [0, 255, 0, 255], [255, 255, 0, 255]], + [[0, 0, 255, 255], [255, 255, 0, 255], [255, 0, 255, 255], [0, 255, 255, 255]], + [[0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255], [255, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 255, 255, 255], [0, 255, 0, 255], [0, 255, 255, 255]], + [[255, 0, 0, 255], [0, 255, 255, 255], [255, 0, 255, 255], [255, 255, 0, 255]], + [[0, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255], [0, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [255, 0, 0, 255], [255, 255, 0, 255], [0, 0, 255, 255]], + [[255, 0, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255], [0, 255, 0, 255]], + [[255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 255, 0, 255]], + [[255, 0, 0, 255], [0, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", None, np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 255, 255], [0, 255, 255, 255], [255, 0, 0, 255]], + [[0, 0, 255, 255], [255, 255, 255, 255], [0, 0, 255, 255], [0, 255, 0, 255]], + [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 255, 255, 255]], + [[0, 0, 255, 255], [255, 255, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 255, 0, 255], [255, 255, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [0, 0, 0, 255]], + [[0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [0, 0, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 255, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 0, 0, 255]], + [[0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [0, 0, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", None, None, True): np.array( + [ + [[16, 69, 0, 255], [66, 134, 148, 255], [220, 80, 0, 255], [53, 33, 0, 255]], + [[140, 22, 55, 255], [0, 63, 255, 255], [96, 255, 0, 255], [226, 185, 233, 255]], + [[28, 241, 255, 255], [215, 236, 255, 255], [17, 15, 0, 255], [96, 190, 0, 255]], + [[220, 196, 255, 255], [134, 171, 212, 255], [55, 64, 255, 255], [96, 109, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", None, None, False): np.array( + [ + [[0, 69, 16, 255], [148, 134, 66, 255], [0, 80, 220, 255], [0, 33, 53, 255]], + [[55, 22, 140, 255], [255, 63, 0, 255], [0, 255, 96, 255], [233, 185, 226, 255]], + [[255, 241, 28, 255], [255, 236, 215, 255], [0, 15, 17, 255], [0, 190, 96, 255]], + [[255, 196, 220, 255], [212, 171, 134, 255], [255, 64, 55, 255], [255, 109, 96, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", None, 232, True): np.array( + [ + [[14, 63, 0, 255], [60, 122, 135, 255], [200, 73, 0, 255], [48, 30, 0, 255]], + [[127, 20, 50, 255], [0, 57, 255, 255], [87, 236, 0, 255], [205, 168, 212, 255]], + [[25, 219, 255, 255], [195, 215, 255, 255], [15, 13, 0, 255], [87, 173, 0, 255]], + [[200, 178, 255, 255], [121, 155, 193, 255], [50, 58, 255, 255], [87, 99, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", None, 232, False): np.array( + [ + [[0, 63, 14, 255], [135, 122, 60, 255], [0, 73, 200, 255], [0, 30, 48, 255]], + [[50, 20, 127, 255], [255, 57, 0, 255], [0, 236, 87, 255], [212, 168, 205, 255]], + [[255, 219, 25, 255], [255, 215, 195, 255], [0, 13, 15, 255], [0, 173, 87, 255]], + [[255, 178, 200, 255], [193, 155, 121, 255], [255, 58, 50, 255], [255, 99, 87, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", None, 13333, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", None, 13333, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint8, None, True): np.array( + [ + [[255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 255, 255]], + [[255, 255, 0, 255], [255, 0, 255, 255], [255, 255, 255, 255], [255, 0, 0, 255]], + [[255, 0, 255, 255], [0, 255, 255, 255], [0, 0, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 0, 255, 255], [0, 255, 255, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint8, None, False): np.array( + [ + [[255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 0, 255]], + [[0, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255], [0, 0, 255, 255]], + [[255, 0, 255, 255], [255, 255, 0, 255], [255, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 0, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint8, 232, True): np.array( + [ + [[255, 0, 255, 255], [255, 255, 0, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[0, 255, 255, 255], [255, 0, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint8, 232, False): np.array( + [ + [[255, 0, 255, 255], [0, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[255, 255, 0, 255], [255, 0, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint16, None, True): np.array( + [ + [[0, 0, 255, 255], [255, 0, 0, 255], [0, 0, 255, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [255, 255, 0, 255], [255, 0, 255, 255], [0, 255, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [0, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint16, None, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 255, 255], [255, 0, 0, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [0, 255, 255, 255], [255, 0, 255, 255], [0, 255, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [0, 255, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint16, 232, True): np.array( + [ + [[0, 255, 255, 255], [255, 0, 255, 255], [0, 0, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [255, 0, 255, 255], [0, 255, 0, 255]], + [[0, 255, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255], [255, 0, 255, 255]], + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint16, 232, False): np.array( + [ + [[255, 255, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [255, 0, 255, 255], [0, 255, 0, 255]], + [[0, 255, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255], [255, 0, 255, 255]], + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint16, 13333, True): np.array( + [ + [[0, 0, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255], [0, 0, 0, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 0, 255, 255], [255, 0, 255, 255]], + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGB", np.uint16, 13333, False): np.array( + [ + [[255, 0, 0, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[0, 0, 255, 255], [0, 0, 255, 255], [255, 0, 255, 255], [0, 0, 0, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [255, 0, 255, 255], [255, 0, 255, 255]], + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGB", "RGBA", None, None, True): Exception, + (np.uint8, "RGB", "RGBA", None, None, False): Exception, + (np.uint8, "RGB", "RGBA", None, 232, True): Exception, + (np.uint8, "RGB", "RGBA", None, 232, False): Exception, + (np.uint8, "RGB", "RGBA", None, 13333, True): Exception, + (np.uint8, "RGB", "RGBA", None, 13333, False): Exception, + (np.uint8, "RGB", "RGBA", np.uint8, None, True): Exception, + (np.uint8, "RGB", "RGBA", np.uint8, None, False): Exception, + (np.uint8, "RGB", "RGBA", np.uint8, 232, True): Exception, + (np.uint8, "RGB", "RGBA", np.uint8, 232, False): Exception, + (np.uint8, "RGB", "RGBA", np.uint8, 13333, True): Exception, + (np.uint8, "RGB", "RGBA", np.uint8, 13333, False): Exception, + (np.uint8, "RGB", "RGBA", np.uint16, None, True): Exception, + (np.uint8, "RGB", "RGBA", np.uint16, None, False): Exception, + (np.uint8, "RGB", "RGBA", np.uint16, 232, True): Exception, + (np.uint8, "RGB", "RGBA", np.uint16, 232, False): Exception, + (np.uint8, "RGB", "RGBA", np.uint16, 13333, True): Exception, + (np.uint8, "RGB", "RGBA", np.uint16, 13333, False): Exception, + (np.uint8, "RGBA", None, None, None, True): np.array( + [ + [[151, 252, 73, 107], [28, 221, 35, 0], [87, 122, 126, 114], [47, 59, 24, 200]], + [[189, 246, 242, 255], [123, 255, 29, 14], [201, 208, 133, 32], [118, 203, 141, 245]], + [[133, 131, 248, 81], [4, 84, 99, 40], [40, 167, 119, 150], [13, 158, 108, 21]], + [[156, 221, 166, 250], [77, 188, 13, 166], [0, 1, 185, 25], [83, 35, 103, 120]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, None, None, False): np.array( + [ + [[73, 252, 151, 107], [35, 221, 28, 0], [126, 122, 87, 114], [24, 59, 47, 200]], + [[242, 246, 189, 255], [29, 255, 123, 14], [133, 208, 201, 32], [141, 203, 118, 245]], + [[248, 131, 133, 81], [99, 84, 4, 40], [119, 167, 40, 150], [108, 158, 13, 21]], + [[166, 221, 156, 250], [13, 188, 77, 166], [185, 1, 0, 25], [103, 35, 83, 120]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, None, 232, True): np.array( + [ + [[137, 229, 66, 97], [25, 201, 31, 0], [79, 110, 114, 103], [42, 53, 21, 181]], + [[171, 223, 220, 232], [111, 232, 26, 12], [182, 189, 121, 29], [107, 184, 128, 222]], + [[121, 119, 225, 73], [3, 76, 90, 36], [36, 151, 108, 136], [11, 143, 98, 19]], + [[141, 201, 151, 227], [70, 171, 11, 151], [0, 0, 168, 22], [75, 31, 93, 109]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, None, 232, False): np.array( + [ + [[66, 229, 137, 97], [31, 201, 25, 0], [114, 110, 79, 103], [21, 53, 42, 181]], + [[220, 223, 171, 232], [26, 232, 111, 12], [121, 189, 182, 29], [128, 184, 107, 222]], + [[225, 119, 121, 73], [90, 76, 3, 36], [108, 151, 36, 136], [98, 143, 11, 19]], + [[151, 201, 141, 227], [11, 171, 70, 151], [168, 0, 0, 22], [93, 31, 75, 109]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [209, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 52, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 209, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 52, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint8, None, True): np.array( + [ + [[0, 255, 0, 0], [255, 0, 0, 255], [0, 255, 255, 255], [0, 0, 255, 255]], + [[0, 255, 255, 255], [0, 255, 0, 255], [0, 255, 0, 255], [255, 0, 0, 0]], + [[0, 0, 255, 0], [255, 255, 0, 255], [255, 0, 0, 255], [0, 255, 255, 0]], + [[255, 0, 255, 255], [0, 255, 0, 255], [255, 0, 0, 0], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint8, None, False): np.array( + [ + [[0, 255, 0, 0], [0, 0, 255, 255], [255, 255, 0, 255], [255, 0, 0, 255]], + [[255, 255, 0, 255], [0, 255, 0, 255], [0, 255, 0, 255], [0, 0, 255, 0]], + [[255, 0, 0, 0], [0, 255, 255, 255], [0, 0, 255, 255], [255, 255, 0, 0]], + [[255, 0, 255, 255], [0, 255, 0, 255], [0, 0, 255, 0], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint8, 232, True): np.array( + [ + [[0, 0, 255, 0], [0, 0, 0, 255], [0, 255, 255, 0], [255, 0, 0, 0]], + [[0, 0, 255, 255], [0, 255, 255, 255], [255, 0, 0, 0], [0, 255, 255, 255]], + [[0, 0, 0, 0], [0, 255, 255, 255], [255, 0, 255, 255], [0, 0, 255, 0]], + [[0, 0, 0, 0], [255, 0, 0, 0], [255, 255, 255, 255], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint8, 232, False): np.array( + [ + [[255, 0, 0, 0], [0, 0, 0, 255], [255, 255, 0, 0], [0, 0, 255, 0]], + [[255, 0, 0, 255], [255, 255, 0, 255], [0, 0, 255, 0], [255, 255, 0, 255]], + [[0, 0, 0, 0], [255, 255, 0, 255], [255, 0, 255, 255], [255, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 255, 0], [255, 255, 255, 255], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint16, None, True): np.array( + [ + [[0, 255, 0, 0], [0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 255, 0]], + [[255, 255, 0, 0], [255, 0, 0, 0], [255, 0, 0, 0], [0, 0, 255, 0]], + [[0, 0, 0, 255], [0, 255, 255, 0], [0, 0, 0, 255], [0, 0, 255, 255]], + [[255, 0, 0, 0], [0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint16, None, False): np.array( + [ + [[0, 255, 0, 0], [0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 0, 0]], + [[0, 255, 255, 0], [0, 0, 255, 0], [0, 0, 255, 0], [255, 0, 0, 0]], + [[0, 0, 0, 255], [255, 255, 0, 0], [0, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint16, 232, True): np.array( + [ + [[0, 0, 255, 0], [0, 255, 0, 255], [0, 0, 255, 0], [255, 0, 255, 0]], + [[255, 0, 0, 0], [255, 0, 0, 255], [0, 255, 0, 0], [0, 0, 0, 255]], + [[0, 0, 255, 0], [255, 0, 255, 255], [255, 0, 255, 0], [0, 0, 0, 0]], + [[255, 255, 0, 0], [0, 255, 0, 0], [255, 255, 255, 0], [255, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint16, 232, False): np.array( + [ + [[255, 0, 0, 0], [0, 255, 0, 255], [255, 0, 0, 0], [255, 0, 255, 0]], + [[0, 0, 255, 0], [0, 0, 255, 255], [0, 255, 0, 0], [0, 0, 0, 255]], + [[255, 0, 0, 0], [255, 0, 255, 255], [255, 0, 255, 0], [0, 0, 0, 0]], + [[0, 255, 255, 0], [0, 255, 0, 0], [255, 255, 255, 0], [255, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint16, 13333, True): np.array( + [ + [[0, 255, 255, 0], [255, 0, 255, 255], [255, 255, 255, 0], [255, 255, 255, 0]], + [[255, 0, 0, 0], [0, 0, 0, 255], [255, 255, 255, 0], [0, 255, 0, 255]], + [[255, 255, 255, 0], [0, 255, 0, 255], [255, 0, 255, 255], [0, 0, 255, 255]], + [[0, 0, 255, 255], [255, 0, 0, 255], [255, 0, 255, 0], [0, 255, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", None, np.uint16, 13333, False): np.array( + [ + [[255, 255, 0, 0], [255, 0, 255, 255], [255, 255, 255, 0], [255, 255, 255, 0]], + [[0, 0, 255, 0], [0, 0, 0, 255], [255, 255, 255, 0], [0, 255, 0, 255]], + [[255, 255, 255, 0], [0, 255, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255]], + [[255, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 0], [255, 255, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 232, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 232, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 255, 0, 0], [0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 255, 0]], + [[255, 255, 0, 255], [255, 255, 0, 0], [255, 0, 0, 0], [0, 0, 255, 0]], + [[0, 0, 0, 255], [0, 255, 255, 0], [0, 0, 0, 255], [0, 0, 255, 255]], + [[255, 0, 0, 0], [0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 255, 0, 0], [0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 0, 0]], + [[0, 255, 255, 255], [0, 255, 255, 0], [0, 0, 255, 0], [255, 0, 0, 0]], + [[0, 0, 0, 255], [255, 255, 0, 0], [0, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGB", None, None, True): Exception, + (np.uint8, "RGBA", "RGB", None, None, False): Exception, + (np.uint8, "RGBA", "RGB", None, 232, True): Exception, + (np.uint8, "RGBA", "RGB", None, 232, False): Exception, + (np.uint8, "RGBA", "RGB", None, 13333, True): Exception, + (np.uint8, "RGBA", "RGB", None, 13333, False): Exception, + (np.uint8, "RGBA", "RGB", np.uint8, None, True): Exception, + (np.uint8, "RGBA", "RGB", np.uint8, None, False): Exception, + (np.uint8, "RGBA", "RGB", np.uint8, 232, True): Exception, + (np.uint8, "RGBA", "RGB", np.uint8, 232, False): Exception, + (np.uint8, "RGBA", "RGB", np.uint8, 13333, True): Exception, + (np.uint8, "RGBA", "RGB", np.uint8, 13333, False): Exception, + (np.uint8, "RGBA", "RGB", np.uint16, None, True): Exception, + (np.uint8, "RGBA", "RGB", np.uint16, None, False): Exception, + (np.uint8, "RGBA", "RGB", np.uint16, 232, True): Exception, + (np.uint8, "RGBA", "RGB", np.uint16, 232, False): Exception, + (np.uint8, "RGBA", "RGB", np.uint16, 13333, True): Exception, + (np.uint8, "RGBA", "RGB", np.uint16, 13333, False): Exception, + (np.uint8, "RGBA", "RGBA", None, None, True): np.array( + [ + [[0, 3, 73, 0], [0, 3, 35, 0], [0, 0, 126, 0], [0, 0, 24, 145]], + [[0, 3, 242, 255], [0, 3, 29, 0], [0, 2, 133, 0], [0, 2, 141, 235]], + [[0, 0, 248, 0], [0, 0, 99, 0], [0, 1, 119, 45], [0, 1, 108, 0]], + [[0, 3, 166, 245], [0, 2, 13, 77], [0, 0, 185, 0], [0, 0, 103, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", None, None, False): np.array( + [ + [[73, 3, 0, 0], [35, 3, 0, 0], [126, 0, 0, 0], [24, 0, 0, 145]], + [[242, 3, 0, 255], [29, 3, 0, 0], [133, 2, 0, 0], [141, 2, 0, 235]], + [[248, 0, 0, 0], [99, 0, 0, 0], [119, 1, 0, 45], [108, 1, 0, 0]], + [[166, 3, 0, 245], [13, 2, 0, 77], [185, 0, 0, 0], [103, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", None, 232, True): np.array( + [ + [[0, 3, 66, 0], [0, 2, 31, 0], [0, 0, 114, 0], [0, 0, 21, 132]], + [[0, 3, 220, 232], [0, 3, 26, 0], [0, 2, 121, 0], [0, 2, 128, 213]], + [[0, 0, 225, 0], [0, 0, 90, 0], [0, 1, 108, 41], [0, 1, 98, 0]], + [[0, 2, 151, 222], [0, 2, 11, 70], [0, 0, 168, 0], [0, 0, 93, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", None, 232, False): np.array( + [ + [[66, 3, 0, 0], [31, 2, 0, 0], [114, 0, 0, 0], [21, 0, 0, 132]], + [[220, 3, 0, 232], [26, 3, 0, 0], [121, 2, 0, 0], [128, 2, 0, 213]], + [[225, 0, 0, 0], [90, 0, 0, 0], [108, 1, 0, 41], [98, 1, 0, 0]], + [[151, 2, 0, 222], [11, 2, 0, 70], [168, 0, 0, 0], [93, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", None, 13333, True): np.array( + [ + [[0, 204, 255, 0], [0, 162, 255, 0], [0, 29, 255, 0], [0, 0, 255, 255]], + [[0, 196, 255, 255], [0, 208, 255, 0], [0, 145, 255, 0], [0, 138, 255, 255]], + [[0, 41, 255, 0], [0, 0, 255, 0], [0, 90, 255, 255], [0, 78, 255, 0]], + [[0, 162, 255, 255], [0, 118, 255, 255], [0, 0, 255, 0], [0, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", None, 13333, False): np.array( + [ + [[255, 204, 0, 0], [255, 162, 0, 0], [255, 29, 0, 0], [255, 0, 0, 255]], + [[255, 196, 0, 255], [255, 208, 0, 0], [255, 145, 0, 0], [255, 138, 0, 255]], + [[255, 41, 0, 0], [255, 0, 0, 0], [255, 90, 0, 255], [255, 78, 0, 0]], + [[255, 162, 0, 255], [255, 118, 0, 255], [255, 0, 0, 0], [255, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint8, None, True): np.array( + [ + [[255, 0, 0, 255], [255, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0]], + [[255, 0, 255, 255], [255, 0, 0, 255], [255, 255, 0, 255], [255, 255, 0, 0]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 0, 0, 0], [255, 0, 255, 255]], + [[255, 0, 255, 0], [255, 255, 0, 0], [255, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint8, None, False): np.array( + [ + [[0, 0, 255, 255], [0, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0]], + [[255, 0, 255, 255], [0, 0, 255, 255], [0, 255, 255, 255], [0, 255, 255, 0]], + [[255, 255, 255, 255], [0, 255, 255, 255], [0, 0, 255, 0], [255, 0, 255, 255]], + [[255, 0, 255, 0], [0, 255, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint8, 232, True): np.array( + [ + [[255, 0, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[255, 0, 255, 255], [255, 0, 255, 255], [255, 255, 0, 255], [255, 255, 255, 0]], + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 0, 255, 0], [255, 0, 255, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint8, 232, False): np.array( + [ + [[255, 0, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[255, 0, 255, 255], [255, 0, 255, 255], [0, 255, 255, 255], [255, 255, 255, 0]], + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 0], [255, 0, 255, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint16, None, True): np.array( + [ + [[255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255], [255, 255, 255, 0]], + [[255, 255, 0, 0], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 255, 0]], + [[255, 0, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255], [255, 0, 255, 255]], + [[255, 0, 0, 0], [255, 255, 0, 0], [255, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint16, None, False): np.array( + [ + [[0, 0, 255, 255], [0, 0, 255, 255], [255, 0, 255, 255], [255, 255, 255, 0]], + [[0, 255, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 0]], + [[0, 0, 255, 255], [255, 255, 255, 255], [0, 0, 255, 255], [255, 0, 255, 255]], + [[0, 0, 255, 0], [0, 255, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint16, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 0, 0], [255, 255, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 0], [255, 0, 0, 255]], + [[255, 0, 0, 255], [255, 0, 0, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint16, 232, False): np.array( + [ + [[255, 255, 255, 255], [0, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 255, 255, 0], [0, 255, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 0], [0, 0, 255, 255]], + [[0, 0, 255, 255], [0, 0, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint16, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 0]], + [[255, 0, 0, 0], [255, 0, 0, 255], [255, 0, 255, 255], [255, 255, 0, 255]], + [[255, 0, 255, 255], [255, 255, 0, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint8, "RGBA", "RGBA", np.uint16, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 255, 0]], + [[0, 0, 255, 0], [0, 0, 255, 255], [255, 0, 255, 255], [0, 255, 255, 255]], + [[255, 0, 255, 255], [0, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 0], [0, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, None, None, True): np.array( + [ + [[52, 52, 52, 255], [214, 214, 214, 255], [158, 158, 158, 255], [159, 159, 159, 255]], + [[224, 224, 224, 255], [133, 133, 133, 255], [132, 132, 132, 255], [0, 0, 0, 255]], + [[39, 39, 39, 255], [221, 221, 221, 255], [162, 162, 162, 255], [83, 83, 83, 255]], + [[57, 57, 57, 255], [255, 255, 255, 255], [187, 187, 187, 255], [44, 44, 44, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, None, None, False): np.array( + [ + [[52, 52, 52, 255], [214, 214, 214, 255], [158, 158, 158, 255], [159, 159, 159, 255]], + [[224, 224, 224, 255], [133, 133, 133, 255], [132, 132, 132, 255], [0, 0, 0, 255]], + [[39, 39, 39, 255], [221, 221, 221, 255], [162, 162, 162, 255], [83, 83, 83, 255]], + [[57, 57, 57, 255], [255, 255, 255, 255], [187, 187, 187, 255], [44, 44, 44, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, None, 232, True): np.array( + [ + [[47, 47, 47, 255], [194, 194, 194, 255], [144, 144, 144, 255], [144, 144, 144, 255]], + [[203, 203, 203, 255], [121, 121, 121, 255], [120, 120, 120, 255], [0, 0, 0, 255]], + [[35, 35, 35, 255], [201, 201, 201, 255], [148, 148, 148, 255], [76, 76, 76, 255]], + [[52, 52, 52, 255], [232, 232, 232, 255], [170, 170, 170, 255], [40, 40, 40, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, None, 232, False): np.array( + [ + [[47, 47, 47, 255], [194, 194, 194, 255], [144, 144, 144, 255], [144, 144, 144, 255]], + [[203, 203, 203, 255], [121, 121, 121, 255], [120, 120, 120, 255], [0, 0, 0, 255]], + [[35, 35, 35, 255], [201, 201, 201, 255], [148, 148, 148, 255], [76, 76, 76, 255]], + [[52, 52, 52, 255], [232, 232, 232, 255], [170, 170, 170, 255], [40, 40, 40, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint8, 232, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint8, 232, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint16, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", None, np.uint16, 13333, False): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGB", None, None, True): Exception, + (np.uint16, "2D", "RGB", None, None, False): Exception, + (np.uint16, "2D", "RGB", None, 232, True): Exception, + (np.uint16, "2D", "RGB", None, 232, False): Exception, + (np.uint16, "2D", "RGB", None, 13333, True): Exception, + (np.uint16, "2D", "RGB", None, 13333, False): Exception, + (np.uint16, "2D", "RGB", np.uint8, None, True): Exception, + (np.uint16, "2D", "RGB", np.uint8, None, False): Exception, + (np.uint16, "2D", "RGB", np.uint8, 232, True): Exception, + (np.uint16, "2D", "RGB", np.uint8, 232, False): Exception, + (np.uint16, "2D", "RGB", np.uint8, 13333, True): Exception, + (np.uint16, "2D", "RGB", np.uint8, 13333, False): Exception, + (np.uint16, "2D", "RGB", np.uint16, None, True): Exception, + (np.uint16, "2D", "RGB", np.uint16, None, False): Exception, + (np.uint16, "2D", "RGB", np.uint16, 232, True): Exception, + (np.uint16, "2D", "RGB", np.uint16, 232, False): Exception, + (np.uint16, "2D", "RGB", np.uint16, 13333, True): Exception, + (np.uint16, "2D", "RGB", np.uint16, 13333, False): Exception, + (np.uint16, "2D", "RGBA", None, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[231, 231, 231, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[231, 231, 231, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[210, 210, 210, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[210, 210, 210, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "2D", "RGBA", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, None, None, True): np.array( + [ + [[216, 176, 0, 255], [75, 152, 141, 255], [129, 33, 221, 255], [146, 121, 149, 255]], + [[229, 63, 143, 255], [38, 93, 48, 255], [64, 185, 70, 255], [66, 152, 233, 255]], + [[170, 192, 38, 255], [139, 15, 154, 255], [208, 226, 113, 255], [208, 15, 137, 255]], + [[62, 84, 176, 255], [255, 183, 202, 255], [24, 134, 77, 255], [168, 145, 136, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, None, None, False): np.array( + [ + [[0, 176, 216, 255], [141, 152, 75, 255], [221, 33, 129, 255], [149, 121, 146, 255]], + [[143, 63, 229, 255], [48, 93, 38, 255], [70, 185, 64, 255], [233, 152, 66, 255]], + [[38, 192, 170, 255], [154, 15, 139, 255], [113, 226, 208, 255], [137, 15, 208, 255]], + [[176, 84, 62, 255], [202, 183, 255, 255], [77, 134, 24, 255], [136, 145, 168, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, None, 232, True): np.array( + [ + [[196, 160, 0, 255], [68, 138, 128, 255], [117, 30, 201, 255], [133, 110, 135, 255]], + [[208, 58, 130, 255], [35, 84, 43, 255], [58, 168, 64, 255], [60, 139, 212, 255]], + [[154, 175, 34, 255], [127, 13, 140, 255], [189, 205, 103, 255], [189, 14, 124, 255]], + [[56, 76, 160, 255], [232, 166, 184, 255], [22, 122, 70, 255], [153, 132, 124, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, None, 232, False): np.array( + [ + [[0, 160, 196, 255], [128, 138, 68, 255], [201, 30, 117, 255], [135, 110, 133, 255]], + [[130, 58, 208, 255], [43, 84, 35, 255], [64, 168, 58, 255], [212, 139, 60, 255]], + [[34, 175, 154, 255], [140, 13, 127, 255], [103, 205, 189, 255], [124, 14, 189, 255]], + [[160, 76, 56, 255], [184, 166, 232, 255], [70, 122, 22, 255], [124, 132, 153, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, None, 13333, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, None, 13333, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 255, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [0, 0, 255, 255], [255, 255, 0, 255], [255, 0, 0, 255]], + [[255, 255, 255, 255], [255, 0, 255, 255], [255, 255, 0, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 0, 0, 255], [0, 255, 255, 255], [0, 0, 255, 255]], + [[255, 255, 255, 255], [255, 0, 255, 255], [0, 255, 255, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 0, 255], [0, 255, 0, 255]], + [[255, 255, 255, 255], [0, 255, 0, 255], [255, 255, 255, 255], [255, 0, 255, 255]], + [[255, 0, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 0, 255], [0, 255, 0, 255]], + [[255, 255, 255, 255], [0, 255, 0, 255], [255, 255, 255, 255], [255, 0, 255, 255]], + [[255, 0, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint16, None, True): np.array( + [ + [[255, 0, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255]], + [[0, 0, 255, 255], [255, 255, 255, 255], [0, 0, 255, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint16, None, False): np.array( + [ + [[255, 0, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [255, 0, 255, 255], [0, 0, 255, 255]], + [[255, 0, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint16, 232, True): np.array( + [ + [[0, 0, 255, 255], [0, 255, 0, 255], [255, 255, 255, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [0, 255, 0, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint16, 232, False): np.array( + [ + [[255, 0, 0, 255], [0, 255, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [0, 255, 0, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint16, 13333, True): np.array( + [ + [[0, 0, 255, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255]], + [[0, 255, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255], [0, 0, 0, 255]], + [[255, 255, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", None, np.uint16, 13333, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 255, 255], [0, 0, 255, 255], [255, 0, 255, 255]], + [[0, 255, 0, 255], [255, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255], [0, 0, 0, 255]], + [[0, 255, 255, 255], [0, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", None, None, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", None, 232, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", None, 13333, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint16, None, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", None, None, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", None, None, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", None, 232, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", None, 232, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", None, 13333, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", None, 13333, False): np.array( + [ + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint16, None, True): np.array( + [ + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint16, None, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint16, 232, True): np.array( + [ + [[0, 255, 255, 255], [255, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 0, 255], [0, 0, 0, 255], [255, 255, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint16, 232, False): np.array( + [ + [[255, 255, 0, 255], [0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 255, 255, 255], [0, 0, 0, 255], [0, 255, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint16, 13333, True): np.array( + [ + [[0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGB", np.uint16, 13333, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGB", "RGBA", None, None, True): Exception, + (np.uint16, "RGB", "RGBA", None, None, False): Exception, + (np.uint16, "RGB", "RGBA", None, 232, True): Exception, + (np.uint16, "RGB", "RGBA", None, 232, False): Exception, + (np.uint16, "RGB", "RGBA", None, 13333, True): Exception, + (np.uint16, "RGB", "RGBA", None, 13333, False): Exception, + (np.uint16, "RGB", "RGBA", np.uint8, None, True): Exception, + (np.uint16, "RGB", "RGBA", np.uint8, None, False): Exception, + (np.uint16, "RGB", "RGBA", np.uint8, 232, True): Exception, + (np.uint16, "RGB", "RGBA", np.uint8, 232, False): Exception, + (np.uint16, "RGB", "RGBA", np.uint8, 13333, True): Exception, + (np.uint16, "RGB", "RGBA", np.uint8, 13333, False): Exception, + (np.uint16, "RGB", "RGBA", np.uint16, None, True): Exception, + (np.uint16, "RGB", "RGBA", np.uint16, None, False): Exception, + (np.uint16, "RGB", "RGBA", np.uint16, 232, True): Exception, + (np.uint16, "RGB", "RGBA", np.uint16, 232, False): Exception, + (np.uint16, "RGB", "RGBA", np.uint16, 13333, True): Exception, + (np.uint16, "RGB", "RGBA", np.uint16, 13333, False): Exception, + (np.uint16, "RGBA", None, None, None, True): np.array( + [ + [[50, 158, 115, 181], [0, 142, 130, 220], [188, 89, 183, 104], [223, 47, 120, 45]], + [[18, 160, 124, 98], [108, 253, 233, 185], [34, 71, 19, 189], [255, 100, 106, 67]], + [[202, 255, 158, 255], [0, 224, 107, 164], [195, 136, 74, 247], [68, 21, 140, 187]], + [[239, 107, 145, 28], [212, 155, 227, 49], [81, 220, 126, 0], [52, 57, 229, 189]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, None, None, False): np.array( + [ + [[115, 158, 50, 181], [130, 142, 0, 220], [183, 89, 188, 104], [120, 47, 223, 45]], + [[124, 160, 18, 98], [233, 253, 108, 185], [19, 71, 34, 189], [106, 100, 255, 67]], + [[158, 255, 202, 255], [107, 224, 0, 164], [74, 136, 195, 247], [140, 21, 68, 187]], + [[145, 107, 239, 28], [227, 155, 212, 49], [126, 220, 81, 0], [229, 57, 52, 189]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, None, 232, True): np.array( + [ + [[46, 144, 104, 165], [0, 129, 118, 200], [171, 81, 167, 94], [202, 43, 109, 41]], + [[17, 145, 113, 89], [98, 230, 212, 169], [31, 64, 17, 172], [232, 91, 96, 61]], + [[184, 232, 144, 232], [0, 204, 98, 149], [177, 124, 68, 225], [62, 19, 128, 170]], + [[218, 98, 132, 25], [193, 141, 206, 45], [73, 200, 114, 0], [47, 51, 208, 172]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, None, 232, False): np.array( + [ + [[104, 144, 46, 165], [118, 129, 0, 200], [167, 81, 171, 94], [109, 43, 202, 41]], + [[113, 145, 17, 89], [212, 230, 98, 169], [17, 64, 31, 172], [96, 91, 232, 61]], + [[144, 232, 184, 232], [98, 204, 0, 149], [68, 124, 177, 225], [128, 19, 62, 170]], + [[132, 98, 218, 25], [206, 141, 193, 45], [114, 200, 73, 0], [208, 51, 47, 172]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [6, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 17], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 6, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 17], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint8, None, True): np.array( + [ + [[255, 255, 0, 0], [255, 255, 255, 255], [255, 0, 0, 255], [0, 0, 255, 0]], + [[255, 255, 255, 255], [255, 0, 0, 0], [255, 0, 0, 0], [255, 255, 255, 0]], + [[255, 255, 255, 255], [255, 255, 0, 255], [0, 255, 255, 0], [255, 0, 255, 0]], + [[0, 0, 0, 255], [255, 0, 0, 0], [0, 255, 255, 255], [255, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint8, None, False): np.array( + [ + [[0, 255, 255, 0], [255, 255, 255, 255], [0, 0, 255, 255], [255, 0, 0, 0]], + [[255, 255, 255, 255], [0, 0, 255, 0], [0, 0, 255, 0], [255, 255, 255, 0]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 0, 0], [255, 0, 255, 0]], + [[0, 0, 0, 255], [0, 0, 255, 0], [255, 255, 0, 255], [0, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 0], [255, 0, 255, 255], [0, 0, 0, 255], [255, 0, 0, 0]], + [[0, 0, 0, 0], [255, 255, 255, 0], [0, 255, 0, 255], [255, 0, 255, 0]], + [[255, 255, 255, 255], [255, 255, 255, 0], [0, 255, 255, 0], [255, 0, 255, 255]], + [[255, 255, 255, 0], [0, 0, 255, 0], [0, 255, 255, 255], [0, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 0], [255, 0, 255, 255], [0, 0, 0, 255], [0, 0, 255, 0]], + [[0, 0, 0, 0], [255, 255, 255, 0], [0, 255, 0, 255], [255, 0, 255, 0]], + [[255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 0, 0], [255, 0, 255, 255]], + [[255, 255, 255, 0], [255, 0, 0, 0], [255, 255, 0, 255], [255, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 0, 0], [255, 0, 255, 255]], + [[255, 255, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 255, 0, 0]], + [[255, 0, 255, 0], [255, 255, 255, 0], [0, 255, 0, 0], [0, 0, 255, 255]], + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 255, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 0, 0], [255, 0, 255, 255]], + [[0, 255, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 255, 0, 0]], + [[255, 0, 255, 0], [255, 255, 255, 0], [0, 255, 0, 0], [255, 0, 0, 255]], + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 255, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint16, 232, True): np.array( + [ + [[0, 255, 0, 255], [255, 255, 0, 0], [255, 255, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 255, 0]], + [[0, 0, 255, 0], [255, 255, 0, 0], [255, 0, 0, 255], [0, 0, 0, 0]], + [[0, 0, 255, 0], [0, 255, 0, 255], [0, 0, 255, 255], [0, 255, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint16, 232, False): np.array( + [ + [[0, 255, 0, 255], [0, 255, 255, 0], [0, 255, 255, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [255, 0, 0, 0]], + [[255, 0, 0, 0], [0, 255, 255, 0], [0, 0, 255, 255], [0, 0, 0, 0]], + [[255, 0, 0, 0], [0, 255, 0, 255], [255, 0, 0, 255], [0, 255, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint16, 13333, True): np.array( + [ + [[0, 255, 0, 255], [255, 0, 0, 0], [0, 255, 0, 0], [255, 0, 255, 0]], + [[0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 255, 0], [255, 255, 0, 0], [0, 0, 0, 255], [0, 0, 255, 0]], + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", None, np.uint16, 13333, False): np.array( + [ + [[0, 255, 0, 255], [0, 0, 255, 0], [0, 255, 0, 0], [255, 0, 255, 0]], + [[255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255]], + [[255, 0, 0, 0], [0, 255, 255, 0], [0, 0, 0, 255], [255, 0, 0, 0]], + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGB", None, None, True): Exception, + (np.uint16, "RGBA", "RGB", None, None, False): Exception, + (np.uint16, "RGBA", "RGB", None, 232, True): Exception, + (np.uint16, "RGBA", "RGB", None, 232, False): Exception, + (np.uint16, "RGBA", "RGB", None, 13333, True): Exception, + (np.uint16, "RGBA", "RGB", None, 13333, False): Exception, + (np.uint16, "RGBA", "RGB", np.uint8, None, True): Exception, + (np.uint16, "RGBA", "RGB", np.uint8, None, False): Exception, + (np.uint16, "RGBA", "RGB", np.uint8, 232, True): Exception, + (np.uint16, "RGBA", "RGB", np.uint8, 232, False): Exception, + (np.uint16, "RGBA", "RGB", np.uint8, 13333, True): Exception, + (np.uint16, "RGBA", "RGB", np.uint8, 13333, False): Exception, + (np.uint16, "RGBA", "RGB", np.uint16, None, True): Exception, + (np.uint16, "RGBA", "RGB", np.uint16, None, False): Exception, + (np.uint16, "RGBA", "RGB", np.uint16, 232, True): Exception, + (np.uint16, "RGBA", "RGB", np.uint16, 232, False): Exception, + (np.uint16, "RGBA", "RGB", np.uint16, 13333, True): Exception, + (np.uint16, "RGBA", "RGB", np.uint16, 13333, False): Exception, + (np.uint16, "RGBA", "RGBA", None, None, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[108, 255, 255, 255], [255, 255, 255, 255], [203, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 139, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", None, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 108, 255], [255, 255, 255, 255], [255, 255, 203, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 139, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[98, 255, 255, 255], [255, 255, 255, 255], [184, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 126, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 98, 255], [255, 255, 255, 255], [255, 255, 184, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 126, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint16, 232, True): np.array( + [ + [[255, 255, 255, 0], [255, 0, 255, 0], [0, 0, 255, 0], [0, 0, 255, 0]], + [[0, 0, 255, 0], [255, 0, 255, 0], [0, 255, 0, 0], [255, 0, 255, 0]], + [[0, 255, 0, 0], [255, 0, 0, 0], [0, 255, 0, 0], [255, 255, 255, 0]], + [[0, 0, 255, 0], [0, 0, 255, 255], [0, 255, 255, 255], [255, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint16, 232, False): np.array( + [ + [[255, 255, 255, 0], [255, 0, 255, 0], [255, 0, 0, 0], [255, 0, 0, 0]], + [[255, 0, 0, 0], [255, 0, 255, 0], [0, 255, 0, 0], [255, 0, 255, 0]], + [[0, 255, 0, 0], [0, 0, 255, 0], [0, 255, 0, 0], [255, 255, 255, 0]], + [[255, 0, 0, 0], [255, 0, 0, 255], [255, 255, 0, 255], [255, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint16, 13333, True): np.array( + [ + [[255, 255, 0, 0], [255, 0, 0, 0], [0, 255, 0, 0], [0, 255, 0, 0]], + [[0, 255, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [255, 0, 0, 0], [255, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.uint16, "RGBA", "RGBA", np.uint16, 13333, False): np.array( + [ + [[0, 255, 255, 0], [0, 0, 255, 0], [0, 255, 0, 0], [0, 255, 0, 0]], + [[0, 255, 0, 0], [0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 255, 0], [0, 0, 255, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", None, None, True): np.array( + [ + [[255, 255, 255, 255], [136, 136, 136, 255], [0, 0, 0, 0], [225, 225, 225, 255]], + [[216, 216, 216, 255], [224, 224, 224, 255], [255, 255, 255, 255], [16, 16, 16, 255]], + [[252, 252, 252, 255], [216, 216, 216, 255], [42, 42, 42, 255], [252, 252, 252, 255]], + [[18, 18, 18, 255], [169, 169, 169, 255], [223, 223, 223, 255], [252, 252, 252, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", None, None, False): np.array( + [ + [[255, 255, 255, 255], [136, 136, 136, 255], [0, 0, 0, 0], [225, 225, 225, 255]], + [[216, 216, 216, 255], [224, 224, 224, 255], [255, 255, 255, 255], [16, 16, 16, 255]], + [[252, 252, 252, 255], [216, 216, 216, 255], [42, 42, 42, 255], [252, 252, 252, 255]], + [[18, 18, 18, 255], [169, 169, 169, 255], [223, 223, 223, 255], [252, 252, 252, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [124, 124, 124, 255], [0, 0, 0, 0], [205, 205, 205, 255]], + [[197, 197, 197, 255], [204, 204, 204, 255], [232, 232, 232, 255], [15, 15, 15, 255]], + [[230, 230, 230, 255], [196, 196, 196, 255], [38, 38, 38, 255], [229, 229, 229, 255]], + [[16, 16, 16, 255], [153, 153, 153, 255], [203, 203, 203, 255], [229, 229, 229, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [124, 124, 124, 255], [0, 0, 0, 0], [205, 205, 205, 255]], + [[197, 197, 197, 255], [204, 204, 204, 255], [232, 232, 232, 255], [15, 15, 15, 255]], + [[230, 230, 230, 255], [196, 196, 196, 255], [38, 38, 38, 255], [229, 229, 229, 255]], + [[16, 16, 16, 255], [153, 153, 153, 255], [203, 203, 203, 255], [229, 229, 229, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 255, 255, 0], [0, 0, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGB", None, None, True): Exception, + (np.float32, "2D", "RGB", None, None, False): Exception, + (np.float32, "2D", "RGB", None, 232, True): Exception, + (np.float32, "2D", "RGB", None, 232, False): Exception, + (np.float32, "2D", "RGB", None, 13333, True): Exception, + (np.float32, "2D", "RGB", None, 13333, False): Exception, + (np.float32, "2D", "RGB", np.uint8, None, True): Exception, + (np.float32, "2D", "RGB", np.uint8, None, False): Exception, + (np.float32, "2D", "RGB", np.uint8, 232, True): Exception, + (np.float32, "2D", "RGB", np.uint8, 232, False): Exception, + (np.float32, "2D", "RGB", np.uint8, 13333, True): Exception, + (np.float32, "2D", "RGB", np.uint8, 13333, False): Exception, + (np.float32, "2D", "RGB", np.uint16, None, True): Exception, + (np.float32, "2D", "RGB", np.uint16, None, False): Exception, + (np.float32, "2D", "RGB", np.uint16, 232, True): Exception, + (np.float32, "2D", "RGB", np.uint16, 232, False): Exception, + (np.float32, "2D", "RGB", np.uint16, 13333, True): Exception, + (np.float32, "2D", "RGB", np.uint16, 13333, False): Exception, + (np.float32, "2D", "RGBA", None, None, True): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [1, 1, 1, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", None, None, False): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [1, 1, 1, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", None, 232, True): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", None, 232, False): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [52, 52, 52, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [8, 8, 8, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [45, 45, 45, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [52, 52, 52, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [8, 8, 8, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [45, 45, 45, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint16, None, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint16, None, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint16, 232, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint16, 232, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint16, 13333, True): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "2D", "RGBA", np.uint16, 13333, False): np.array( + [ + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", None, None, True): np.array( + [ + [[59, 99, 32, 255], [103, 231, 72, 255], [233, 117, 43, 255], [74, 249, 184, 255]], + [[0, 185, 137, 0], [232, 72, 187, 255], [86, 56, 249, 255], [35, 88, 101, 255]], + [[217, 49, 0, 255], [123, 12, 152, 255], [255, 83, 37, 255], [95, 90, 22, 255]], + [[177, 133, 125, 255], [238, 36, 174, 255], [70, 177, 17, 255], [67, 65, 219, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", None, None, False): np.array( + [ + [[32, 99, 59, 255], [72, 231, 103, 255], [43, 117, 233, 255], [184, 249, 74, 255]], + [[137, 185, 0, 0], [187, 72, 232, 255], [249, 56, 86, 255], [101, 88, 35, 255]], + [[0, 49, 217, 255], [152, 12, 123, 255], [37, 83, 255, 255], [22, 90, 95, 255]], + [[125, 133, 177, 255], [174, 36, 238, 255], [17, 177, 70, 255], [219, 65, 67, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", None, 232, True): np.array( + [ + [[54, 90, 29, 255], [94, 210, 65, 255], [212, 106, 39, 255], [68, 227, 167, 255]], + [[0, 169, 125, 0], [211, 65, 170, 255], [78, 51, 227, 255], [32, 80, 91, 255]], + [[197, 44, 0, 255], [112, 11, 138, 255], [255, 75, 34, 255], [86, 82, 20, 255]], + [[161, 121, 113, 255], [216, 33, 158, 255], [64, 161, 15, 255], [61, 59, 199, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", None, 232, False): np.array( + [ + [[29, 90, 54, 255], [65, 210, 94, 255], [39, 106, 212, 255], [167, 227, 68, 255]], + [[125, 169, 0, 0], [170, 65, 211, 255], [227, 51, 78, 255], [91, 80, 32, 255]], + [[0, 44, 197, 255], [138, 11, 112, 255], [34, 75, 255, 255], [20, 82, 86, 255]], + [[113, 121, 161, 255], [158, 33, 216, 255], [15, 161, 64, 255], [199, 59, 61, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 0, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint8, None, True): np.array( + [ + [[0, 0, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[255, 0, 0, 0], [255, 255, 0, 255], [255, 255, 0, 255], [0, 255, 0, 255]], + [[0, 0, 255, 255], [0, 255, 255, 255], [255, 0, 0, 255], [0, 255, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint8, None, False): np.array( + [ + [[255, 0, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[0, 0, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 0, 255]], + [[255, 0, 0, 255], [255, 255, 0, 255], [0, 0, 255, 255], [255, 255, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 0, 0, 255]], + [[255, 0, 0, 0], [0, 0, 255, 255], [255, 0, 0, 255], [255, 255, 0, 255]], + [[0, 255, 255, 255], [255, 0, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 0, 255, 255]], + [[0, 0, 255, 0], [255, 0, 0, 255], [0, 0, 255, 255], [0, 255, 255, 255]], + [[255, 255, 0, 255], [255, 0, 255, 255], [255, 0, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 255, 255], [255, 0, 0, 255], [255, 0, 255, 255], [0, 0, 0, 255]], + [[255, 0, 255, 0], [0, 0, 255, 255], [255, 255, 0, 255], [0, 0, 255, 255]], + [[255, 0, 255, 255], [255, 0, 0, 255], [0, 0, 255, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [0, 0, 255, 255], [0, 255, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint16, None, False): np.array( + [ + [[255, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255], [0, 0, 0, 255]], + [[255, 0, 255, 0], [255, 0, 0, 255], [0, 255, 255, 255], [255, 0, 0, 255]], + [[255, 0, 255, 255], [0, 0, 255, 255], [255, 0, 0, 255], [255, 255, 255, 255]], + [[0, 0, 0, 255], [255, 0, 0, 255], [0, 255, 0, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[255, 255, 0, 255], [0, 255, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[255, 0, 0, 0], [0, 0, 0, 255], [255, 255, 0, 255], [0, 0, 0, 255]], + [[0, 0, 255, 255], [0, 0, 255, 255], [0, 255, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 255, 255, 255], [0, 255, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 255, 0], [0, 0, 0, 255], [0, 255, 255, 255], [0, 0, 0, 255]], + [[255, 0, 0, 255], [255, 0, 0, 255], [0, 255, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 255, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 0, 0, 255]], + [[255, 255, 0, 0], [0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 255, 255]], + [[0, 255, 255, 255], [255, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 0, 255, 255], [0, 0, 0, 255], [255, 0, 255, 255], [255, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 0, 255, 255]], + [[0, 255, 255, 0], [0, 0, 0, 255], [0, 0, 255, 255], [255, 0, 255, 255]], + [[255, 255, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[255, 0, 255, 255], [0, 0, 0, 255], [255, 0, 255, 255], [0, 0, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", None, None, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", None, None, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", None, 232, True): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [255, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", None, 232, False): np.array( + [ + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 255, 255], [0, 0, 0, 255]], + [[0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255], [0, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", None, 13333, True): np.array( + [ + [[12, 0, 0, 255], [21, 0, 0, 255], [47, 0, 0, 255], [15, 0, 0, 255]], + [[0, 0, 0, 0], [47, 0, 0, 255], [17, 0, 0, 255], [7, 0, 0, 255]], + [[44, 0, 0, 255], [25, 0, 0, 255], [255, 0, 0, 255], [19, 0, 0, 255]], + [[36, 0, 0, 255], [48, 0, 0, 255], [14, 0, 0, 255], [13, 0, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", None, 13333, False): np.array( + [ + [[0, 0, 12, 255], [0, 0, 21, 255], [0, 0, 47, 255], [0, 0, 15, 255]], + [[0, 0, 0, 0], [0, 0, 47, 255], [0, 0, 17, 255], [0, 0, 7, 255]], + [[0, 0, 44, 255], [0, 0, 25, 255], [0, 0, 255, 255], [0, 0, 19, 255]], + [[0, 0, 36, 255], [0, 0, 48, 255], [0, 0, 14, 255], [0, 0, 13, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 0], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint16, None, True): np.array( + [ + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint16, None, False): np.array( + [ + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint16, 232, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint16, 232, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint16, 13333, True): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGB", np.uint16, 13333, False): np.array( + [ + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 0], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGB", "RGBA", None, None, True): Exception, + (np.float32, "RGB", "RGBA", None, None, False): Exception, + (np.float32, "RGB", "RGBA", None, 232, True): Exception, + (np.float32, "RGB", "RGBA", None, 232, False): Exception, + (np.float32, "RGB", "RGBA", None, 13333, True): Exception, + (np.float32, "RGB", "RGBA", None, 13333, False): Exception, + (np.float32, "RGB", "RGBA", np.uint8, None, True): Exception, + (np.float32, "RGB", "RGBA", np.uint8, None, False): Exception, + (np.float32, "RGB", "RGBA", np.uint8, 232, True): Exception, + (np.float32, "RGB", "RGBA", np.uint8, 232, False): Exception, + (np.float32, "RGB", "RGBA", np.uint8, 13333, True): Exception, + (np.float32, "RGB", "RGBA", np.uint8, 13333, False): Exception, + (np.float32, "RGB", "RGBA", np.uint16, None, True): Exception, + (np.float32, "RGB", "RGBA", np.uint16, None, False): Exception, + (np.float32, "RGB", "RGBA", np.uint16, 232, True): Exception, + (np.float32, "RGB", "RGBA", np.uint16, 232, False): Exception, + (np.float32, "RGB", "RGBA", np.uint16, 13333, True): Exception, + (np.float32, "RGB", "RGBA", np.uint16, 13333, False): Exception, + (np.float32, "RGBA", "SIMPLE", None, None, True): np.array( + [ + [[248, 159, 5, 0], [217, 237, 151, 255], [102, 203, 57, 44], [244, 177, 73, 196]], + [[29, 189, 17, 250], [168, 188, 84, 208], [198, 134, 144, 81], [208, 209, 210, 126]], + [[8, 212, 80, 241], [159, 153, 128, 236], [0, 0, 7, 0], [249, 140, 61, 88]], + [[175, 255, 216, 90], [42, 76, 23, 172], [64, 65, 205, 254], [0, 255, 148, 117]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", None, None, False): np.array( + [ + [[5, 159, 248, 0], [151, 237, 217, 255], [57, 203, 102, 44], [73, 177, 244, 196]], + [[17, 189, 29, 250], [84, 188, 168, 208], [144, 134, 198, 81], [210, 209, 208, 126]], + [[80, 212, 8, 241], [128, 153, 159, 236], [7, 0, 0, 0], [61, 140, 249, 88]], + [[216, 255, 175, 90], [23, 76, 42, 172], [205, 65, 64, 254], [148, 255, 0, 117]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", None, 232, True): np.array( + [ + [[225, 145, 5, 0], [197, 215, 138, 255], [93, 185, 51, 40], [222, 161, 66, 178]], + [[26, 172, 15, 227], [153, 171, 76, 189], [181, 122, 131, 73], [189, 190, 191, 114]], + [[7, 193, 73, 219], [144, 139, 116, 215], [0, 0, 6, 0], [227, 127, 55, 80]], + [[159, 255, 197, 82], [38, 69, 21, 157], [58, 59, 186, 231], [0, 232, 134, 107]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", None, 232, False): np.array( + [ + [[5, 145, 225, 0], [138, 215, 197, 255], [51, 185, 93, 40], [66, 161, 222, 178]], + [[15, 172, 26, 227], [76, 171, 153, 189], [131, 122, 181, 73], [191, 190, 189, 114]], + [[73, 193, 7, 219], [116, 139, 144, 215], [6, 0, 0, 0], [55, 127, 227, 80]], + [[197, 255, 159, 82], [21, 69, 38, 157], [186, 59, 58, 231], [134, 232, 0, 107]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", None, 13333, True): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 0, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", None, 13333, False): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 0, 0, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint8, None, True): np.array( + [ + [[255, 0, 0, 0], [0, 0, 0, 255], [255, 0, 0, 255], [255, 0, 0, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 0, 255, 255]], + [[255, 255, 255, 0], [0, 0, 255, 255], [255, 255, 0, 0], [0, 255, 0, 255]], + [[0, 255, 255, 255], [255, 255, 0, 255], [255, 0, 0, 255], [255, 255, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint8, None, False): np.array( + [ + [[0, 0, 255, 0], [0, 0, 0, 255], [0, 0, 255, 255], [0, 0, 255, 255]], + [[0, 0, 0, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 0, 255, 255]], + [[255, 255, 255, 0], [255, 0, 0, 255], [0, 255, 255, 0], [0, 255, 0, 255]], + [[255, 255, 0, 255], [0, 255, 255, 255], [0, 0, 255, 255], [255, 255, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint8, 232, True): np.array( + [ + [[0, 0, 0, 0], [0, 0, 255, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[255, 255, 0, 0], [0, 0, 255, 0], [0, 255, 0, 0], [0, 255, 0, 255]], + [[0, 0, 0, 0], [255, 0, 255, 0], [255, 255, 255, 0], [0, 0, 0, 255]], + [[0, 255, 0, 255], [255, 0, 0, 0], [255, 0, 255, 0], [255, 255, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint8, 232, False): np.array( + [ + [[0, 0, 0, 0], [255, 0, 0, 255], [0, 0, 0, 255], [255, 0, 255, 255]], + [[0, 255, 255, 0], [255, 0, 0, 0], [0, 255, 0, 0], [0, 255, 0, 255]], + [[0, 0, 0, 0], [255, 0, 255, 0], [255, 255, 255, 0], [0, 0, 0, 255]], + [[0, 255, 0, 255], [0, 0, 255, 0], [255, 0, 255, 0], [255, 255, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint8, 13333, True): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint8, 13333, False): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint16, None, True): np.array( + [ + [[0, 0, 255, 0], [0, 255, 255, 0], [255, 255, 0, 255], [0, 0, 0, 255]], + [[0, 255, 0, 255], [255, 255, 0, 255], [0, 0, 255, 255], [255, 0, 0, 255]], + [[0, 0, 255, 0], [255, 0, 0, 0], [255, 255, 255, 0], [0, 255, 255, 0]], + [[255, 0, 255, 0], [255, 0, 0, 255], [255, 0, 0, 0], [255, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint16, None, False): np.array( + [ + [[255, 0, 0, 0], [255, 255, 0, 0], [0, 255, 255, 255], [0, 0, 0, 255]], + [[0, 255, 0, 255], [0, 255, 255, 255], [255, 0, 0, 255], [0, 0, 255, 255]], + [[255, 0, 0, 0], [0, 0, 255, 0], [255, 255, 255, 0], [255, 255, 0, 0]], + [[255, 0, 255, 0], [0, 0, 255, 255], [0, 0, 255, 0], [0, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint16, 232, True): np.array( + [ + [[255, 0, 0, 0], [0, 0, 255, 0], [255, 0, 255, 0], [255, 0, 255, 0]], + [[0, 0, 255, 0], [255, 255, 0, 255], [0, 0, 0, 0], [255, 0, 0, 255]], + [[0, 0, 0, 255], [255, 0, 0, 0], [255, 255, 255, 0], [0, 0, 0, 0]], + [[255, 0, 0, 0], [0, 255, 255, 0], [0, 0, 255, 255], [255, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint16, 232, False): np.array( + [ + [[0, 0, 255, 0], [255, 0, 0, 0], [255, 0, 255, 0], [255, 0, 255, 0]], + [[255, 0, 0, 0], [0, 255, 255, 255], [0, 0, 0, 0], [0, 0, 255, 255]], + [[0, 0, 0, 255], [0, 0, 255, 0], [255, 255, 255, 0], [0, 0, 0, 0]], + [[0, 0, 255, 0], [255, 255, 0, 0], [255, 0, 0, 255], [0, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint16, 13333, True): np.array( + [ + [[255, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 255, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [255, 0, 0, 0], [0, 0, 255, 255]], + [[0, 0, 0, 0], [0, 0, 255, 255], [255, 255, 0, 0], [255, 255, 0, 0]], + [[255, 0, 0, 0], [0, 255, 0, 0], [0, 0, 0, 255], [255, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "SIMPLE", np.uint16, 13333, False): np.array( + [ + [[0, 0, 255, 0], [0, 0, 0, 0], [0, 0, 0, 255], [255, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 255, 0], [255, 0, 0, 255]], + [[0, 0, 0, 0], [255, 0, 0, 255], [0, 255, 255, 0], [0, 255, 255, 0]], + [[0, 0, 255, 0], [0, 255, 0, 0], [0, 0, 0, 255], [0, 0, 255, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGB", None, None, True): Exception, + (np.float32, "RGBA", "RGB", None, None, False): Exception, + (np.float32, "RGBA", "RGB", None, 232, True): Exception, + (np.float32, "RGBA", "RGB", None, 232, False): Exception, + (np.float32, "RGBA", "RGB", None, 13333, True): Exception, + (np.float32, "RGBA", "RGB", None, 13333, False): Exception, + (np.float32, "RGBA", "RGB", np.uint8, None, True): Exception, + (np.float32, "RGBA", "RGB", np.uint8, None, False): Exception, + (np.float32, "RGBA", "RGB", np.uint8, 232, True): Exception, + (np.float32, "RGBA", "RGB", np.uint8, 232, False): Exception, + (np.float32, "RGBA", "RGB", np.uint8, 13333, True): Exception, + (np.float32, "RGBA", "RGB", np.uint8, 13333, False): Exception, + (np.float32, "RGBA", "RGB", np.uint16, None, True): Exception, + (np.float32, "RGBA", "RGB", np.uint16, None, False): Exception, + (np.float32, "RGBA", "RGB", np.uint16, 232, True): Exception, + (np.float32, "RGBA", "RGB", np.uint16, 232, False): Exception, + (np.float32, "RGBA", "RGB", np.uint16, 13333, True): Exception, + (np.float32, "RGBA", "RGB", np.uint16, 13333, False): Exception, + (np.float32, "RGBA", "RGBA", None, None, True): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 255, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", None, None, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 255, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", None, 232, True): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 255, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", None, 232, False): np.array( + [ + [[0, 0, 0, 0], [0, 0, 0, 255], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + [[0, 255, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", None, 13333, True): np.array( + [ + [[0, 0, 1, 0], [0, 0, 31, 255], [0, 0, 11, 0], [0, 0, 15, 0]], + [[0, 0, 3, 0], [0, 0, 17, 0], [0, 0, 29, 0], [0, 0, 43, 0]], + [[0, 0, 16, 0], [0, 0, 26, 0], [0, 0, 1, 0], [0, 0, 12, 0]], + [[0, 255, 44, 0], [0, 0, 4, 0], [0, 0, 42, 0], [0, 0, 30, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", None, 13333, False): np.array( + [ + [[1, 0, 0, 0], [31, 0, 0, 255], [11, 0, 0, 0], [15, 0, 0, 0]], + [[3, 0, 0, 0], [17, 0, 0, 0], [29, 0, 0, 0], [43, 0, 0, 0]], + [[16, 0, 0, 0], [26, 0, 0, 0], [1, 0, 0, 0], [12, 0, 0, 0]], + [[44, 255, 0, 0], [4, 0, 0, 0], [42, 0, 0, 0], [30, 0, 0, 0]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint8, None, True): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint8, None, False): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint8, 232, True): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint8, 232, False): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint8, 13333, True): np.array( + [ + [[255, 255, 0, 0], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint8, 13333, False): np.array( + [ + [[0, 255, 255, 0], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 0], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint16, None, True): np.array( + [ + [[255, 255, 0, 0], [255, 255, 0, 0], [255, 255, 255, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 0, 0], [255, 255, 0, 255]], + [[255, 0, 0, 255], [255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint16, None, False): np.array( + [ + [[0, 255, 255, 0], [0, 255, 255, 0], [255, 255, 255, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [0, 255, 255, 0], [0, 255, 255, 255]], + [[0, 0, 255, 255], [255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint16, 232, True): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint16, 232, False): np.array( + [ + [[255, 255, 255, 0], [255, 255, 255, 0], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 0], [255, 255, 255, 255]], + [[255, 0, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint16, 13333, True): np.array( + [ + [[255, 255, 0, 0], [255, 255, 0, 0], [255, 255, 0, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 255]], + [[255, 255, 0, 255], [255, 255, 0, 255], [255, 255, 0, 0], [255, 255, 255, 255]], + [[255, 0, 0, 255], [255, 255, 0, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), + (np.float32, "RGBA", "RGBA", np.uint16, 13333, False): np.array( + [ + [[0, 255, 255, 0], [0, 255, 255, 0], [0, 255, 255, 255], [255, 255, 255, 255]], + [[255, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 255]], + [[0, 255, 255, 255], [0, 255, 255, 255], [0, 255, 255, 0], [255, 255, 255, 255]], + [[0, 0, 255, 255], [0, 255, 255, 255], [255, 255, 255, 255], [255, 255, 255, 255]], + ], + dtype=np.uint8, + ), +} + + +def _error_description(output, test_case): + return ( + f"'{test_case['name']}' output does not match expectations\n" + + f"\tExpected:\n{test_case['expected_output']!r}\n\n" + + f"\tReceived:\n{output!r}\n" + ) + + +def _do_something_for_every_combo(func): + for dtype in [np.uint8, np.uint16, np.float32]: + for in_fmt in ["2D", "RGB", "RGBA"]: + data = INPUTS[(dtype, in_fmt)] + for levels_name in [None, "SIMPLE", "RGB", "RGBA"]: + levels = LEVELS.get(levels_name, None) + if dtype == np.float32 and levels_name is None: + continue + for lut_type in [None, np.uint8, np.uint16]: + lut = LUTS.get(lut_type, None) + for scale in [None, 232, 13333]: + for use_rgba in [True, False]: + key = (dtype, in_fmt, levels_name, lut_type, scale, use_rgba) + func(data, key, levels, lut, scale, use_rgba) + + +def _makeARGB(*args, **kwds): + img, alpha = real_makeARGB(*args, **kwds) + if kwds.get('useRGBA'): # endian independent + out = img + elif sys.byteorder == 'little': # little-endian ARGB32 to B,G,R,A + out = img + else: # big-endian ARGB32 to B,G,R,A + out = img[..., [3, 2, 1, 0]] + return out, alpha + + +def save_reference(): + """ + This saves the output (or exception type) of running makeARGB for every combo of arguments. The + output isn't fit for immediate inclusion in this file as EXPECTED_OUTPUTS, and needs some replace-all + work. + """ + with open("_unformatted_expected_outputs_", "w") as tmp_file: + + def write_expectation_to_file(data, key, levels, lut, scale, use_rgba): + try: + output, alpha = _makeARGB(data, lut=lut, levels=levels, scale=scale, useRGBA=use_rgba) + except Exception as e: + tmp_file.write(f"{key!r}: {type(e)}\n") + else: + tmp_file.write(f"{key!r}: {output!r},\n") + + _do_something_for_every_combo(write_expectation_to_file) + + +def test_makeARGB_against_generated_references(): + def assert_correct(data, key, levels, lut, scale, use_rgba): + expectation = EXPECTED_OUTPUTS[key] + if isinstance(expectation, type) and issubclass(expectation, Exception): + try: + _makeARGB(data, lut=lut, levels=levels, scale=scale, useRGBA=use_rgba) + except Exception as e: + assert expectation == type(e) + else: + assert False, f"makeARGB({key!r}) was supposed to raise {expectation} but didn't raise anything." + else: + output, alpha = _makeARGB(data, lut=lut, levels=levels, scale=scale, useRGBA=use_rgba) + assert ( + output == expectation + ).all(), f"Incorrect _makeARGB({key!r}) output! Expected:\n{expectation!r}\n Got:\n{output!r}" + + _do_something_for_every_combo(assert_correct) + + +@pytest.mark.skipif(cupy is None, reason="CuPy unavailable to test") +def test_cupy_makeARGB_against_generated_references(): + prev_setting = getConfigOption("useCupy") + try: + setConfigOption("useCupy", True) + + cupy = getCupy() + + def assert_cupy_correct(data, key, levels, lut, scale, use_rgba): + data = cupy.asarray(data) + if lut is not None: + lut = cupy.asarray(lut) + expectation = EXPECTED_OUTPUTS[key] + if isinstance(expectation, type) and issubclass(expectation, Exception): + try: + _makeARGB(data, lut=lut, levels=levels, scale=scale, useRGBA=use_rgba) + except Exception as e: + assert expectation == type(e) + else: + assert False, f"makeARGB({key!r}) was supposed to raise {expectation} but didn't raise anything." + else: + expectation = cupy.asarray(expectation) + output, alpha = _makeARGB(data, lut=lut, levels=levels, scale=scale, useRGBA=use_rgba) + assert ( + output == expectation + ).all(), f"Incorrect _makeARGB({key!r}) output! Expected:\n{expectation!r}\n Got:\n{output!r}" + + _do_something_for_every_combo(assert_cupy_correct) + finally: + setConfigOption("useCupy", prev_setting) + + +@pytest.mark.filterwarnings("ignore:invalid value encountered") +def test_numba_makeARGB_against_generated_references(): + oldcfg_numba = getConfigOption("useNumba") + if not oldcfg_numba: + try: + import numba + except ImportError: + pytest.skip("Numba unavailable to test") + + # useCupy needs to be set to False because it takes + # precedence over useNumba in rescaleData + oldcfg_cupy = getConfigOption("useCupy") + setConfigOption("useCupy", False) + setConfigOption("useNumba", not oldcfg_numba) + test_makeARGB_against_generated_references() + setConfigOption("useNumba", oldcfg_numba) + setConfigOption("useCupy", oldcfg_cupy) + + +def test_makeARGB_with_human_readable_code(): + # Many parameters to test here: + # * data dtype (ubyte, uint16, float, others) + # * data ndim (2 or 3) + # * levels (None, 1D, or 2D) + # * lut dtype + # * lut size + # * lut ndim (1 or 2) + # * useRGBA argument + # Need to check that all input values map to the correct output values, especially + # at and beyond the edges of the level range. + + def checkArrays(a, b): + # because py.test output is difficult to read for arrays + if not np.all(a == b): + comp = [] + for i in range(a.shape[0]): + if a.shape[1] > 1: + comp.append('[') + for j in range(a.shape[1]): + m = a[i, j] == b[i, j] + comp.append('%d,%d %s %s %s%s' % + (i, j, str(a[i, j]).ljust(15), str(b[i, j]).ljust(15), + m, ' ********' if not np.all(m) else '')) + if a.shape[1] > 1: + comp.append(']') + raise Exception("arrays do not match:\n%s" % '\n'.join(comp)) + + def checkImage(img, check, alpha, alphaCheck): + assert img.dtype == np.ubyte + assert alpha is alphaCheck + if alpha is False: + checkArrays(img[..., 3], 255) + + if np.isscalar(check) or check.ndim == 3: + checkArrays(img[..., :3], check) + elif check.ndim == 2: + checkArrays(img[..., :3], check[..., np.newaxis]) + elif check.ndim == 1: + checkArrays(img[..., :3], check[..., np.newaxis, np.newaxis]) + else: + raise Exception('invalid check array ndim') + + # uint8 data tests + + im1 = np.arange(256).astype('ubyte').reshape(256, 1) + im2, alpha = _makeARGB(im1, levels=(0, 255)) + checkImage(im2, im1, alpha, False) + + im3, alpha = _makeARGB(im1, levels=(0.0, 255.0)) + checkImage(im3, im1, alpha, False) + + im4, alpha = _makeARGB(im1, levels=(255, 0)) + checkImage(im4, 255 - im1, alpha, False) + + im5, alpha = _makeARGB(np.concatenate([im1] * 3, axis=1), levels=[(0, 255), (0.0, 255.0), (255, 0)]) + checkImage(im5, np.concatenate([im1, im1, 255 - im1], axis=1), alpha, False) + + im2, alpha = _makeARGB(im1, levels=(128, 383)) + checkImage(im2[:128], 0, alpha, False) + checkImage(im2[128:], im1[:128], alpha, False) + + # uint8 data + uint8 LUT + lut = np.arange(256)[::-1].astype(np.uint8) + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, lut, alpha, False) + + # lut larger than maxint + lut = np.arange(511).astype(np.uint8) + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, lut[::2], alpha, False) + + # lut smaller than maxint + lut = np.arange(128).astype(np.uint8) + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, np.linspace(0, 127.5, 256, dtype='ubyte'), alpha, False) + + # lut + levels + lut = np.arange(256)[::-1].astype(np.uint8) + im2, alpha = _makeARGB(im1, lut=lut, levels=[-128, 384]) + checkImage(im2, np.linspace(191.5, 64.5, 256, dtype='ubyte'), alpha, False) + + im2, alpha = _makeARGB(im1, lut=lut, levels=[64, 192]) + checkImage(im2, np.clip(np.linspace(384.5, -127.5, 256), 0, 255).astype('ubyte'), alpha, False) + + # uint8 data + uint16 LUT + lut = np.arange(4096)[::-1].astype(np.uint16) // 16 + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, np.arange(256)[::-1].astype('ubyte'), alpha, False) + + # uint8 data + float LUT + lut = np.linspace(10., 137., 256) + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, lut.astype('ubyte'), alpha, False) + + # uint8 data + 2D LUT + lut = np.zeros((256, 3), dtype='ubyte') + lut[:, 0] = np.arange(256) + lut[:, 1] = np.arange(256)[::-1] + lut[:, 2] = 7 + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, lut[:, None, ::-1], alpha, False) + + # check useRGBA + im2, alpha = _makeARGB(im1, lut=lut, useRGBA=True) + checkImage(im2, lut[:, None, :], alpha, False) + + # uint16 data tests + im1 = np.arange(0, 2 ** 16, 256).astype('uint16')[:, None] + im2, alpha = _makeARGB(im1, levels=(512, 2 ** 16)) + checkImage(im2, np.clip(np.linspace(-2, 253, 256), 0, 255).astype('ubyte'), alpha, False) + + lut = (np.arange(512, 2 ** 16)[::-1] // 256).astype('ubyte') + im2, alpha = _makeARGB(im1, lut=lut, levels=(512, 2 ** 16 - 256)) + checkImage(im2, np.clip(np.linspace(257, 2, 256), 0, 255).astype('ubyte'), alpha, False) + + lut = np.zeros(2 ** 16, dtype='ubyte') + lut[1000:1256] = np.arange(256) + lut[1256:] = 255 + im1 = np.arange(1000, 1256).astype('uint16')[:, None] + im2, alpha = _makeARGB(im1, lut=lut) + checkImage(im2, np.arange(256).astype('ubyte'), alpha, False) + + # float data tests + im1 = np.linspace(1.0, 17.0, 256)[:, None] + im2, alpha = _makeARGB(im1, levels=(5.0, 13.0)) + checkImage(im2, np.clip(np.linspace(-128, 383, 256), 0, 255).astype('ubyte'), alpha, False) + + lut = (np.arange(1280)[::-1] // 10).astype('ubyte') + im2, alpha = _makeARGB(im1, lut=lut, levels=(1, 17)) + checkImage(im2, np.linspace(127.5, 0, 256).astype('ubyte'), alpha, False) + + # nans in image + + # 2d input image, one pixel is nan + im1 = np.ones((10, 12)) + im1[3, 5] = np.nan + im2, alpha = _makeARGB(im1, levels=(0, 1)) + assert alpha + assert im2[3, 5, 3] == 0 # nan pixel is transparent + assert im2[0, 0, 3] == 255 # doesn't affect other pixels + + # 3d RGB input image, any color channel of a pixel is nan + im1 = np.ones((10, 12, 3)) + im1[3, 5, 1] = np.nan + im2, alpha = _makeARGB(im1, levels=(0, 1)) + assert alpha + assert im2[3, 5, 3] == 0 # nan pixel is transparent + assert im2[0, 0, 3] == 255 # doesn't affect other pixels + + # 3d RGBA input image, any color channel of a pixel is nan + im1 = np.ones((10, 12, 4)) + im1[3, 5, 1] = np.nan + im2, alpha = _makeARGB(im1, levels=(0, 1), useRGBA=True) + assert alpha + assert im2[3, 5, 3] == 0 # nan pixel is transparent + + # test sanity checks + class AssertExc(object): + def __init__(self, exc=Exception): + self.exc = exc + + def __enter__(self): + return self + + def __exit__(self, *args): + assert args[0] is self.exc, "Should have raised %s (got %s)" % (self.exc, args[0]) + return True + + with AssertExc(TypeError): # invalid image shape + _makeARGB(np.zeros((2,), dtype='float')) + with AssertExc(TypeError): # invalid image shape + _makeARGB(np.zeros((2, 2, 7), dtype='float')) + with AssertExc(): # float images require levels arg + _makeARGB(np.zeros((2, 2), dtype='float')) + with AssertExc(): # bad levels arg + _makeARGB(np.zeros((2, 2), dtype='float'), levels=[1]) + with AssertExc(): # bad levels arg + _makeARGB(np.zeros((2, 2), dtype='float'), levels=[1, 2, 3]) + with AssertExc(): # can't mix 3-channel levels and LUT + _makeARGB(np.zeros((2, 2)), lut=np.zeros((10, 3), dtype='ubyte'), levels=[(0, 1)] * 3) + with AssertExc(): # multichannel levels must have same number of channels as image + _makeARGB(np.zeros((2, 2, 3), dtype='float'), levels=[(1, 2)] * 4) + with AssertExc(): # 3d levels not allowed + _makeARGB(np.zeros((2, 2, 3), dtype='float'), levels=np.zeros([3, 2, 2])) diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index 193f17a6..495a3f9d 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -1,4 +1,4 @@ -import tempfile, os, sys, shutil, time +import os, sys, shutil, time import pyqtgraph as pg import pyqtgraph.reload import pytest @@ -7,21 +7,6 @@ import pytest pgpath = os.path.join(os.path.dirname(pg.__file__), '..') pgpath_repr = repr(pgpath) -# make temporary directory to write module code -path = None - -def setup_module(): - # make temporary directory to write module code - global path - path = tempfile.mkdtemp() - sys.path.insert(0, path) - -def teardown_module(): - global path - shutil.rmtree(path) - sys.path.remove(path) - - code = """ import sys sys.path.append({path_repr}) @@ -50,13 +35,13 @@ def remove_cache(mod): or (sys.version_info >= (3, 10)), reason="Unknown Issue" ) -def test_reload(): - py3 = sys.version_info >= (3,) - +@pytest.mark.usefixtures("tmp_module") +def test_reload(tmp_module): # write a module - mod = os.path.join(path, 'reload_test_mod.py') + mod = os.path.join(tmp_module, 'reload_test_mod.py') print("\nRELOAD FILE:", mod) - open(mod, 'w').write(code.format(path_repr=pgpath_repr, msg="C.fn() Version1")) + with open(mod, "w") as file_: + file_.write(code.format(path_repr=pgpath_repr, msg="C.fn() Version1")) # import the new module import reload_test_mod @@ -64,61 +49,37 @@ def test_reload(): c = reload_test_mod.C() c.sig.connect(c.fn) - if py3: - v1 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) - else: - v1 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, reload_test_mod.C.fn.__func__, c.sig, c.fn, c.fn.__func__) - + v1 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) # write again and reload - open(mod, 'w').write(code.format(path_repr=pgpath_repr, msg="C.fn() Version2")) + with open(mod, "w") as file_: + file_.write(code.format(path_repr=pgpath_repr, msg="C.fn() Version 2")) time.sleep(1.1) #remove_cache(mod) - result1 = pg.reload.reloadAll(path, debug=True) - if py3: - v2 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) - else: - v2 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, reload_test_mod.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + _ = pg.reload.reloadAll(tmp_module, debug=True) + v2 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) - if not py3: - assert c.fn.im_class is v2[0] oldcfn = pg.reload.getPreviousVersion(c.fn) if oldcfn is None: # Function did not reload; are we using pytest's assertion rewriting? raise Exception("Function did not reload. (This can happen when using py.test" " with assertion rewriting; use --assert=plain for this test.)") - if py3: - assert oldcfn.__func__ is v1[2] - else: - assert oldcfn.im_class is v1[0] - assert oldcfn.__func__ is v1[2].__func__ + assert oldcfn.__func__ is v1[2] assert oldcfn.__self__ is c - # write again and reload - open(mod, 'w').write(code.format(path_repr=pgpath_repr, msg="C.fn() Version2")) + with open(mod, "w") as file_: + file_.write(code.format(path_repr=pgpath_repr, msg="C.fn() Version2")) time.sleep(1.1) # remove_cache(mod) - result2 = pg.reload.reloadAll(path, debug=True) - if py3: - v3 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) - else: - v3 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, reload_test_mod.C.fn.__func__, c.sig, c.fn, c.fn.__func__) - - #for i in range(len(old)): - #print id(old[i]), id(new1[i]), id(new2[i]), old[i], new1[i] + _ = pg.reload.reloadAll(tmp_module, debug=True) + _ = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) cfn1 = pg.reload.getPreviousVersion(c.fn) cfn2 = pg.reload.getPreviousVersion(cfn1) - if py3: - assert cfn1.__func__ is v2[2] - assert cfn2.__func__ is v1[2] - else: - assert cfn1.__func__ is v2[2].__func__ - assert cfn2.__func__ is v1[2].__func__ - assert cfn1.im_class is v2[0] - assert cfn2.im_class is v1[0] + assert cfn1.__func__ is v2[2] + assert cfn2.__func__ is v1[2] assert cfn1.__self__ is c assert cfn2.__self__ is c diff --git a/pyqtgraph/util/cupy_helper.py b/pyqtgraph/util/cupy_helper.py index fadfaebe..40059c8f 100644 --- a/pyqtgraph/util/cupy_helper.py +++ b/pyqtgraph/util/cupy_helper.py @@ -1,8 +1,10 @@ +# -*- coding: utf-8 -*- import os from warnings import warn from .. import getConfigOption + def getCupy(): if getConfigOption("useCupy"): try: diff --git a/pyqtgraph/util/numba_helper.py b/pyqtgraph/util/numba_helper.py new file mode 100644 index 00000000..6eb46680 --- /dev/null +++ b/pyqtgraph/util/numba_helper.py @@ -0,0 +1,16 @@ +from warnings import warn + +from .. import getConfigOption + +def getNumbaFunctions(): + if getConfigOption("useNumba"): + try: + import numba + except ImportError: + warn("numba library could not be loaded, but 'useNumba' is set.") + return None + + from .. import functions_numba + return functions_numba + else: + return None diff --git a/pyqtgraph/widgets/ColorMapWidget.py b/pyqtgraph/widgets/ColorMapWidget.py index 5d1e5681..ee153423 100644 --- a/pyqtgraph/widgets/ColorMapWidget.py +++ b/pyqtgraph/widgets/ColorMapWidget.py @@ -161,7 +161,7 @@ class ColorMapParameter(ptree.types.GroupParameter): elif op == 'Set': colors[mask] = colors2[mask] - colors = np.clip(colors, 0, 1) + colors = fn.clip_array(colors, 0., 1.) if mode == 'byte': colors = (colors * 255).astype(np.ubyte) @@ -210,11 +210,11 @@ class RangeColorMapItem(ptree.types.SimpleParameter): def map(self, data): data = data[self.fieldName] - scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1) + scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1) cmap = self.value() colors = cmap.map(scaled, mode='float') - mask = np.isnan(data) | np.isinf(data) + mask = np.invert(np.isfinite(data)) nanColor = self['NaN'] nanColor = (nanColor.red()/255., nanColor.green()/255., nanColor.blue()/255., nanColor.alpha()/255.) colors[mask] = nanColor @@ -228,9 +228,7 @@ class EnumColorMapItem(ptree.types.GroupParameter): self.fieldName = name vals = opts.get('values', []) if isinstance(vals, list): - vals = OrderedDict([(v,str(v)) for v in vals]) - childs = [{'name': v, 'type': 'color'} for v in vals] - + vals = OrderedDict([(v,str(v)) for v in vals]) childs = [] for val,vname in vals.items(): ch = ptree.Parameter.create(name=vname, type='color') diff --git a/pyqtgraph/widgets/GraphicsView.py b/pyqtgraph/widgets/GraphicsView.py index 1cf6646e..a60bb028 100644 --- a/pyqtgraph/widgets/GraphicsView.py +++ b/pyqtgraph/widgets/GraphicsView.py @@ -7,10 +7,7 @@ Distributed under MIT/X11 license. See license.txt for more information. from ..Qt import QtCore, QtGui, QtWidgets, QT_LIB from ..Point import Point -import sys, os -from .FileDialog import FileDialog from ..GraphicsScene import GraphicsScene -import numpy as np from .. import functions as fn from .. import debug as debug from .. import getConfigOption @@ -124,8 +121,8 @@ class GraphicsView(QtGui.QGraphicsView): self.scaleCenter = False ## should scaling center around view center (True) or mouse click (False) self.clickAccepted = False - # connect to style update signals from NamedColorManager: - fn.NAMED_COLOR_MANAGER.paletteHasChangedSignal.connect(self.styleHasChanged) + # connect to style update signals from ColorRegistry: + fn.COLOR_REGISTRY.paletteHasChangedSignal.connect(self.styleHasChanged) def setAntialiasing(self, aa): @@ -389,7 +386,7 @@ class GraphicsView(QtGui.QGraphicsView): return if ev.buttons() == QtCore.Qt.RightButton: - delta = Point(np.clip(delta[0], -50, 50), np.clip(-delta[1], -50, 50)) + delta = Point(fn.clip_scalar(delta[0], -50, 50), fn.clip_scalar(-delta[1], -50, 50)) scale = 1.01 ** delta self.scale(scale[0], scale[1], center=self.mapToScene(self.mousePressPos)) self.sigDeviceRangeChanged.emit(self, self.range) diff --git a/pyqtgraph/widgets/JoystickButton.py b/pyqtgraph/widgets/JoystickButton.py index 88955934..53674df2 100644 --- a/pyqtgraph/widgets/JoystickButton.py +++ b/pyqtgraph/widgets/JoystickButton.py @@ -1,4 +1,5 @@ -from ..Qt import QtGui, QtCore +from math import hypot +from ..Qt import QtGui, QtCore, mkQApp __all__ = ['JoystickButton'] @@ -41,7 +42,7 @@ class JoystickButton(QtGui.QPushButton): def setState(self, *xy): xy = list(xy) - d = (xy[0]**2 + xy[1]**2)**0.5 + d = hypot(xy[0], xy[1]) # length nxy = [0, 0] for i in [0,1]: if xy[i] == 0: @@ -84,7 +85,7 @@ class JoystickButton(QtGui.QPushButton): if __name__ == '__main__': - app = QtGui.QApplication([]) + app = mkQApp() w = QtGui.QMainWindow() b = JoystickButton() w.setCentralWidget(b) @@ -96,8 +97,5 @@ if __name__ == '__main__': b.sigStateChanged.connect(fn) - ## Start Qt event loop unless running in interactive mode. - import sys - if sys.flags.interactive != 1: - app.exec_() + app.exec_() \ No newline at end of file diff --git a/pyqtgraph/widgets/RemoteGraphicsView.py b/pyqtgraph/widgets/RemoteGraphicsView.py index c9549c8a..2989f184 100644 --- a/pyqtgraph/widgets/RemoteGraphicsView.py +++ b/pyqtgraph/widgets/RemoteGraphicsView.py @@ -80,17 +80,14 @@ class RemoteGraphicsView(QtGui.QWidget): self.shm = mmap.mmap(-1, size, self.shmtag) ## can't use tmpfile on windows because the file can only be opened once. else: self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_READ) - self.shm.seek(0) - data = self.shm.read(w*h*4) - self._img = QtGui.QImage(data, w, h, QtGui.QImage.Format_ARGB32) - self._img.data = data # data must be kept alive or PySide 1.2.1 (and probably earlier) will crash. + self._img = QtGui.QImage(self.shm, w, h, QtGui.QImage.Format.Format_RGB32).copy() self.update() def paintEvent(self, ev): if self._img is None: return p = QtGui.QPainter(self) - p.drawImage(self.rect(), self._img, QtCore.QRect(0, 0, self._img.width(), self._img.height())) + p.drawImage(self.rect(), self._img, self._img.rect()) p.end() def serialize_mouse_enum(self, *args): @@ -211,7 +208,10 @@ class Renderer(GraphicsView): ## make sure shm is large enough and get its address if self.width() == 0 or self.height() == 0: return - size = self.width() * self.height() * 4 + dpr = self.devicePixelRatioF() + iwidth = int(self.width() * dpr) + iheight = int(self.height() * dpr) + size = iwidth * iheight * 4 if size > self.shm.size(): if sys.platform.startswith('win'): ## windows says "WindowsError: [Error 87] the parameter is incorrect" if we try to resize the mmap @@ -240,13 +240,14 @@ class Renderer(GraphicsView): # PySide2, PySide6 img_ptr = self.shm - self.img = QtGui.QImage(img_ptr, self.width(), self.height(), QtGui.QImage.Format_ARGB32) + self.img = QtGui.QImage(img_ptr, iwidth, iheight, QtGui.QImage.Format.Format_RGB32) + self.img.setDevicePixelRatio(dpr) self.img.fill(0xffffffff) p = QtGui.QPainter(self.img) self.render(p, self.viewRect(), self.rect()) p.end() - self.sceneRendered.emit((self.width(), self.height(), self.shm.size(), self.shmFileName())) + self.sceneRendered.emit((iwidth, iheight, self.shm.size(), self.shmFileName())) def deserialize_mouse_event(self, mouse_event): typ, pos, gpos, btn, btns, mods = mouse_event @@ -259,9 +260,10 @@ class Renderer(GraphicsView): def deserialize_wheel_event(self, wheel_event): pos, gpos, pixelDelta, angleDelta, btns, mods, phase, inverted = wheel_event - btns = QtCore.Qt.MouseButtons(btns) - mods = QtCore.Qt.KeyboardModifiers(mods) - phase = QtCore.Qt.ScrollPhase(phase) + if QT_LIB != 'PyQt6': + btns = QtCore.Qt.MouseButtons(btns) + mods = QtCore.Qt.KeyboardModifiers(mods) + phase = QtCore.Qt.ScrollPhase(phase) return QtGui.QWheelEvent(pos, gpos, pixelDelta, angleDelta, btns, mods, phase, inverted) def mousePressEvent(self, mouse_event): diff --git a/pytest.ini b/pytest.ini index cc80f5f7..550a2faa 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,15 +7,15 @@ xvfb_args=-ac +extension GLX +render faulthandler_timeout = 30 filterwarnings = + # re-enable standard library warnings + once::DeprecationWarning + once::PendingDeprecationWarning # comfortable skipping these warnings runtime warnings # https://stackoverflow.com/questions/40845304/runtimewarning-numpy-dtype-size-changed-may-indicate-binary-incompatibility ignore:numpy.ufunc size changed, may indicate binary incompatibility.*:RuntimeWarning - # Warnings generated from PyQt5.9 + # pyside2_512 specific issue ignore:This method will be removed in future versions. Use 'tree.iter\(\)' or 'list\(tree.iter\(\)\)' instead.:PendingDeprecationWarning - ignore:.*'U' mode is deprecated.*:DeprecationWarning - # py36/pyside2_512 specific issue - ignore:split\(\) requires a non-empty pattern match\.:FutureWarning # pyqtgraph specific warning we want to ignore during testing ignore:Visible window deleted. To prevent this, store a reference to the window object. - # xvfb warnings + # xvfb warnings on non-linux systems ignore:Unknown config option:pytest.PytestConfigWarning diff --git a/setup.py b/setup.py index 8f328f9a..6068ceba 100644 --- a/setup.py +++ b/setup.py @@ -81,7 +81,7 @@ class Build(build.build): if os.path.isdir(buildPath): distutils.dir_util.remove_tree(buildPath) - ret = build.build.run(self) + build.build.run(self) class Install(install.install): @@ -110,8 +110,10 @@ class Install(install.install): 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)) + with open(initfile, "r") as file_: + data = file_.read() + with open(initfile, "w") as file_: + file_.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. " @@ -134,12 +136,14 @@ setup( 'test': helpers.TestCommand, 'debug': helpers.DebugCommand, 'mergetest': helpers.MergeTestCommand, + 'asv_config': helpers.ASVConfigCommand, 'style': helpers.StyleCommand}, packages=allPackages, python_requires=">=3.7", package_dir={'pyqtgraph.examples': 'examples'}, ## install examples along with the rest of the source package_data={'pyqtgraph.examples': ['optics/*.gz', 'relativity/presets/*.cfg'], "pyqtgraph.icons": ["*.svg", "*.png"], + "pyqtgraph": ["colors/maps/*.csv", "colors/maps/*.txt"], }, install_requires = [ 'numpy>=1.17.0', diff --git a/tools/generateChangelog.py b/tools/generateChangelog.py index 3dcd692d..f003c263 100644 --- a/tools/generateChangelog.py +++ b/tools/generateChangelog.py @@ -28,17 +28,18 @@ def generateDebianChangelog(package, logFile, version, maintainer): current_version = None current_log = None current_date = None - for line in open(logFile).readlines(): - match = re.match(package+r'-(\d+\.\d+\.\d+(\.\d+)?)\s*(\d+-\d+-\d+)\s*$', line) - if match is None: - if current_log is not None: - current_log.append(line) - else: - if current_log is not None: - releases.append((current_version, current_log, current_date)) - current_version, current_date = match.groups()[0], match.groups()[2] - #sys.stderr.write("Found release %s\n" % current_version) - current_log = [] + with open(logFile) as file_: + for line in file_.readlines(): + match = re.match(package+r'-(\d+\.\d+\.\d+(\.\d+)?)\s*(\d+-\d+-\d+)\s*$', line) + if match is None: + if current_log is not None: + current_log.append(line) + else: + if current_log is not None: + releases.append((current_version, current_log, current_date)) + current_version, current_date = match.groups()[0], match.groups()[2] + #sys.stderr.write("Found release %s\n" % current_version) + current_log = [] if releases[0][0] != version: raise Exception("Latest release in changelog (%s) does not match current release (%s)\n" % (releases[0][0], version)) diff --git a/tools/setupHelpers.py b/tools/setupHelpers.py index 63b255d3..097fec76 100644 --- a/tools/setupHelpers.py +++ b/tools/setupHelpers.py @@ -1,21 +1,16 @@ # -*- coding: utf-8 -*- -import os, sys, re -try: - from subprocess import check_output, check_call -except ImportError: - import subprocess as sp - def check_output(*args, **kwds): - kwds['stdout'] = sp.PIPE - proc = sp.Popen(*args, **kwds) - output = proc.stdout.read() - proc.wait() - if proc.returncode != 0: - ex = Exception("Process had nonzero return value " - + "%d " % proc.returncode) - ex.returncode = proc.returncode - ex.output = output - raise ex - return output +from contextlib import suppress + +import json +import os +import re +import shutil +import subprocess +import sys +from distutils import core +from typing import Dict, Any + +from generateChangelog import generateDebianChangelog # Maximum allowed repository size difference (in kB) following merge. # This is used to prevent large files from being inappropriately added to @@ -154,20 +149,20 @@ def checkStyle(): if os.path.splitext(f)[1] not in ('.py', '.rst'): continue filename = os.path.join(path, f) - fh = open(filename, 'U') - _ = fh.readlines() - endings = set( - fh.newlines - if isinstance(fh.newlines, tuple) - else (fh.newlines,) - ) - endings -= allowedEndings - if len(endings) > 0: - print("\033[0;31m" - + "File has invalid line endings: " - + "%s" % filename + "\033[0m") - ret = ret | 2 - count += 1 + with open(filename, 'U') as fh: + _ = fh.readlines() + endings = set( + fh.newlines + if isinstance(fh.newlines, tuple) + else (fh.newlines,) + ) + endings -= allowedEndings + if len(endings) > 0: + print("\033[0;31m" + + "File has invalid line endings: " + + "%s" % filename + "\033[0m") + ret = ret | 2 + count += 1 print('checked line endings in %d files' % count) @@ -229,9 +224,9 @@ def unitTests(): """ try: if sys.version[0] == '3': - out = check_output('PYTHONPATH=. py.test-3', shell=True) + out = subprocess.check_output('PYTHONPATH=. py.test-3', shell=True) else: - out = check_output('PYTHONPATH=. py.test', shell=True) + out = subprocess.check_output('PYTHONPATH=. py.test', shell=True) ret = 0 except Exception as e: out = e.output @@ -295,12 +290,12 @@ def checkMergeSize( try: print("Check out target branch:\n" + setup) - check_call(setup, shell=True) - targetSize = int(check_output(checkSize, shell=True)) + subprocess.check_call(setup, shell=True) + targetSize = int(subprocess.check_output(checkSize, shell=True)) print("TARGET SIZE: %d kB" % targetSize) print("Merge source branch:\n" + merge) - check_call(merge, shell=True) - mergeSize = int(check_output(checkSize, shell=True)) + subprocess.check_call(merge, shell=True) + mergeSize = int(subprocess.check_output(checkSize, shell=True)) print("MERGE SIZE: %d kB" % mergeSize) diff = mergeSize - targetSize @@ -355,7 +350,7 @@ def getInitVersion(pkgroot): def gitCommit(name): """Return the commit ID for the given name.""" - commit = check_output( + commit = subprocess.check_output( ['git', 'show', name], universal_newlines=True).split('\n')[0] assert commit[:7] == 'commit ' @@ -375,11 +370,16 @@ def getGitVersion(tagPrefix): if not os.path.isdir(os.path.join(path, '.git')): return None - v = check_output(['git', - 'describe', - '--tags', - '--dirty', - '--match="%s*"'%tagPrefix]).strip().decode('utf-8') + try: + v = ( + subprocess.check_output( + ["git", "describe", "--tags", "--dirty", '--match="%s*"' % tagPrefix], + stderr=subprocess.DEVNULL) + .strip() + .decode("utf-8") + ) + except (FileNotFoundError, subprocess.CalledProcessError): + return None # chop off prefix assert v.startswith(tagPrefix) @@ -414,7 +414,7 @@ def getGitVersion(tagPrefix): def getGitBranch(): m = re.search( r'\* (.*)', - check_output(['git', 'branch'], + subprocess.check_output(['git', 'branch'], universal_newlines=True)) if m is None: return '' @@ -476,11 +476,57 @@ def getVersionStrings(pkg): return version, forcedVersion, gitVersion, initVersion -from distutils.core import Command -import shutil, subprocess -from generateChangelog import generateDebianChangelog +DEFAULT_ASV: Dict[str, Any] = { + "version": 1, + "project": "pyqtgraph", + "project_url": "http://pyqtgraph.org/", + "repo": ".", + "branches": ["master"], + "environment_type": "virtualenv", + "show_commit_url": "http://github.com/pyqtgraph/pyqtgraph/commit/", + # "pythons": ["3.7", "3.8", "3.9"], + "matrix": { + # "numpy": ["1.17", "1.18", "1.19", ""], + "numpy": "", + "pyqt5": ["", None], + "pyside2": ["", None], + }, + "exclude": [ + {"pyqt5": "", "pyside2": ""}, + {"pyqt5": None, "pyside2": None} + ], + "benchmark_dir": "benchmarks", + "env_dir": ".asv/env", + "results_dir": ".asv/results", + "html_dir": ".asv/html", + "build_cache_size": 5 +} -class DebCommand(Command): + +class ASVConfigCommand(core.Command): + description = "Setup the ASV benchmarking config for this system" + user_options = [] + + def initialize_options(self) -> None: + pass + + def finalize_options(self) -> None: + pass + + def run(self) -> None: + config = DEFAULT_ASV + with suppress(FileNotFoundError, subprocess.CalledProcessError): + cuda_check = subprocess.check_output(["nvcc", "--version"]) + match = re.search(r"release (\d{1,2}\.\d)", cuda_check.decode("utf-8")) + ver = match.groups()[0] # e.g. 11.0 + ver_str = ver.replace(".", "") # e.g. 110 + config["matrix"][f"cupy-cuda{ver_str}"] = "" + + with open("asv.conf.json", "w") as conf_file: + conf_file.write(json.dumps(config, indent=2)) + + +class DebCommand(core.Command): description = "build .deb package using `debuild -us -uc`" maintainer = "Luke Campagnola " debTemplate = "debian" @@ -500,8 +546,7 @@ class DebCommand(Command): debName = "python-" + pkgName debDir = self.debDir - assert os.getcwd() == self.cwd, 'Must be in package root: ' - + '%s' % self.cwd + assert os.getcwd() == self.cwd, 'Must be in package root: %s' % self.cwd if os.path.isdir(debDir): raise Exception('DEB build dir already exists: "%s"' % debDir) @@ -539,7 +584,7 @@ class DebCommand(Command): raise Exception("Error during debuild.") -class DebugCommand(Command): +class DebugCommand(core.Command): """Just for learning about distutils.""" description = "" user_options = [] @@ -554,7 +599,7 @@ class DebugCommand(Command): print(self.distribution.version) -class TestCommand(Command): +class TestCommand(core.Command): description = "Run all package tests and exit immediately with ", \ "informative return code." user_options = [] @@ -569,7 +614,7 @@ class TestCommand(Command): pass -class StyleCommand(Command): +class StyleCommand(core.Command): description = "Check all code for style, exit immediately with ", \ "informative return code." user_options = [] @@ -584,7 +629,7 @@ class StyleCommand(Command): pass -class MergeTestCommand(Command): +class MergeTestCommand(core.Command): description = "Run all tests needed to determine whether the current ",\ "code is suitable for merge." user_options = [] diff --git a/tox.ini b/tox.ini index c05fe689..6c0d03d4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,43 +1,37 @@ [tox] envlist = - ; qt latest - py{37,38}-{pyqt5,pyside2}_latest + ; qt 5.15.x + py{37,38,39}-{pyqt5,pyside2}_515 - ; qt 5.12.x (LTS) - py{36,37}-{pyqt5,pyside2}_512 + ; qt 5.12.x + py{37}-{pyqt5,pyside2}_512 + ; py38-pyside2_512 doesn't work due to PYSIDE-1140 + py38-pyqt5_512 - ; qt 5.9.7 (LTS) - py36-{pyqt5,pyside2}_59_conda + ; qt 6 + py{37,38,39}-{pyqt6,pyside6} [base] deps = pytest + pytest-xdist numpy scipy pyopengl - flake8 - coverage + h5py [testenv] -passenv = DISPLAY XAUTHORITY +passenv = DISPLAY XAUTHORITY, PYTHON_VERSION setenv = PYTHONWARNINGS=ignore:DEPRECATION::pip._internal.cli.base_command deps= {[base]deps} - pytest-cov - h5py - pyside2_512: pyside2>=5.12,<5.13 - pyqt5_512: pyqt5>=5.12,<5.13 - pyside2_latest: pyside2 - pyqt5_latest: pyqt5 - -conda_deps= - pyside2_59_conda: pyside2=5.9 - pyqt5_59_conda: pyqt=5.9 - -conda_channels= - conda-forge - free + pyside2_512: pyside2==5.12.6 + pyqt5_512: pyqt5==5.12.3 + pyside2_515: pyside2 + pyqt5_515: pyqt5 + pyqt6: pyqt6 + pyside6: pyside6 commands= python -c "import pyqtgraph as pg; pg.systemInfo()" - pytest {posargs:} + pytest -n auto {posargs:}