Compare commits

..

1 Commits

Author SHA1 Message Date
Christopher Mullins 65ef2a5a60
Expose the `clickable` property of `PlotDataItem`. (#519)
* Fix the `clickable` property of `PlotDataItem`.

Currently if you attempt to set the `clickable` property of a PlotDataItem,
this property is silently ignored. The expected behavior is to set the
`clickable` property of the underlying PlotCurveItem.

* Use setCurvesClickable and curvesClickable instead

* curve is singular
2021-04-13 22:02:30 -07:00
632 changed files with 12557 additions and 44993 deletions

46
.flake8
View File

@ -3,5 +3,47 @@ exclude = .git,.tox,__pycache__,doc,old,build,dist
show_source = True
statistics = True
verbose = 2
max-line-length = 88
extend-ignore = E203, W503
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

View File

@ -1,55 +0,0 @@
name: codeql
on: [push, pull_request]
jobs:
analyze:
name: analyze
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install PyQt5 numpy scipy
echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: 'python'
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
queries: +security-and-quality
setup-python-dependencies: false
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -1,128 +0,0 @@
name: main
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04 , windows-latest, macos-latest]
qt-lib: [pyqt, pyside]
python-version: [3.7, 3.8, 3.9]
include:
- python-version: "3.7"
qt-lib: "pyqt"
qt-version: "PyQt5~=5.12.0"
numpy-version: "~=1.18.0"
- python-version: "3.7"
qt-lib: "pyside"
qt-version: "PySide2~=5.12.0"
numpy-version: "~=1.18.0"
- python-version: "3.8"
qt-lib: "pyqt"
qt-version: "PyQt5~=5.15.0"
numpy-version: "~=1.21.0"
- python-version: "3.8"
qt-lib: "pyside"
qt-version: "PySide2~=5.15.0"
numpy-version: "~=1.21.0"
- python-version: "3.9"
qt-lib: "pyqt"
qt-version: "PyQt6"
numpy-version: "~=1.21.0"
- python-version: "3.9"
qt-lib: "pyside"
qt-version: "PySide6"
numpy-version: "~=1.21.0"
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
# Semantic version range syntax or exact version of a Python version
python-version: ${{ matrix.python-version }}
- name: "Install Windows-Mesa OpenGL DLL"
if: runner.os == 'Windows'
run: |
curl -LJO https://github.com/pal1000/mesa-dist-win/releases/download/19.2.7/mesa3d-19.2.7-release-msvc.7z
7z x mesa3d-19.2.7-release-msvc.7z
cd x64
xcopy opengl32.dll C:\windows\system32\mesadrv.dll*
xcopy opengl32.dll C:\windows\syswow64\mesadrv.dll*
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f
shell: cmd
- name: Install Dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
python -m pip install ${{ matrix.qt-version }} numpy${{ matrix.numpy-version }} scipy pyopengl h5py matplotlib numba
python -m pip install --use-feature=in-tree-build .
python -m pip install pytest
- name: "Install Linux VirtualDisplay"
if: runner.os == 'Linux'
run: |
sudo apt-get update -y
sudo apt-get install -y libxkbcommon-x11-0 x11-utils
sudo apt-get install --no-install-recommends -y libyaml-dev libegl1-mesa libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0
sudo apt-get install -y libopengl0
python -m pip install pytest-xvfb
- name: 'Debug Info'
run: |
echo python location: `which python`
echo python version: `python --version`
echo pytest location: `which pytest`
echo installed packages
python -m pip list
echo pyqtgraph system info
python -c "import pyqtgraph as pg; pg.systemInfo()"
shell: bash
env:
QT_DEBUG_PLUGINS: 1
- name: 'XVFB Display Info'
run: |
xvfb-run --server-args="-screen 0, 1920x1200x24 -ac +extension GLX +render -noreset" python -c "from pyqtgraph.opengl.glInfo import GLTest"
xvfb-run --server-args="-screen 0, 1920x1200x24 -ac +extension GLX +render -noreset" python -m pyqtgraph.util.get_resolution
if: runner.os == 'Linux'
- name: 'Display Info'
run: |
python -c "from pyqtgraph.opengl.glInfo import GLTest"
python -m pyqtgraph.util.get_resolution
if: runner.os != 'Linux'
- name: Run Tests
run: |
mkdir $SCREENSHOT_DIR
pytest tests -v
pytest examples -v
shell: bash
- name: Upload Screenshots
uses: actions/upload-artifact@v2
with:
name: Screenshots (Python ${{ matrix.python-version }} - Qt-Bindings ${{ matrix.qt-lib }} - OS ${{ matrix.os }})
path: $SCREENSHOT_DIR
if-no-files-found: ignore
env:
SCREENSHOT_DIR: ./screenshots
build-wheel:
name: build wheel
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: Build Wheel
run: |
python -m pip install setuptools wheel
python setup.py bdist_wheel

View File

@ -1,33 +0,0 @@
# This workflows will upload a Python Package using setup.py/twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install wheel twine setuptools
- name: Build and publish
env:
TWINE_USERNAME: __token__
# The PYPI_PASSWORD must be a pypi token with the "pypi-" prefix with sufficient permissions to upload this package
# https://pypi.org/help/#apitoken
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*

2
.gitignore vendored
View File

@ -107,5 +107,3 @@ rtr.cvs
# ctags
.tags*
.asv/
asv.conf.json

398
CHANGELOG
View File

@ -1,401 +1,3 @@
pyqtgraph-0.12.3
Highlights:
- PlotCurveItem render speed is now substantially faster
- #1868/#1873 Example app now has filter text input
- #1910 PlotSpeedTest now has parameter tree control panel
New Features:
- #1844 More parameter item types (File, Calendar, ProgressBar, Font, Pen, Slider)
- #1865 Matplotlib colormaps viridis, plasma, magma and inferno are now included in pyqtgraph
- #1911 Extend Colormap with HSL cycles and subset generation
- #1932 Make anti-aliasing optional for paintGL in PlotCurveItem
- #1944 Expand use of QColor functions/methods, including setNamedColor
- #1952 Add checklist parameter item
- #1998 ThreadTrace can now save to a file
Performance Enhancement:
- #1927 Reduce ColorMap inefficiencies
- #1956 use QByteArray as backing store in arrayToQPath
- #1965 perform arrayToQPath in chunks
Bug Fixes:
- #1845 Fix zoom behavior with showGrid by separating mouse events stolen by AxisItem
- #1860 RemoteGraphicsView and RemoteSpeedTest now work under windows venv environments
- #1865 Fixed matplotlib colormap importer code
- #1869 Fix ColorBarItem tick position on export
- #1871 Allow adding items to GLViewWidget before showing plot
- #1875 Fix calls in mouse methods in GLViewWidgets due to missing event.localPos() in PyQt6
- #1876 Fix for improper placement of ROI handle positions in some cases
- #1889/#2003 Fix call to drawText in GLTextItem and GLGradientLegendItem on Python 3.10
- #1897/#1902 Re-enable "experimental" feature with fix for PlotCurveItem with OpenGL on Windows
- #1907 Fix GLVolumeItem example for arm64 platforms
- #1909 Check if AxisItem.label is None before and exit early in resizeEvent
- #1920 arrayToQPath can handle empty paths
- #1936 QPolygonF creation can now handle empty arrays
- #1968 Fix output of clip_array in colormap.modulatedBarData not being assigned
- #1973 Fix PlotItem.updateDecimate unhiding intentionally hidden curves
- #1974 Fix ImageView levelMode with levelMode == 'rgba'
- #1987 Fix HistogramLUTItem itemChanged with use of autoLevel
- #2009 Fix ROI curves hidding in ImageView
API/Behavior Changes:
- #1992 Reverted to traditional log10 mode for PlotDataItem
- #1840 Allow border=False in GraphicsLayout
- #1846 Reduced pollution to pg.namespace
- #1853 ColorMap.getColors and getStops behavior changes
- #1864 Draw GradientLegend in ViewBox coordinates
- #1885 Raise TypeError instead of general Exception if functions.eq is unable to determine equality
- #1903 Cleanup GLViewWidget
- #1908 More readable parameters for ColorBarItem
- #1914 Emit deprecation warning for use of pyqtgraph.ptime
- #1928 Restore previous signature of TargetItem.setPos
- #1940 fix log mode by reverting to previous formulation
- #1954 Deprecate use of values opt for list parameter type
- #1995 ColorButton now takes optional padding argument instead of hardcoded value of 6
Other:
- #1862/#1901 MetaArray now under deprecation warning, to be removed in a future version
- #1892 Add GLPainterItem Example
- #1844 Debugged elusive intermitted CI segfault
- #1870/#1891 Updated README.md
- #1895 Update CONTRIBUTING.md
- #1913 Bump sphinx and theme versions
- #1919 Re-organize paramtypes
- #1935 Remove some unused imports
- #1939 Remove usage of python2_3.py
- #1941 Remove str casting of QTextEdit.toPlainText output
- #1942 Add EOF newline to files missing it
- #1943 Remove python2 code paths
- #1951 Fix typos in docs
- #1957 Bump minimum numpy version to 1.18
- #1968 Fix ImageView calling deprecated QGraphicsItem.scale()
- #1985 delegate float LUTs to makeARGB with warning
- #2014 Replace couple absolute imports with relative imports
pyqtgraph-0.12.2
Highlights
- Qt6 6.0 support has been removed in favor of Qt6 6.1+
- More numba utilization, specifically for makeARGB
- Substantial ImageItem performance improvements have been made thanks to @pijyoi and @outofculture
- Significant performance improvements made to ScatterPlotItem and LinePlots
- More ColorMap features/support (more are coming!)
New Features
- #1318 Added TargetItem
- #1707 Added Qt 6.1 support
- #1729 Allow gradient position to be configured on a histogram
- #1742 Better support for plotting with gradients
- #1776 Add GLTextItem
- #1797 Add ColorMap linearization (using CIELab calculations), colorDistance functionality
- #1865 Include viridis, magma, plasma, inferno, and cividis colormaps
- #1868/#1873 Example app now has a filter text box to search for relevant examples
Performance enhancement:
- #1738, #1695, 1786, #1768, 1794 - ImageItem/makeARGB performance improvements
- #1724 Use math module for scalar values math instead of numpy functions
- #1796 Greatly speed up line plots with use-case of connect='all'
- #1817 Speed up some cases of connect='finite' (few discontinuities)
- #1829 Use QPainter.drawPixmapFragments for ScatterPlotItem
Bug Fixes:
- #1700 Fixed ROI getArrayRegion
- #1748 Fixed bug when plotting boolean arrays in PlotDataItem
- #1791 Callable LUTs being used on the ImageItem substrates
- #1783 Fix memory leak in GLMeshItem
- #1802 Updated cx_freeze example and added workaround for template files
- #1804 Fix mouseClick handling for Qt6 on ROIs
- #1799 Force cameraPosition() to return a Vector in GLViewWidget
- #1809 Sanitize ShowGrid Alpha Input PlotItem
- #1816 Fix bug with Parameter value failing with numpy array-like values
- #1827 Fix BusyCursor to use internal stack provided by setOverrideCursor/restoreOverrideCursor
- #1833 Fix ScatterPlot render issues for PyQt6 6.1.0
- #1843 Fix zoom only applied to y-axis with show grid
- #1860 Fix pyqtgraph multiprocessing on Windows inside a venv environment
- #1869 Fix color bar ticks not being drawn correctly during export
- #1865 Fix matplotlib colormap import code
- #1876 Fix LineROI handle positions being way off-base in some circumstances
- #1871 Allow adding items to GLViewWidget before calling GLViewWidget.show()
- #1864 Draw GradientLegend in ViewBox coordinate system with correct orientation
- #1875 Fixed mouse events in GLViewWidget for PyQt6 bindings
API/Behavior Changes:
- #519 Expose clickable property in PlotDataItem
- #1772 Keep ColorMap values for RGBA as uint8
- #1736 RemoteGraphicsView is now hidpi aware
- #1779 Have SpinBox use fallback minStep in dec mode
- #1706 Colors defined with hex string values must start with a #
- #1819 Added method to disable autoscaling for HistogramLUTItem
- #1638 Expose number of subsamples in ImageItem auto-level determination
- #1824 Remove little-endian assumption for image export
Other
- #1807 Merge pyqtgraph/test-data repo into main repo, move test files to tests directory
- #1862 Scheduled deprecation for MetaArray module
- #1846 Cleaned up pg namespace
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 individual points in scatter plot
- #395 LegendItem display options
- #1310 Added `Pa` to Units
- #1310 `debug.ThreadTrace` add support for thread names
- #117 Flow Chart Nodes now resized based on number of inputs/outputs
- #1154 `DateAxisItem`
- #1285 Improve control over ROI/handle pens
- #1273 `PColorMeshItem`
- #1397 `LegendItem` enable customization of label text size
- #1422 Permit entry of non-finite values into float `SpinBox`
- #1442 `TickSliderItem.allowRemote` property added
- #1441 `Tick.removeAllowed` is now a regular property and used with `TickSliderItem`
- #1388 Emit a signal when `GraphicsScene` `addItem` or `removeItem` methods called
Performance enhancement:
- #1240/#345 GLImageItem clear need update flag
- #977 Faster computation option for pseudoscatter
- #1297 Improve ArrowItem performance
- #1296 Update `h5py` deps in metaarray
- #1295 Improve TextItem performance
- #1283 Performance improvements to arrayToQPath
- #816 Avoid constructing shadow pens when no shadow pen is set
- #1311 HistogramLUTItem detect trivial gradients
- #1312 avoid extra work when setLabelAngle would have no effect
- #1391 cache viewRect of `GraphicsItem` to reduce CPU load
- #150 Slight speedup to ViewBox panning
- #1420 Many ScatterPlot Improvements
API/Behavior Change:
- #496 Always antialias lines between gradient and region in HistogramLUTItem
- #385 Add headWidth parameter to arrows
- #551 fps variable on ImageView
- #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 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
- #1387 ScatterPlotItem: Make + and x symbols thicker
- #1362 Make flowchart.Terminal sortable
- #1360 Add "left" and "right" step Modes to PlotCurveItem and PlotDataItem
- #1414 Emit event with mouse clicks for some items
- #1413 `InfiniteLine.viewTransformChanged` now calls superclass-method
- #1411 Hide `WidgetParameterItem.defaultBtn` if param has no default
- #1410 add fontSize kwarg to `Dock`
- #159 Add wrapping option to `SpinBox`
- #330 Set parameter default button `autoDefault` value to `False`
- #157 Provide `WidgetGroupInterface` to `GradientWidget`
- #151 Optional provide custom `PlotItem` to `PlotWidget`
- #1140 Dynamic range limiting in `PlotDataItem`
- #1383 GraphicsView set a transparent background palette
- #1428 Add default color for `ColorMap` type in `ColorMapWidget`
Bug Fixes:
- #1239 Avoid adding PlotItem twice
- #508 siScale precision
- #503 Fix butg in RawImageWidget which resulted in mirrored image
- #1242,#1267 Add the mouse event to the sigClicked signal in PlotCurveItem
- #1247 Restore the now-deprecated PlotWindow and ImageWindow classes
- #1249 Remove ScatterPlotItem's SpotItems during addItem call
- #1252 Fix incorrect tick text boundaries calculation on axis by setting the font
- #1310 Fixed `Vector.__init__` when used with `QVector3D` argument
- #1310 Fix console exception filtering for python3
- #1310 BusyCursor only restore cursor after all nested levels have exited
- #1310 `SimpleParameter.setValue` coerces argument to int if parameter type is int
- #1307 Fixed `reload` methods for python3
- #1294 Various console fixes
- #1293 Various Python3 code fixes
- #1282 Handle Axis SI prefix scaling in MatplotlibExporter
- #1276 Fix problems with high frequency gaming mice
- #1270 Various fixes with AxisItem space being taken
- #1272 `LegendItem.clear()` fixed
- #1268 Check for container before setting dock orientation
- #1312 Avoid divide by 0 condition in TargetItem
- #1309 Properly retain and use hoverPen argument in _PolyLineSegment
- #1371 Explicitly set line width in PlotCurveItem when using OpenGL
- #1319 don't automatically reload modules without pyc
- #1319 make ptime.time on py3 return precision wall-clock time
- #1334 Edge case detection in PlotCurveItem
- #1339 fix handling of QVector3D args in Vector.__init__
- #1336 Make `parent` an explicit kwarg of ArrowItem.__init__, avoid passing into setStyle
- #1368 Disconnect from correct slots in Flowchart
- #1364 fix log scaling
- #963 Allow last image in stack to be selected by slider in ImageView
- #1045 Raise AttributeError in __getattr__ in TabWindow
- #960 Work around PySide setOverrideCursor bug in BusyCursor
- #309 Encode QPropertyAnimation property name if not passed as bytes
- #1072 Fix storing of ragged curves in HDF5Exporter
- #1275 Fix Parameter.hasDefault
- #1291 Get ImageView ROI working with both row and col major data
- #1377 Consolidate and fix downsample factor computation in ImageItem
- #1376 Fix PlotItem.setAxisItems
- #1392 SignalProxy: Correct initialization without slot argument
- #1306 Fix incorrect rendering of overlapping object in renderToArray()
- #1349 Avoid calling method on undefined attribute
- #1367 AxisItem: Account for empty strings in the visibility of text and units
- #1356 Fix `ParameterTree` tree name and title handling
- #1419 Fix `DataTreeWidget` dict sorting crash
- #1408 Fix mouse interaction issues with `DockLabel`
- #329 Fix bug where `int` and `float` parameter limits are not always set
- #158 Make `DockArea` compatible with Qt Designer
- #1405 Fix name setting in `ScatterPlotItem` options
- #1403 Do not apply transparent background to Qt4
- #1468 Allow zero step in `ImageItem`
- #1464 Remove `ViewBox.childGroup`'s `ItemClipsChildrenToShape` flag
- #1461 arrayToQPath revert to old behavior of `connect=ndarray` parameter
- #1459 Fix `TickSliderItem` to avoid ghost ticks
- #1456 Resolve issue with `PlotCurveItem` with merging PRs in an incorrect order
- #1287 Fill in non-finite plot values for Qt versions >= 5.12.3
- #1447 Clipped AxisItem tick value labels to prevent drawing artifacts
- #1435 Fix autosize not taking to the correct range with `TextItem` in view
- #1452 merge `InfiniteLine` caching calls
- #1446 Fix `PlotDataItem.setData([], [])`
- #1443 Fix Viewbox axis zoom in RectMode
- #1439 Fix `TickSliderItem.setTickValue` when it references a `GradientEditorItem` method
- #1361 Prevent item duplication in `Node` context menu
- #1423 Fix typo in `kwargs` for `GridItem.setTextPen`
- #1401 Fix width, height and background in SVG exporter
- #1416 Handle case in `ROI` when `shape`, `vectors`, or `origin` keywords are passed in
Maintenance:
- #389 Revert workaround for upstream QT bug regarding mouse events
- #1243 Ensure setPos in ROI is initialized correctly
- #1241 Pin pytest-xvfb version on py2
- #1310 Added tests for `functions.subArray`
- #1307 Add `ThreadSafeTimer` to `__init__.py`
- #467 derivatePlots cleanup
- #400 `ImageView.Timeline` better visibility
- #1265 Make the documentation reproducible
- #1319 clean up exception messages in console
- #356 Fix some NumPy warnings
- #1326 Improve docs for MultiPlotWidget and MultiPlotItem
- #1331 Migrate imports of PyQt5's sip module to new namespace
- #1370 Fix deprecation warning in multiprocess module
- #308 Fix opt name for SpinBox: range -> bounds in UnsharpMaskNode example
- #887 Update collections.abc imports
- #1142 Miscellaneous doc fixups
- #1169 Avoid using mutable default argument value
- #1073 Python3 fixes
- #1284 Update doc strings to clarify getArrayRegion API for ROI subclasses
- #1042 Close windows at the end of test functions
- #1374 Test warnings cleanup
- #1375 Add targeted Vector test coverage
- #1384 GLViewWidget.pan docstring typo
- #1382 Autoformat LegendItem
- #1396 Add tests for GraphicsView
- #1399 Disable mouse rate limiting on test_ROI
- #1409 Prepend conda-forge channel prior to env creation in CI
- #1302 Fix Example app now works with Qt4/Python2
- #1402 Handle case of version string having no `+`
- #1400 Fix sphinx warnings on `PColorMeshItem`
- #1328 Add docs build job to CI
- #1464 Use `conda-forge` on pyside2+linux
- #1448 Fixes for `examples/CustomPlot.py`
- #1432 ExampleApp fix to use `pg` module from directory
pyqtgraph-0.11.0
NOTICE: This is the _last_ feature release to support Python 2 and Qt 4 (PyQt4 or pyside 1)

View File

@ -1,135 +0,0 @@
# Code of Conduct
Diversity is one of our huge strengths, but it can also lead to communication issues. To support a welcoming environment
for all, regardless of individual differences, we have a few ground rules that we ask people to adhere to when they
participate in this community. These rules apply equally to founders, organizers, contributors, users and affiliates —
in short, to all participants.
This isnt an exhaustive list of things that you must do, or cant do. Rather, take it in the spirit in which its
intended. Its a guide to make it easier to enrich all of us and the technical communities in which we participate, and
which we represent.
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for
everyone, regardless of background or identity. This includes, but is not limited to, members of any race, ethnicity,
culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual
orientation, gender identity and expression, age, size, family status, political belief, religion, level of experience,
and mental or physical ability.
We pledge to be respectful. Not all of us will agree all the time, but disagreement is no excuse for poor behavior and
poor manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a
personal attack. Its important to remember that a community where people feel uncomfortable or threatened is not a
productive one. Members of the PyQtGraph community should be respectful when dealing with each other as well as with
people outside the community.
We pledge to be welcoming, supportive, kind and professional. We pledge to act and interact in ways that contribute to
an open, diverse, inclusive, and healthy community. We pledge not insult or put down other participants, individually or
as a group. Harassment and other exclusionary behavior arent acceptable.
## Examples
Examples of behavior that contributes to a positive environment for our community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
* Violent threats or language directed against another person.
* Discriminatory jokes and language.
* Posting sexually explicit or violent material.
* Posting (or threatening to post) other peoples personally identifying information ("doxing").
* Personal insults, especially those using racist or sexist terms.
* Unwelcome sexual attention.
* Repeated harassment of others. In general, if someone asks you to stop, then stop.
* Advocating for, or encouraging, any of the above behavior.
## Where does this code of conduct apply
This Code of Conduct applies within all online community spaces, official organized meetups, and when an individual is
officially representing the community in public spaces, online or offline.
## What to do in case of violations
If you believe someone is violating the code of conduct, we ask that you report it by contacting any of the following
individuals, being sure to include mention of your message being about a "PyQtGraph conduct violation":
* Ognyan Moore @j9ac9k <ognyan.moore@gmail.com>
* Martin Chase @outofculture <outofculture@gmail.com>
* Nathan Jessurun @ntjess <ntjessu@gmail.com>
All complaints will be reviewed and investigated promptly and fairly. All reports will be kept confidential. In some
cases, we may determine that a public statement will need to be made. If thats the case, the identities of all victims
and reporters will remain confidential unless those individuals instruct us otherwise.
If you believe anyone is in physical danger, please notify appropriate law enforcement first. If you are unsure which
law enforcement agency is appropriate, please include this in your report and we will attempt to notify them.
### What to include in the report
* Your contact information (so we can get in touch with you if we need to follow up)
* Names of any individuals involved (real names, nicknames, or pseudonyms). If there were other witnesses besides you,
please try to include them as well.
* When and where the incident occurred. Please be as specific as possible.
* Your account of what occurred. If there is a publicly available record (e.g., a mailing list archive or a public chat
logger) please include a link. Our Slack is currently on a free plan that does not retain more than 10,000 messages of
history, if an incident occurred on Slack please take a screenshot so it is not lost.
* Any extra context you believe existed for the incident.
* Whether you believe this incident is ongoing.
* Any other information you believe we should have.
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take
appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits,
issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for
moderation decisions when appropriate. This removal should be as transparent as possible, with information available in
the same scope of context as the original moderated content, linking to an incident report, the acting moderators,
and/or this document as appropriate.
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
Upon receiving a report of misconduct, the staff must review the incident and determine the following:
* What happened.
* Whether this event constitutes a code of conduct violation.
* Who the bad actor(s) was.
* Whether this is an ongoing situation.
* If any previous disiplinary measures are relevent and in effect.
* If there is a threat to anyones physical safety.
* What impact this has had on the community.
After the conduct committee has a complete account of the events they need to make a decision as to how to resolve the
case. Resolutions might include:
* Nothing (if we determine no violation occurred).
* A private reprimand from the staff to the individual(s) involved, providing clarity around the nature of the
violation, and an explanation of why the behavior was inappropriate.
* A request for a public or private apology.
* A public reprimand.
* An imposed vacation (for example, asking someone to "take a week off" from Slack).
* A permanent or temporary ban from some or all PyQtGraph spaces (Slack, github, mailing list or events) or from
communicating, in public or in private, with some are all of the members of our community.
* Contacting law enforcement.
A response must be sent within one week to the person who filed the report with either a resolution or an explanation of
why the situation is not yet resolved.
A Code of Conduct transparency report will then be published
to [the PyQtGraph users' list](https://groups.google.com/g/pyqtgraph) with anonymized information about any violations
that might have occurred. This report should be handled with care not to divulge personally identifying information
about victims, reporters, and violators, and should serve as a means to ensure that members will be comfortable
reporting violations and that our community will be kept accountable for supporting and encouraging safe spaces.
## Attribution
This Code of Conduct is adapted from
the [Contributor Covenant, version 2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html) and
the [PuPPy Code of Conduct](https://www.pspython.com/pages/code-of-conduct/).

View File

@ -1,92 +1,72 @@
# Contributing to PyQtGraph
Contributions to pyqtgraph are welcome! Be kind and respectful! See [our Code of Conduct](CODE_OF_CONDUCT.md) for details.
Contributions to pyqtgraph are welcome!
Please use the following guidelines when preparing changes:
## Development Environment Creation
## Submitting Code Changes
First thing to do is fork the repository, and clone your own fork to your local computer.
```bash
git clone https://github.com/<username>/pyqtgraph.git
cd pyqtgraph
```
While there is nothing preventing users from using `conda` environments, as a general principle, we recommend using the `venv` module for creating an otherwise empty virtual environment. Furthermore, at this time, WSL is not supported (it can likely be made to work, but you're on your own if you go down this route).
```bash
python3.9 -m venv .venv
source .venv/bin/activate
# on windows this would be .venv/Scripts/activate
python -m pip install --upgrade wheel setuptools pip
python -m pip install numpy scipy pyside6 -e .
```
Before making changes to the code-base, create a different branch with a name that should be unique (this makes it easier for maintainers to examine the proposed changes locally).
```bash
git switch -c my-new-feature
```
When you're ready to submit the pull request, do so via the github, and the target of the pull request should be the `master` branch in the pyqtgraph repo.
Pull requests should include only a focused and related set of changes. Mixed features and unrelated changes may be rejected.
For major changes, it is recommended to discuss your plans on the mailing list or in a github issue/discussion before putting in too much effort.
PyQtGraph has adopted [NEP-29](https://numpy.org/neps/nep-0029-deprecation_policy.html) which governs the timeline for phasing out support for numpy and python versions.
* The preferred method for submitting changes is by github pull request against the "develop" branch.
* Pull requests should include only a focused and related set of changes. Mixed features and unrelated changes may be rejected.
* For major changes, it is recommended to discuss your plans on the mailing list or in a github issue before putting in too much effort.
* The following deprecations are being considered by the maintainers
* `pyqtgraph.opengl` may be deprecated and replaced with `VisPy` functionality
* After v0.11, pyqtgraph will adopt [NEP-29](https://numpy.org/neps/nep-0029-deprecation_policy.html) which will effectively mean that python2 support will be deprecated
* Qt4 will be deprecated shortly, as well as Qt5<5.9 (and potentially <5.12)
## Documentation
* Writing proper documentation and unit tests is highly encouraged. PyQtGraph uses [`pytest`](https://docs.pytest.org/) for testing.
* Documentation is generated with sphinx, and usage of [numpy-docstyle](https://numpydoc.readthedocs.io/en/latest/format.html) is encouraged (many places in the library do not use this docstring style at present, it's a gradual process to migrate).
* The docs built for this PR can be previewed by clicking on the "Details" link for the read-the-docs entry in the checks section of the PR conversation page.
* Writing proper documentation and unit tests is highly encouraged. PyQtGraph uses pytest style testing, so tests should usually be included in a tests/ directory adjacent to the relevant code.
* Documentation is generated with sphinx; please check that docstring changes compile correctly
## Style guidelines
### Formatting ~~Rules~~ Suggestions
### Rules
* PyQtGraph prefers PEP8 for most style issues, but this is not enforced rigorously as long as the code is clean and readable.
* Variable and Function/Methods that are intended to be part of the public API should be camelCase.
* "Private" methods/variables should have a leading underscore (`_`) before the name.
* Use `python setup.py style` to see whether your code follows the mandatory style guidelines checked by flake8.
* Exception 1: All variable names should use camelCase rather than underscore_separation. This is done for consistency with Qt
* Exception 2: Function docstrings use ReStructuredText tables for describing arguments:
```text
============== ========================================================
**Arguments:**
argName1 (type) Description of argument
argName2 (type) Description of argument. Longer descriptions must
be wrapped within the column guidelines defined by the
"====" header and footer.
============== ========================================================
```
QObject subclasses that implement new signals should also describe
these in a similar table.
### Pre-Commit
PyQtGraph developers are highly encouraged to (but not required) to use [`pre-commit`](https://pre-commit.com/). `pre-commit` does a number of checks when attempting to commit the code to being committed, such as ensuring no large files are accidentally added, address mixed-line-endings types and so on. Check the [pre-commit documentation](https://pre-commit.com) on how to setup.
PyQtGraph developers are highly encouraged to (but not required) to use [`pre-commit`](https://pre-commit.com/). `pre-commit` does a number of checks when attempting to commit the code to ensure it conforms to various standards, such as `flake8`, utf-8 encoding pragma, line-ending fixers, and so on. If any of the checks fail, the commit will be rejected, and you will have the opportunity to make the necessary fixes before adding and committing a file again. This ensures that every commit made conforms to (most) of the styling standards that the library enforces; and you will most likely pass the code style checks by the CI.
## Testing
To make use of `pre-commit`, have it available in your `$PATH` and run `pre-commit install` from the root directory of PyQtGraph.
### Basic Setup
## Testing Setting up a test environment
### Dependencies
* tox
* tox-conda
* pytest
* pytest-cov
* pytest-xdist
* Optional: pytest-xvfb (used on linux with headless displays)
* Optional: pytest-xvfb
To run the test suite, after installing the above dependencies run
```bash
python -m pytest examples tests
```
If you have `pytest<5` (used in python2), you may also want to install `pytest-faulthandler==1.6` plugin to output extra debugging information in case of test failures. This isn't necessary with `pytest>=5`
### Tox
As PyQtGraph supports a wide array of Qt-bindings, and python versions, we make use of `tox` to test against as many supported configurations as feasible. With tox installed, simply run `tox` and it will run through all the configurations. This should be done if there is uncertainty regarding changes working on specific combinations of PyQt bindings and/or python versions.
As PyQtGraph supports a wide array of Qt-bindings, and python versions, we make use of `tox` to test against most of the configurations in our test matrix. As some of the qt-bindings are only installable via `conda`, `conda` needs to be in your `PATH`, and we utilize the `tox-conda` plugin.
### Continuous Integration
* Tests for a module should ideally cover all code in that module, i.e., statement coverage should be at 100%.
* To measure the test coverage, un `pytest --cov -n 4` to run the test suite with coverage on 4 cores.
For our Continuous Integration, we utilize Github Actions. Tested configurations are visible on [README](README.md).
### Continous Integration
### 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:
```bash
pip install asv
python setup.py asv_config
asv run
```
( TODO publish results )
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)

112
README.md
View File

@ -1,15 +1,11 @@
[![Build Status](https://pyqtgraph.visualstudio.com/pyqtgraph/_apis/build/status/pyqtgraph.pyqtgraph?branchName=develop)](https://pyqtgraph.visualstudio.com/pyqtgraph/_build/latest?definitionId=17&branchName=develop)
[![Documentation Status](https://readthedocs.org/projects/pyqtgraph/badge/?version=latest)](https://pyqtgraph.readthedocs.io/en/latest/?badge=latest)
PyQtGraph
=========
[![PyPi](https://img.shields.io/pypi/v/pyqtgraph.svg)](https://pypi.org/project/pyqtgraph/)
[![conda-forge](https://img.shields.io/conda/vn/conda-forge/pyqtgraph.svg)](https://anaconda.org/conda-forge/pyqtgraph)
[![Build Status](https://github.com/pyqtgraph/pyqtgraph/workflows/main/badge.svg)](https://github.com/pyqtgraph/pyqtgraph/actions/?query=workflow%3Amain)
[![CodeQL Status](https://github.com/pyqtgraph/pyqtgraph/workflows/codeql/badge.svg)](https://github.com/pyqtgraph/pyqtgraph/actions/?query=workflow%3Acodeql)
[![Documentation Status](https://readthedocs.org/projects/pyqtgraph/badge/?version=latest)](https://pyqtgraph.readthedocs.io/en/latest/?badge=latest)
[![Total alerts](https://img.shields.io/lgtm/alerts/g/pyqtgraph/pyqtgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/pyqtgraph/pyqtgraph/alerts/)
[![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/pyqtgraph/pyqtgraph.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/pyqtgraph/pyqtgraph/context:python)
A pure-Python graphics library for PyQt5/PyQt6/PySide2/PySide6
A pure-Python graphics library for PyQt/PySide/PyQt5/PySide2
Copyright 2020 Luke Campagnola, University of North Carolina at Chapel Hill
@ -23,104 +19,54 @@ heavy leverage of numpy for number crunching, Qt's GraphicsView framework for
Requirements
------------
PyQtGraph has adopted [NEP 29](https://numpy.org/neps/nep-0029-deprecation_policy.html).
This project supports:
* All minor versions of Python released 42 months prior to the project, and at minimum the two latest minor versions.
* All minor versions of numpy released in the 24 months prior to the project, and at minimum the last three minor versions.
* All Qt5 versions from 5.12-5.15, and Qt6 6.1
Currently this means:
* Python 3.7+
* Qt 5.12-5.15, 6.1
* [PyQt5](https://www.riverbankcomputing.com/software/pyqt/),
[PyQt6](https://www.riverbankcomputing.com/software/pyqt/),
[PySide2](https://wiki.qt.io/Qt_for_Python), or
[PySide6](https://wiki.qt.io/Qt_for_Python)
* [`numpy`](https://github.com/numpy/numpy) 1.18+
### Optional added functionalities
Through 3rd part libraries, additional functionality may be added to PyQtGraph, see the table below for a summary.
| Library | Added functionality |
|----------------|-|
| [`scipy`] | <ul><li> Image processing through [`ndimage`]</li><li> Data array filtering through [`signal`] </li><ul> |
| [`pyopengl`] | <ul><li> 3D graphics </li><li> Faster image processing </li><li>Note: on macOS Big Sur only works with python 3.9.1+</li></ul> |
| [`h5py`] | <ul><li> Export in hdf5 format </li></ul> |
| [`colorcet`] | <ul><li> Add a collection of perceptually uniform colormaps </li></ul> |
| [`matplotlib`] | <ul><li> Export of PlotItem in matplotlib figure </li><li> Add matplotlib collection of colormaps </li></ul> |
| [`cupy`] | <ul><li> CUDA-enhanced image processing </li><li> Note: On Windows, CUDA toolkit must be >= 11.1 </li></ul> |
| [`numba`] | <ul><li> Faster image processing </li></ul> |
[`scipy`]: https://github.com/scipy/scipy
[`ndimage`]: https://docs.scipy.org/doc/scipy/reference/ndimage.html
[`signal`]: https://docs.scipy.org/doc/scipy/reference/signal.html
[`pyopengl`]: https://github.com/mcfletch/pyopengl
[`h5py`]: https://github.com/h5py/h5py
[`colorcet`]: https://github.com/holoviz/colorcet
[`matplotlib`]: https://github.com/matplotlib/matplotlib
[`numba`]: https://github.com/numba/numba
[`cupy`]: https://docs.cupy.dev/en/stable/install.html
* Python 2.7, or 3.x
* Required
* PyQt 4.8+, PySide, PyQt5, or PySide2
* `numpy`
* Optional
* `scipy` for image processing
* `pyopengl` for 3D graphics
* `hdf5` for large hdf5 binary format support
Qt Bindings Test Matrix
-----------------------
The following table represents the python environments we test in our CI system. Our CI system uses Ubuntu 20.04, Windows Server 2019, and macOS 10.15 base images.
The following table represents the python environments we test in our CI system. Our CI system uses Ubuntu 18.04, Windows Server 2019, and macOS 10.15 base images.
| 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: |
| PySide2-5.15 | | :white_check_mark: | |
| PyQt5-5.15 | | :white_check_mark: | |
| PySide6-6.1 | | | :white_check_mark: |
| PyQt6-6.1 | | | :white_check_mark: |
| Qt-Bindings | Python 2.7 | Python 3.6 | Python 3.7 | Python 3.8 |
| :------------- | :----------------: | :----------------: | :----------------: | :----------------: |
| PyQt-4 | :white_check_mark: | :x: | :x: | :x: |
| PySide1 | :white_check_mark: | :x: | :x: | :x: |
| PyQt5-5.9 | :x: | :white_check_mark: | :x: | :x: |
| PySide2-5.13 | :x: | :x: | :white_check_mark: | :x: |
| PyQt5-Latest | :x: | :x: | :x: | :white_check_mark: |
| PySide2-Latest | :x: | :x: | :x: | :white_check_mark: |
* :x: - Not compatible
* :white_check_mark: - Tested
* pyqtgraph has had some incompatibilities with PySide2 versions 5.6-5.11, and we recommend you avoid those versions if possible
* on macOS with Python 2.7 and Qt4 bindings (PyQt4 or PySide) the openGL related visualizations do not work reliably
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)
Installation Methods
--------------------
* From PyPI:
* From PyPI:
* Last released version: `pip install pyqtgraph`
* Latest development version: `pip install git+https://github.com/pyqtgraph/pyqtgraph@master`
* From conda
* Last released version: `conda install -c conda-forge pyqtgraph`
* To install system-wide from source distribution: `python setup.py install`
* Many linux package repositories have release versions.
* To use with a specific project, simply copy the PyQtGraph subdirectory
* To use with a specific project, simply copy the pyqtgraph subdirectory
anywhere that is importable from your project.
Documentation
-------------
The official documentation lives at [pyqtgraph.readthedocs.io](https://pyqtgraph.readthedocs.io)
The official documentation lives at https://pyqtgraph.readthedocs.io
The easiest way to learn PyQtGraph is to browse through the examples; run `python -m pyqtgraph.examples` to launch the examples application.
Used By
-------
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](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)
* [PySpectra](http://hasyweb.desy.de/services/computing/Spock/node138.html)
* [Semi-Supervised Semantic Annotator](https://gitlab.com/ficsresearch/s3ah)
Do you use PyQtGraph in your own project, and want to add it to the list? Submit a pull request to update this listing!
The easiest way to learn pyqtgraph is to browse through the examples; run `python -m pyqtgraph.examples` to launch the examples application.

99
azure-pipelines.yml Normal file
View File

@ -0,0 +1,99 @@
trigger:
branches:
include:
- '*' # Build for all branches if they have a azure-pipelines.yml file.
tags:
include:
- 'v*' # Ensure that we are building for tags starting with 'v' (Official Versions)
# Build only for PRs for master branch
pr:
autoCancel: true
branches:
include:
- master
- develop
variables:
OFFICIAL_REPO: 'pyqtgraph/pyqtgraph'
DEFAULT_MERGE_BRANCH: 'develop'
disable.coverage.autogenerate: 'true'
stages:
- stage: "pre_test"
jobs:
- job: check_diff_size
pool:
vmImage: 'Ubuntu 18.04'
steps:
- bash: |
git config --global advice.detachedHead false
mkdir ~/repo-clone && cd ~/repo-clone
git init
git remote add -t $(Build.SourceBranchName) origin $(Build.Repository.Uri)
git remote add -t ${DEFAULT_MERGE_BRANCH} upstream https://github.com/${OFFICIAL_REPO}.git
git fetch origin $(Build.SourceBranchName)
git fetch upstream ${DEFAULT_MERGE_BRANCH}
git checkout $(Build.SourceBranchName)
MERGE_SIZE=`du -s . | sed -e "s/\t.*//"`
echo -e "Merge Size ${MERGE_SIZE}"
git checkout ${DEFAULT_MERGE_BRANCH}
TARGET_SIZE=`du -s . | sed -e "s/\t.*//"`
echo -e "Target Size ${TARGET_SIZE}"
if [ "${MERGE_SIZE}" != "${TARGET_SIZE}" ]; then
SIZE_DIFF=`expr \( ${MERGE_SIZE} - ${TARGET_SIZE} \)`;
else
SIZE_DIFF=0;
fi;
echo -e "Estimated content size difference = ${SIZE_DIFF} kB" &&
test ${SIZE_DIFF} -lt 100;
displayName: 'Diff Size Check'
continueOnError: true
- job: "style_check"
pool:
vmImage: "Ubuntu 18.04"
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.7
- bash: |
pip install flake8
python setup.py style
displayName: 'flake8 check'
continueOnError: true
- job: "build_wheel"
pool:
vmImage: 'Ubuntu 18.04'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.8
- script: |
python -m pip install setuptools wheel
python setup.py bdist_wheel --universal
displayName: "Build Python Wheel"
continueOnError: false
- publish: dist
artifact: wheel
- stage: "test"
jobs:
- template: azure-test-template.yml
parameters:
name: linux
vmImage: 'Ubuntu 18.04'
- template: azure-test-template.yml
parameters:
name: windows
vmImage: 'windows-2019'
- template: azure-test-template.yml
parameters:
name: macOS
vmImage: 'macOS-10.15'

203
azure-test-template.yml Normal file
View File

@ -0,0 +1,203 @@
# Azure Pipelines CI job template for PyDM Tests
# https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/anaconda?view=azure-devops
parameters:
name: ''
vmImage: ''
jobs:
- job: ${{ parameters.name }}
pool:
vmImage: ${{ parameters.vmImage }}
strategy:
matrix:
Python27-PyQt4-4.8:
python.version: '2.7'
qt.bindings: "pyqt=4"
install.method: "conda"
Python27-PySide-4.8:
python.version: '2.7'
qt.bindings: "pyside"
install.method: "conda"
Python36-PyQt5-5.9:
python.version: "3.6"
qt.bindings: "pyqt"
install.method: "conda"
Python37-PySide2-5.13:
python.version: "3.7"
qt.bindings: "pyside2"
install.method: "conda"
Python38-PyQt5-Latest:
python.version: '3.8'
qt.bindings: "PyQt5"
install.method: "pip"
Python38-PySide2-Latest:
python.version: '3.8'
qt.bindings: "PySide2"
install.method: "pip"
steps:
- task: DownloadPipelineArtifact@2
inputs:
source: 'current'
artifact: wheel
path: 'dist'
- task: ScreenResolutionUtility@1
inputs:
displaySettings: 'specific'
width: '1920'
height: '1080'
condition: eq(variables['agent.os'], 'Windows_NT' )
- task: UsePythonVersion@0
inputs:
versionSpec: $(python.version)
condition: eq(variables['install.method'], 'pip')
- script: |
curl -LJO https://github.com/pal1000/mesa-dist-win/releases/download/19.1.0/mesa3d-19.1.0-release-msvc.exe
7z x mesa3d-19.1.0-release-msvc.exe
cd x64
xcopy opengl32.dll C:\windows\system32\mesadrv.dll*
xcopy opengl32.dll C:\windows\syswow64\mesadrv.dll*
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DLL /t REG_SZ /d "mesadrv.dll" /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v DriverVersion /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Flags /t REG_DWORD /d 1 /f
REG ADD "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\OpenGLDrivers\MSOGL" /v Version /t REG_DWORD /d 2 /f
displayName: "Install Windows-Mesa OpenGL DLL"
condition: eq(variables['agent.os'], 'Windows_NT')
- bash: |
if [ $(agent.os) == 'Linux' ]
then
echo "##vso[task.prependpath]$CONDA/bin"
elif [ $(agent.os) == 'Darwin' ]
then
sudo chown -R $USER $CONDA
echo "##vso[task.prependpath]$CONDA/bin"
elif [ $(agent.os) == 'Windows_NT' ]
then
echo "##vso[task.prependpath]$CONDA/Scripts"
else
echo 'Just what OS are you using?'
fi
displayName: 'Add Conda To $PATH'
condition: eq(variables['install.method'], 'conda' )
continueOnError: false
- bash: |
if [ $(install.method) == "conda" ]
then
conda update --all --yes --quiet
conda create --name test-environment-$(python.version) python=$(python.version) --yes --quiet
source activate test-environment-$(python.version)
conda config --env --set always_yes true
if [ $(python.version) == '2.7' ]
then
conda config --set restore_free_channel true
fi
if [ $(qt.bindings) == "pyside2" ] || ([ $(qt.bindings) == 'pyside' ] && [ $(agent.os) == 'Darwin' ])
then
conda config --prepend channels conda-forge
fi
conda info
conda install $(qt.bindings) numpy scipy pyopengl h5py six --yes --quiet
else
pip install $(qt.bindings) numpy scipy pyopengl h5py six
fi
pip install pytest pytest-cov coverage pytest-xdist
if [ $(python.version) == "2.7" ]
then
pip install pytest-faulthandler==1.6.0
export PYTEST_ADDOPTS="--faulthandler-timeout=15"
else
pip install pytest pytest-cov coverage
fi
displayName: "Install Dependencies"
- bash: |
if [ $(install.method) == "conda" ]
then
source activate test-environment-$(python.version)
fi
python -m pip install --no-index --find-links=dist pyqtgraph
displayName: 'Install Wheel'
- bash: |
sudo apt-get install -y libxkbcommon-x11-dev
# workaround for QTBUG-84489
sudo apt-get install -y libxcb-xfixes0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-xinerama0
if [ $(install.method) == "conda" ]
then
source activate test-environment-$(python.version)
fi
pip install PyVirtualDisplay==0.2.5 pytest-xvfb
displayName: "Virtual Display Setup"
condition: eq(variables['agent.os'], 'Linux' )
- bash: |
export QT_DEBUG_PLUGINS=1
if [ $(install.method) == "conda" ]
then
source activate test-environment-$(python.version)
fi
echo python location: `which python`
echo python version: `python --version`
echo pytest location: `which pytest`
echo installed packages
pip list
echo pyqtgraph system info
python -c "import pyqtgraph as pg; pg.systemInfo()"
echo display information
if [ $(agent.os) == 'Linux' ]
then
export DISPLAY=:99.0
Xvfb :99 -screen 0 1920x1200x24 -ac +extension GLX +render -noreset &
sleep 3
fi
python -m pyqtgraph.util.get_resolution
echo openGL information
python -c "from pyqtgraph.opengl.glInfo import GLTest"
displayName: 'Debug Info'
continueOnError: false
- bash: |
if [ $(install.method) == "conda" ]
then
source activate test-environment-$(python.version)
fi
mkdir -p "$SCREENSHOT_DIR"
# echo "If Screenshots are generated, they may be downloaded from:"
# echo "https://dev.azure.com/pyqtgraph/pyqtgraph/_apis/build/builds/$(Build.BuildId)/artifacts?artifactName=Screenshots&api-version=5.0"
pytest . -v \
-n 1 \
--junitxml=junit/test-results.xml \
--cov pyqtgraph --cov-report=xml --cov-report=html
displayName: 'Unit tests'
env:
AZURE: 1
SCREENSHOT_DIR: $(Build.ArtifactStagingDirectory)/screenshots
- task: PublishBuildArtifacts@1
displayName: 'Publish Screenshots'
condition: failed()
inputs:
pathtoPublish: $(Build.ArtifactStagingDirectory)/screenshots
artifactName: Screenshots
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs:
testResultsFiles: '**/test-*.xml'
testRunTitle: 'Test Results for $(agent.os) - $(python.version) - $(qt.bindings) - $(install.method)'
publishRunAttachments: true
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov'

View File

@ -1 +0,0 @@

View File

@ -1,30 +0,0 @@
import numpy as np
import pyqtgraph as pg
rng = np.random.default_rng(12345)
class _TimeSuite:
params = ([10_000, 100_000, 1_000_000], ['all', 'finite', 'pairs', 'array'])
def setup(self, nelems, connect):
self.xdata = np.arange(nelems, dtype=np.float64)
self.ydata = rng.standard_normal(nelems, dtype=np.float64)
if connect == 'array':
self.connect_array = np.ones(nelems, dtype=bool)
if self.have_nonfinite:
self.ydata[::5000] = np.nan
def time_test(self, nelems, connect):
if connect == 'array':
connect = self.connect_array
pg.arrayToQPath(self.xdata, self.ydata, connect=connect)
class TimeSuiteAllFinite(_TimeSuite):
def __init__(self):
super().__init__()
self.have_nonfinite = False
class TimeSuiteWithNonFinite(_TimeSuite):
def __init__(self):
super().__init__()
self.have_nonfinite = True

View File

@ -1,139 +0,0 @@
# -*- coding: utf-8 -*-
import numpy as np
import pyqtgraph as pg
try:
import cupy as cp
pg.setConfigOption("useCupy", True)
except ImportError:
cp = None
try:
import numba
except ImportError:
numba = None
def renderQImage(*args, **kwargs):
imgitem = pg.ImageItem(axisOrder='row-major')
if 'autoLevels' not in kwargs:
kwargs['autoLevels'] = False
imgitem.setImage(*args, **kwargs)
imgitem.render()
def prime_numba():
shape = (64, 64)
lut_small = np.random.randint(256, size=(256, 3), dtype=np.uint8)
lut_big = np.random.randint(256, size=(512, 3), dtype=np.uint8)
for lut in [lut_small, lut_big]:
renderQImage(np.zeros(shape, dtype=np.uint8), levels=(20, 220), lut=lut)
renderQImage(np.zeros(shape, dtype=np.uint16), levels=(250, 3000), lut=lut)
renderQImage(np.zeros(shape, dtype=np.float32), levels=(-4.0, 4.0), lut=lut)
class _TimeSuite(object):
def __init__(self):
super(_TimeSuite, self).__init__()
self.size = None
self.float_data = None
self.uint8_data = None
self.uint8_lut = None
self.uint16_data = None
self.uint16_lut = None
self.cupy_uint16_lut = None
self.cupy_uint8_lut = None
def setup(self):
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
)
if numba is not None:
# ensure JIT compilation
pg.setConfigOption("useNumba", True)
prime_numba()
pg.setConfigOption("useNumba", False)
if cp:
_d1, _d2, _d3, self.cupy_uint16_lut, self.cupy_uint8_lut = self._create_data(size, cp)
renderQImage(cp.asarray(self.uint16_data["data"])) # prime the gpu
@property
def numba_uint16_lut(self):
return self.uint16_lut
@property
def numba_uint8_lut(self):
return self.uint8_lut
@property
def numpy_uint16_lut(self):
return self.uint16_lut
@property
def numpy_uint8_lut(self):
return self.uint8_lut
@staticmethod
def _create_data(size, xp):
float_data = {
"data": xp.random.normal(size=size).astype("float32"),
"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, kind, 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, f"{kind}_{lut_name}_lut", None) if lut_name is not None else None
pg.setConfigOption("useNumba", kind == "numba")
img_data = data["data"]
if kind == "cupy":
img_data = cp.asarray(img_data)
renderQImage(img_data, lut=lut, levels=levels)
time_test.__name__ = func_name
return time_test
for option in ["cupy", "numba", "numpy"]:
if option == "cupy" and cp is None:
continue
if option == "numba" and numba is None:
continue
for data_type in ["float", "uint16", "uint8"]:
for lvls in [True, False]:
if data_type == "float" and not lvls:
continue
for lutname in [None, "uint8", "uint16"]:
name = (
f'time_1x_renderImageItem_{option}_{data_type}_{"" if lvls else "no"}levels_{lutname or "no"}lut'
)
setattr(_TimeSuite, name, make_test(data_type, option, lvls, lutname, name))
class Time4096Suite(_TimeSuite):
def __init__(self):
super(Time4096Suite, self).__init__()
self.size = 4096

View File

@ -1,5 +1,5 @@
pyside2
numpy
pyopengl
sphinx==4.1.1
sphinx_rtd_theme==0.5.2
sphinx
sphinx_rtd_theme

View File

@ -1,7 +0,0 @@
GLGraphItem
===========
.. autoclass:: pyqtgraph.opengl.GLGraphItem
:members:
.. automethod:: pyqtgraph.opengl.GLGraphItem.__init__

View File

@ -19,7 +19,6 @@ Contents:
glviewwidget
glgriditem
glgraphitem
glsurfaceplotitem
glvolumeitem
glimageitem

View File

@ -12,7 +12,7 @@ Contents:
widgets/index
3dgraphics/index
colormap
parametertree/apiref
parametertree/index
dockarea
graphicsscene/index
flowchart/index

View File

@ -1,84 +1,8 @@
Color Maps
==========
A color map defines a relationship between scalar data values and a range of colors. Color maps are
commonly used to generate false color images, color scatter-plot points, and illustrate the height
of surface plots.
PyQtGraph's :class:`~pyqtgraph.ColorMap` can conveniently be applied to images and interactively
adjusted by using :class:`~pyqtgraph.ColorBarItem`.
To provide interactively user-defined color mappings, see
:class:`~pyqtgraph.GradientEditorItem` and :class:`~pyqtgraph.GradientWidget`, which wraps it.
:class:`~pyqtgraph.GradientEditorItem` combines the editing with a histogram and controls for
interactively adjusting image levels.
ColorMap can also be used a convenient source of colors from a consistent palette or to generate
QPen and QBrush objects used to draw lines and fills that are colored according to their values
along the horizontal or vertical axis.
Sources for color maps
----------------------
Color maps can be user defined by assigning a number of *stops* over the range of 0 to 1. A color
is given for each stop, and the in-between values are generated by interpolation.
When map colors directly represent values, an improperly designed map can obscure detail over
certain ranges of values, while creating false detail in others. PyQtGraph includes the
perceptually uniform color maps provided by the
`Colorcet project <https://colorcet.holoviz.org/>`_. Color maps can also be imported from the
``colorcet`` library or from ``matplotlib``, if either of these is installed.
To see all available color maps, please run the `ColorMap` demonstration available in the suite of
:ref:`examples`.
Examples
--------
False color display of a 2D data set. Display levels are controlled by
a :class:`ColorBarItem <pyqtgraph.ColorBarItem>`:
.. literalinclude:: images/gen_example_false_color_image.py
:lines: 18-28
:dedent: 8
Using QtGui.QPen and QtGui.QBrush to color plots according to the plotted value:
.. literalinclude:: images/gen_example_gradient_plot.py
:lines: 16-33
:dedent: 8
.. image::
images/example_false_color_image.png
:width: 49%
:alt: Example of a false color image
.. image::
images/example_gradient_plot.png
:width: 49%
:alt: Example of drawing and filling plots with gradients
The use of color maps is also demonstrated in the `ImageView`, `Color Gradient Plots` and `ColorBarItem`
:ref:`examples`.
API Reference
-------------
.. autofunction:: pyqtgraph.colormap.listMaps
.. autofunction:: pyqtgraph.colormap.get
.. autofunction:: pyqtgraph.colormap.getFromMatplotlib
.. autofunction:: pyqtgraph.colormap.getFromColorcet
.. autofunction:: pyqtgraph.colormap.modulatedBarData
ColorMap
========
.. autoclass:: pyqtgraph.ColorMap
:members:
.. automethod:: pyqtgraph.ColorMap.__init__

View File

@ -11,7 +11,6 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import time
import sys
import os
from datetime import datetime
@ -31,7 +30,7 @@ import pyqtgraph
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon']
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@ -47,10 +46,7 @@ master_doc = 'index'
# General information about the project.
project = 'pyqtgraph'
now = datetime.utcfromtimestamp(
int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
)
copyright = '2011 - {}, Luke Campagnola'.format(now.year)
copyright = '2011 - {}, Luke Campagnola'.format(datetime.now().year)
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@ -139,7 +135,7 @@ html_static_path = ['_static']
# add the theme customizations
def setup(app):
app.add_css_file("custom.css")
app.add_stylesheet("custom.css")
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.

View File

@ -26,9 +26,10 @@ 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.
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.
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.
enableExperimental bool False Enable experimental features (the curious can search for this key in the code).
crashWarning bool False If True, print warnings about situations that may result in a crash.
================== =================== ================== ================================================================================

View File

@ -41,33 +41,23 @@ Export Formats
Exporting from the API
----------------------
To export a file programatically, follow this example:
To export a file programatically, follow this example::
.. code-block:: python
import pyqtgraph as pg
import pyqtgraph.exporters
import pyqtgraph as pg
import pyqtgraph.exporters
# generate something to export
plt = pg.plot([1,5,2,4,3])
# generate something to export
plt = pg.plot([1,5,2,4,3])
# create an exporter instance, as an argument give it
# the item you wish to export
exporter = pg.exporters.ImageExporter(plt.plotItem)
# create an exporter instance, as an argument give it
# the item you wish to export
exporter = pg.exporters.ImageExporter(plt.plotItem)
# set export parameters if needed
exporter.parameters()['width'] = 100 # (note this also affects height parameter)
# set export parameters if needed
exporter.parameters()['width'] = 100 # (note this also affects height parameter)
# save to file
exporter.export('fileName.png')
To export the overall layout of a GraphicsLayoutWidget `grl`, the exporter initialization is
.. code-block:: python
exporter = pg.exporters.ImageExporter( grl.scene() )
instead.
# save to file
exporter.export('fileName.png')
Exporting 3D Graphics
@ -79,3 +69,5 @@ generate an image from a GLViewWidget by using QGLWidget.grabFrameBuffer or QGLW
glview.grabFrameBuffer().save('fileName.png')
See the Qt documentation for more information.

View File

@ -40,7 +40,7 @@ In the example above, each terminal is defined by a dictionary of options which
Once the flowchart is created, add its control widget to your application::
ctrl = fc.widget()
ctrl = fc.ctrlWidget()
myLayout.addWidget(ctrl) ## read Qt docs on QWidget and layouts for more information
The control widget provides several features:

View File

@ -30,11 +30,11 @@ Qt uses the classes QColor, QPen, and QBrush to determine how to draw lines and
.. autofunction:: pyqtgraph.intColor
.. autofunction:: pyqtgraph.CIELabColor
.. autofunction:: pyqtgraph.colorTuple
.. autofunction:: pyqtgraph.colorCIELab
.. autofunction:: pyqtgraph.colorStr
.. autofunction:: pyqtgraph.colorDistance
.. autofunction:: pyqtgraph.glColor
Data Slicing
@ -65,8 +65,6 @@ SI Unit Conversion Functions
.. autofunction:: pyqtgraph.siEval
.. autofunction:: pyqtgraph.siParse
Image Preparation Functions
---------------------------
@ -104,14 +102,3 @@ Miscellaneous Functions
.. autofunction:: pyqtgraph.exit
Legacy Color Helper Functions
-------------------------------
The following helper functions should no longer be used. The functionality that they implement is trivial and it is suggested that the user use the equivalent QColor methods directly.
.. autofunction:: pyqtgraph.colorTuple
.. autofunction:: pyqtgraph.colorStr
.. autofunction:: pyqtgraph.glColor

View File

@ -1,7 +0,0 @@
ColorBarItem
============
.. autoclass:: pyqtgraph.ColorBarItem
:members:
.. automethod:: pyqtgraph.ColorBarItem.__init__

View File

@ -1,66 +1,8 @@
ImageItem
=========
:class:`~pyqtgraph.ImageItem` displays images inside a :class:`~pyqtgraph.GraphicsView`, or a
:class:`~pyqtgraph.ViewBox`, which may itself be part of a :class:`~pyqtgraph.PlotItem`. It is designed
for rapid updates as needed for a video display. The supplied data is optionally scaled (see
:func:`~pyqtgraph.ImageItem.setLevels`) and/or colored according to a
lookup table (see :func:`~pyqtgraph.ImageItem.setLookupTable`.
Data is provided as a NumPy array with an ordering of either
* `col-major`, where the shape of the array represents (width, height) or
* `row-major`, where the shape of the array represents (height, width).
While `col-major` is the default, `row-major` ordering typically has the best performance. In either ordering,
a third dimension can be added to the array to hold individual
``[R,G,B]`` or ``[R,G,B,A]`` components.
Notes
-----
Data ordering can be set for each ImageItem, or in the :ref:`global configuration options <apiref_config>` by ::
pyqtgraph.setConfigOption('imageAxisOrder', 'row-major') # best performance
An image can be placed into a plot area of a given extent directly through the
:func:`~pyqtgraph.ImageItem.setRect` method or the ``rect`` keyword. This is internally realized through
assigning a ``QtGui.QTransform``. For other translation, scaling or rotations effects that
persist for all later image data, the user can also directly define and assign such a
transform, as shown in the example below.
ImageItem is frequently used in conjunction with :class:`~pyqtgraph.ColorBarItem` to provide
a color map display and interactive level adjustments, or with
:class:`~pyqtgraph.HistogramLUTItem` or :class:`~pyqtgraph.HistogramLUTWidget` for a full GUI
to control the levels and lookup table used to display the image.
If performance is critial, the following points may be worth investigating:
* Use row-major ordering and C-contiguous image data.
* Manually provide ``level`` information to avoid autoLevels sampling of the image.
* Prefer `float32` to `float64` for floating point data, avoid NaN values.
* Use lookup tables with <= 256 entries for false color images.
* Avoid individual level adjustments RGB components.
* Use the latest version of NumPy. Notably, SIMD code added in version 1.20 significantly improved performance on Linux platforms.
* Enable Numba with ``pyqtgraph.setConfigOption('useNumba', True)``, although the JIT compilation will only accelerate repeated image display.
.. _ImageItem_examples:
Examples
--------
.. literalinclude:: ../images/gen_example_imageitem_transform.py
:lines: 19-28
:dedent: 8
.. image::
../images/example_imageitem_transform.png
:width: 49%
:alt: Example of transformed image display
.. autoclass:: pyqtgraph.ImageItem
:members:
.. automethod:: pyqtgraph.ImageItem.__init__

View File

@ -12,15 +12,12 @@ Contents:
plotdataitem
plotitem
imageitem
colorbaritem
pcolormeshitem
graphitem
viewbox
linearregionitem
infiniteline
roi
graphicslayout
multiplotitem
plotcurveitem
scatterplotitem
isocurveitem
@ -47,4 +44,3 @@ Contents:
uigraphicsitem
graphicswidgetanchor
dateaxisitem
targetitem

View File

@ -11,7 +11,6 @@ GraphicsWidget
GridItem
HistogramLUTItem
ImageItem
PColorMeshItem
InfiniteLine
LabelItem
LinearRegionItem

View File

@ -1,5 +0,0 @@
MultiPlotItem
=============
.. autoclass:: pyqtgraph.MultiPlotItem
:members:

View File

@ -1,8 +0,0 @@
PColorMeshItem
==============
.. autoclass:: pyqtgraph.PColorMeshItem
:members:
.. automethod:: pyqtgraph.PColorMeshItem.__init__

View File

@ -1,17 +0,0 @@
TargetItem
==========
.. autoclass:: pyqtgraph.TargetItem
:members:
.. automethod:: pyqtgraph.TargetItem.__init__
TargetLabel
==================
.. autoclass:: pyqtgraph.TargetLabel
:members:
.. automethod:: pyqtgraph.TargetLabel.__init__

View File

@ -51,29 +51,7 @@ For the serious application developer, all of the functionality in pyqtgraph is
#. Under "Header file", enter "pyqtgraph".
#. Click "Add", then click "Promote".
See the designer documentation for more information on promoting widgets. The "VideoSpeedTest" and "ScatterPlotSpeedTest" examples both demonstrate the use of .ui files that are compiled to .py modules using pyuic5 or pyside-uic. The "designerExample" example demonstrates dynamically generating python classes from .ui files (no pyuic5 / pyside-uic needed).
HiDPI Displays
--------------
PyQtGraph has a method :func:`mkQApp <pyqtgraph.Qt.mkQApp>` that by default sets what we have tested to be the best combination of options to support hidpi displays, when in combination with non-hidpi secondary displays. For your application, you may have instantiated ``QApplication`` yourself, in which case we advise setting these options *before* runing ``QApplication.exec_()``.
For Qt6 bindings, this functionally "just works" without having to set any attributes.
On Versions of Qt >= 5.14 and < 6; you can get ideal behavior with the following lines::
os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1"
QApplication.setHighDpiScaleFactorRoundingPolicy(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
If you are on Qt >= 5.6 and < 5.14; you can get near ideal behavior with the following lines::
QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps)
With the later, ideal behavior was not achieved.
.. autofunction:: pyqtgraph.Qt.mkQApp
See the designer documentation for more information on promoting widgets. The "VideoSpeedTest" and "ScatterPlotSpeedTest" examples both demonstrate the use of .ui files that are compiled to .py modules using pyuic4 or pyside-uic. The "designerExample" example demonstrates dynamically generating python classes from .ui files (no pyuic4 / pyside-uic needed).
PyQt and PySide
@ -83,16 +61,15 @@ PyQtGraph supports two popular python wrappers for the Qt library: PyQt and PySi
APIs and functionality, but for various reasons (discussed elsewhere) you may prefer to use one package or the other. When
pyqtgraph is first imported, it automatically determines which library to use by making the fillowing checks:
#. If PyQt5 is already imported, use that
#. Else, if PySide2 is already imported, use that
#. Else, if PySide6 is already imported, use that
#. Else, if PyQt6 is already imported, use that
#. Else, attempt to import PyQt5, PySide2, PySide6, PyQt6, in that order.
#. If PyQt4 is already imported, use that
#. Else, if PySide is already imported, use that
#. Else, attempt to import PyQt4
#. If that import fails, attempt to import PySide.
If you have both libraries installed on your system and you wish to force pyqtgraph to use one or the other, simply
make sure it is imported before pyqtgraph::
import PySide2 ## this will force pyqtgraph to use PySide2 instead of PyQt5
import PySide ## this will force pyqtgraph to use PySide instead of PyQt4
import pyqtgraph as pg
@ -101,11 +78,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.
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 preferable 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.
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.
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.
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.
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::
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::
my_project/
__init__.py
@ -115,32 +92,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 repository inside the project, with a directory name that distinguishes it from a system-wide installation::
To embed a specific version of pyqtgraph, we would clone the pyqtgraph-core repository inside the project::
my_project$ git clone https://github.com/pyqtgraph/pyqtgraph.git local_pyqtgraph
my_project$ git clone https://github.com/pyqtgraph/pyqtgraph-core.git
Then adjust the import statements accordingly::
my_project/
__init__.py
local_pyqtgraph/
pyqtgraph/
plotting.py
"""Plotting functions used by this package"""
import local_pyqtgraph.pyqtgraph as pg # be sure to use the local subpackage
# rather than any globally-installed
# version.
import my_project.pyqtgraph as pg # be sure to use the local subpackage
# rather than any globally-installed
# versions.
def my_plot_function(*data):
pg.plot(*data)
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.
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).
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 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 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 commit -m "Added pyqtgraph to project repository"
See the ``git subtree`` documentation for more information.

View File

@ -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 <pyqtgraph.ViewBox>` or :class:`GraphicsView <pyqtgraph.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.
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.
For more information, see the classes listed above and the 'VideoSpeedTest', 'ImageItem', 'ImageView', and 'HistogramLUT' :ref:`examples`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,45 +0,0 @@
"""
generates 'example_false_color_image.png'
"""
import numpy as np
import pyqtgraph as pg
import pyqtgraph.exporters as exp
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, mkQApp
class MainWindow(pg.GraphicsLayoutWidget):
""" example application main window """
def __init__(self):
super().__init__()
self.resize(420,400)
self.show()
plot = self.addPlot() # title="non-interactive")
# prepare demonstration data:
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) )
# Example: False color image with interactive level adjustment
img = pg.ImageItem(image=noisy_data) # create monochrome image from demonstration data
plot.addItem( img ) # add to PlotItem 'plot'
cm = pg.colormap.get('CET-L9') # prepare a linear color map
bar = pg.ColorBarItem( values= (0, 20_000), cmap=cm ) # prepare interactive color bar
# Have ColorBarItem control colors of img and appear in 'plot':
bar.setImageItem( img, insert_in=plot )
self.timer = pg.QtCore.QTimer( singleShot=True )
self.timer.timeout.connect(self.export)
self.timer.start(100)
def export(self):
print('exporting')
exporter = exp.ImageExporter(self.scene())
exporter.parameters()['width'] = 420
exporter.export('example_false_color_image.png')
mkQApp("False color image example")
main_window = MainWindow()
## Start Qt event loop
if __name__ == '__main__':
pg.exec()

View File

@ -1,55 +0,0 @@
"""
generates 'example_gradient_plot.png'
"""
import numpy as np
import pyqtgraph as pg
import pyqtgraph.exporters as exp
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, mkQApp
class MainWindow(pg.GraphicsLayoutWidget):
""" example application main window """
def __init__(self):
super().__init__()
self.resize(420,400)
self.show()
# Prepare demonstration data
raw = np.linspace(0.0, 2.0, 400)
y_data1 = ( (raw+0.1)%1 ) ** 4
y_data2 = ( (raw+0.1)%1 ) ** 4 - ( (raw+0.6)%1 ) ** 4
# Example 1: Gradient pen
cm = pg.colormap.get('CET-L17') # prepare a linear color map
cm.reverse() # reverse it to put light colors at the top
pen = cm.getPen( span=(0.0,1.0), width=5 ) # gradient from blue (y=0) to white (y=1)
# plot a curve drawn with a pen colored according to y value:
curve1 = pg.PlotDataItem( y=y_data1, pen=pen )
# Example 2: Gradient brush
cm = pg.colormap.get('CET-D1') # prepare a diverging color map
cm.setMappingMode('diverging') # set mapping mode
brush = cm.getBrush( span=(-1., 1.) ) # gradient from blue at -1 to red at +1
# plot a curve that is filled to zero with the gradient brush:
curve2 = pg.PlotDataItem( y=y_data2, pen='w', brush=brush, fillLevel=0.0 )
for idx, curve in enumerate( (curve1, curve2) ):
plot = self.addPlot(row=idx, col=0)
plot.getAxis('left').setWidth(25)
plot.addItem( curve )
self.timer = pg.QtCore.QTimer( singleShot=True )
self.timer.timeout.connect(self.export)
self.timer.start(100)
def export(self):
print('exporting')
exporter = exp.ImageExporter(self.scene())
exporter.parameters()['width'] = 420
exporter.export('example_gradient_plot.png')
mkQApp("Gradient plotting example")
main_window = MainWindow()
## Start Qt event loop
if __name__ == '__main__':
pg.exec()

View File

@ -1,45 +0,0 @@
"""
generates 'example_false_color_image.png'
"""
import numpy as np
import pyqtgraph as pg
import pyqtgraph.exporters as exp
from pyqtgraph.Qt import QtGui, mkQApp
class MainWindow(pg.GraphicsLayoutWidget):
""" example application main window """
def __init__(self):
super().__init__()
self.resize(420,400)
self.show()
plot = self.addPlot()
# Example: Transformed display of ImageItem
tr = QtGui.QTransform() # prepare ImageItem transformation:
tr.scale(6.0, 3.0) # scale horizontal and vertical axes
tr.translate(-1.5, -1.5) # move 3x3 image to locate center at axis origin
img = pg.ImageItem( image=np.eye(3), levels=(0,1) ) # create example image
img.setTransform(tr) # assign transform
plot.addItem( img ) # add ImageItem to PlotItem
plot.showAxes(True) # frame it with a full set of axes
plot.invertY(True) # vertical axis counts top to bottom
self.timer = pg.QtCore.QTimer( singleShot=True )
self.timer.timeout.connect(self.export)
self.timer.start(100)
def export(self):
print('exporting')
exporter = exp.ImageExporter(self.scene())
exporter.parameters()['width'] = 420
exporter.export('example_imageitem_transform.png')
mkQApp("ImageItem transform example")
main_window = MainWindow()
## Start Qt event loop
if __name__ == '__main__':
pg.exec()

View File

@ -3,8 +3,8 @@ Installation
PyQtGraph depends on:
* Python 3.7+
* A Qt library such as PyQt5, or PySide2
* Python 2.7 or Python 3.x
* A Qt library such as PyQt4, PyQt5, PySide, or PySide2
* numpy
The easiest way to meet these dependencies is with ``pip`` or with a scientific
@ -49,9 +49,9 @@ To get access to the very latest features and bugfixes you have three choices:
2. Directly install from GitHub repo::
$ pip install git+git://github.com/pyqtgraph/pyqtgraph.git@master
$ pip install git+git://github.com/pyqtgraph/pyqtgraph.git@develop
You can change ``master`` of the above command to the branch name or the
You can change ``develop`` of the above command to the branch name or the
commit you prefer.
3. You can simply place the pyqtgraph folder someplace importable, such as

View File

@ -45,13 +45,12 @@ Examples
--------
PyQtGraph includes an extensive set of examples that can be accessed by
running either ``python -m pyqtgraph.examples`` [#editableInstall]_ or ::
running::
import pyqtgraph.examples
pyqtgraph.examples.run()
Or, if the project repository is local, you can run``python examples/`` from
the source root.
Or by running ``python examples/`` from the source root.
This will start a launcher with a list of available examples. Select an item
from the list to view its source code and double-click an item to run the
@ -85,8 +84,3 @@ How does it compare to...
(My experience with these libraries is somewhat outdated; please correct me if
I am wrong here)
.. rubric:: Footnotes
.. [#editableInstall] This method does not work when pyqtgraph is installed in editable mode.

View File

@ -3,22 +3,17 @@
Parameter Trees
===============
.. currentmodule:: pyqtgraph.parametertree
Parameter trees are a system for handling hierarchies of parameters while automatically generating one or more GUIs to display and interact with the parameters.
This feature is commonly seen, for example, in user interface design applications which display a list of editable properties for each widget.
Parameters generally have a name, a data type (int, float, string, color, etc), and a value matching the data type. Parameters may be grouped and nested to form hierarchies and may be subclassed to provide custom behavior and display widgets.
Parameters generally have a name, a data type (int, float, string, color, etc), and a value matching the data type. Parameters may be grouped and nested
to form hierarchies and may be subclassed to provide custom behavior and display widgets.
PyQtGraph's parameter tree system works similarly to the model-view architecture used by some components of Qt:
PyQtGraph's parameter tree system works similarly to the model-view architecture used by some components of Qt: Parameters are purely data-handling classes
that exist independent of any graphical interface. A ParameterTree is a widget that automatically generates a graphical interface which represents
the state of a haierarchy of Parameter objects and allows the user to edit the values within that hierarchy. This separation of data (model) and graphical
interface (view) allows the same data to be represented multiple times and in a variety of different ways.
- A :class:`Parameter` is a purely data-handling class that exists independent of any graphical interface.
- A :class:`ParameterItem` is an interactive graphical representation of a :class:`Parameter`.
- A :class:`ParameterTree` is a widget that automatically generates a graphical interface which represents the state of a hierarchy of Parameter objects and allows the user to edit the values within that hierarchy.
This separation of data (model) and graphical interface (view) allows the same data to be represented multiple times and in a variety of different ways.
For example, a floating point number parameter could be represented by a slider or a spinbox, or both.
For more information, see the 'parametertree' example included with pyqtgraph and the API reference:
For more information, see the 'parametertree' example included with pyqtgraph and the API reference
.. toctree::
:maxdepth: 2

View File

@ -1,11 +1,8 @@
Parameter
=========
.. autofunction:: pyqtgraph.parametertree.registerParameterType
.. autofunction:: pyqtgraph.parametertree.registerParameterItemType
.. autoclass:: pyqtgraph.parametertree.Parameter
:members:
.. automethod:: pyqtgraph.parametertree.Parameter.__init__

View File

@ -1,104 +1,6 @@
..
This file is auto-generated from pyqtgraph/tools/rebuildPtreeRst.py. Do not modify by hand! Instead, rerun the
generation script with `python pyqtgraph/tools/rebuildPtreeRst.py`.
Built-in Parameter Types
========================
.. currentmodule:: pyqtgraph.parametertree.parameterTypes
.. automodule:: pyqtgraph.parametertree.parameterTypes
:members:
Parameters
----------
.. autoclass:: ActionParameter
:members:
.. autoclass:: CalendarParameter
:members:
.. autoclass:: ChecklistParameter
:members:
.. autoclass:: ColorMapParameter
:members:
.. autoclass:: ColorParameter
:members:
.. autoclass:: FileParameter
:members:
.. autoclass:: FontParameter
:members:
.. autoclass:: GroupParameter
:members:
.. autoclass:: ListParameter
:members:
.. autoclass:: PenParameter
:members:
.. autoclass:: ProgressBarParameter
:members:
.. autoclass:: SimpleParameter
:members:
.. autoclass:: SliderParameter
:members:
.. autoclass:: TextParameter
:members:
ParameterItems
--------------
.. autoclass:: ActionParameterItem
:members:
.. autoclass:: BoolParameterItem
:members:
.. autoclass:: CalendarParameterItem
:members:
.. autoclass:: ChecklistParameterItem
:members:
.. autoclass:: ColorMapParameterItem
:members:
.. autoclass:: ColorParameterItem
:members:
.. autoclass:: FileParameterItem
:members:
.. autoclass:: FontParameterItem
:members:
.. autoclass:: GroupParameterItem
:members:
.. autoclass:: ListParameterItem
:members:
.. autoclass:: NumericParameterItem
:members:
.. autoclass:: PenParameterItem
:members:
.. autoclass:: ProgressBarParameterItem
:members:
.. autoclass:: SliderParameterItem
:members:
.. autoclass:: StrParameterItem
:members:
.. autoclass:: TextParameterItem
:members:

View File

@ -17,7 +17,7 @@ PyQtGraph fits into this scheme by providing its own QWidget subclasses to be in
Example::
from PyQt5 import QtGui # (the example applies equally well to PySide2)
from PyQt4 import QtGui # (the example applies equally well to PySide)
import pyqtgraph as pg
## Always start by initializing Qt (only once per application)
@ -99,3 +99,4 @@ QTimer, Multi-Threading
Multi-threading vs Multi-processing in Qt
-----------------------------------------

View File

@ -16,7 +16,7 @@ To select a 2D region from an image, pyqtgraph uses the :class:`ROI <pyqtgraph.R
To automatically extract a region of image data using an ROI and an ImageItem, use :func:`ROI.getArrayRegion <pyqtgraph.ROI.getArrayRegion>`. ROI classes use the :func:`affineSlice <pyqtgraph.affineSlice>` function to perform this extraction.
ROI can also be used as a control for moving/rotating/scaling items in a scene similar to most vector graphics editing applications.
ROI can also be used as a control for moving/rotating/scaling items in a scene similar to most vetctor graphics editing applications.
See the ROITypes example for more information.

View File

@ -15,7 +15,7 @@ from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
app = pg.mkQApp("Arrow Example")
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
cw = pg.GraphicsLayoutWidget()
@ -30,7 +30,7 @@ p2 = cw.addPlot(row=1, col=0)
## variety of arrow shapes
a1 = pg.ArrowItem(angle=-160, tipAngle=60, headLen=40, tailLen=40, tailWidth=20, pen={'color': 'w', 'width': 3})
a2 = pg.ArrowItem(angle=-120, tipAngle=30, baseAngle=20, headLen=40, tailLen=40, tailWidth=8, pen=None, brush='y')
a3 = pg.ArrowItem(angle=-60, baseAngle=20, headLen=40, headWidth=20, tailLen=None, brush=None)
a3 = pg.ArrowItem(angle=-60, tipAngle=30, baseAngle=20, headLen=40, tailLen=None, brush=None)
a4 = pg.ArrowItem(angle=-20, tipAngle=30, baseAngle=-30, headLen=40, tailLen=None)
a2.setPos(10,0)
a3.setPos(20,0)
@ -50,5 +50,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -34,5 +34,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -18,5 +18,9 @@ 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__':
pg.exec()
import sys
if sys.flags.interactive != 1 or not hasattr(QtCore, 'PYQT_VERSION'):
pg.QtGui.QApplication.exec_()

View File

@ -1,100 +0,0 @@
# -*- 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), colorMap=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),
colorMap=cmap,
label='horizontal color bar',
limits = (0, None),
rounding=1000,
orientation = 'h',
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,
colorMap=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__':
pg.exec()

View File

@ -11,7 +11,7 @@ import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
app = pg.mkQApp("ColorButton Example")
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
btn = pg.ColorButton()
win.setCentralWidget(btn)
@ -26,5 +26,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,147 +0,0 @@
# -*- coding: utf-8 -*-
"""
This example demonstrates plotting with color gradients.
It also shows multiple plots with timed rolling updates
"""
# Add path to library (just for examples; you do not need this)
import initExample
import numpy as np
import time
from pyqtgraph.Qt import QtCore, QtGui, QtWidgets, mkQApp
import pyqtgraph as pg
class DataSource(object):
""" source of buffered demonstration data """
def __init__(self, sample_rate=200., signal_period=0.55, negative_period=None, max_length=300):
""" prepare, but don't start yet """
self.rate = sample_rate
self.period = signal_period
self.neg_period = negative_period
self.start_time = 0.
self.sample_idx = 0 # number of next sample to be taken
def start(self, timestamp):
""" start acquiring simulated data """
self.start_time = timestamp
self.sample_idx = 0
def get_data(self, timestamp, max_length=6000):
""" return all data acquired since last get_data call """
next_idx = int( (timestamp - self.start_time) * self.rate )
if next_idx - self.sample_idx > max_length:
self.sample_idx = next_idx - max_length # catch up if needed
# create some mildly intersting data:
sample_phases = np.arange( self.sample_idx, next_idx, dtype=np.float64 )
self.sample_idx = next_idx
sample_phase_pos = sample_phases / (self.period*self.rate)
sample_phase_pos %= 1.0
if self.neg_period is None:
return sample_phase_pos**4
sample_phase_neg = sample_phases / (self.neg_period*self.rate)
sample_phase_neg %= 1.0
return sample_phase_pos**4 - sample_phase_neg**4
class MainWindow(pg.GraphicsLayoutWidget):
""" example application main window """
def __init__(self):
super().__init__()
self.setWindowTitle('pyqtgraph example: gradient plots')
self.resize(800,800)
self.show()
layout = self # we are using a GraphicsLayoutWidget as main window for convenience
cm = pg.colormap.get('CET-L17')
cm.reverse()
pen0 = cm.getPen( span=(0.0,1.0), width=5 )
curve0 = pg.PlotDataItem(pen=pen0 )
comment0 = 'Clipped color map applied to vertical axis'
cm = pg.colormap.get('CET-D1')
cm.setMappingMode('diverging')
brush = cm.getBrush( span=(-1., 1.), orientation='vertical' )
curve1 = pg.PlotDataItem(pen='w', brush=brush, fillLevel=0.0 )
comment1 = 'Diverging vertical color map used as brush'
cm = pg.colormap.get('CET-L17')
cm.setMappingMode('mirror')
pen2 = cm.getPen( span=(400.0,600.0), width=5, orientation='horizontal' )
curve2 = pg.PlotDataItem(pen=pen2 )
comment2 = 'Mirrored color map applied to horizontal axis'
cm = pg.colormap.get('CET-C2')
cm.setMappingMode('repeat')
pen3 = cm.getPen( span=(100, 200), width=5, orientation='horizontal' )
curve3 = pg.PlotDataItem(pen=pen3 ) # vertical diverging fill
comment3 = 'Repeated color map applied to horizontal axis'
curves = (curve0, curve1, curve2, curve3)
comments = (comment0, comment1, comment2, comment3)
length = int( 3.0 * 200. ) # length of display in samples
self.top_plot = None
for idx, (curve, comment) in enumerate( zip(curves,comments) ):
plot = layout.addPlot(row=idx+1, col=0)
text = pg.TextItem( comment, anchor=(0,1) )
text.setPos(0.,1.)
if self.top_plot is None:
self.top_plot = plot
else:
plot.setXLink( self.top_plot )
plot.addItem( curve )
plot.addItem( text )
plot.setXRange( 0, length )
if idx != 1: plot.setYRange( 0. , 1.1 )
else : plot.setYRange( -1. , 1.2 ) # last plot include positive/negative data
self.traces = (
{'crv': curve0, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.55 ) },
{'crv': curve1, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.61, negative_period=0.55 ) },
{'crv': curve2, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.65 ) },
{'crv': curve3, 'buf': np.zeros( length ), 'ptr':0, 'ds': DataSource( signal_period=0.52 ) },
)
self.timer = QtCore.QTimer(timerType=QtCore.Qt.TimerType.PreciseTimer)
self.timer.timeout.connect(self.update)
timestamp = time.perf_counter()
for dic in self.traces:
dic['ds'].start( timestamp )
self.last_update = time.perf_counter()
self.mean_dt = None
self.timer.start(33)
def update(self):
""" called by timer at 30 Hz """
timestamp = time.perf_counter()
# measure actual update rate:
dt = timestamp - self.last_update
if self.mean_dt is None:
self.mean_dt = dt
else:
self.mean_dt = 0.95 * self.mean_dt + 0.05 * dt # average over fluctuating measurements
self.top_plot.setTitle(
'refresh: {:0.1f}ms -> {:0.1f} fps'.format( 1000*self.mean_dt, 1/self.mean_dt )
)
# handle rolling buffer:
self.last_update = timestamp
for dic in self.traces:
new_data = dic['ds'].get_data( timestamp )
idx_a = dic['ptr']
idx_b = idx_a + len( new_data )
len_buffer = dic['buf'].shape[0]
if idx_b < len_buffer: # data does not cross buffer boundary
dic['buf'][idx_a:idx_b] = new_data
else: # part of the new data needs to roll over to beginning of buffer
len_1 = len_buffer - idx_a # this many elements still fit
dic['buf'][idx_a:idx_a+len_1] = new_data[:len_1] # first part of data at end
idx_b = len(new_data) - len_1
dic['buf'][0:idx_b] = new_data[len_1:] # second part of data at re-start
dic['ptr'] = idx_b
dic['crv'].setData( dic['buf'] )
mkQApp("Gradient plotting example")
main_window = MainWindow()
## Start Qt event loop
if __name__ == '__main__':
pg.exec()

View File

@ -29,5 +29,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -51,7 +51,7 @@ class Graph(pg.GraphItem):
def mouseDragEvent(self, ev):
if ev.button() != QtCore.Qt.MouseButton.LeftButton:
if ev.button() != QtCore.Qt.LeftButton:
ev.ignore()
return
@ -129,5 +129,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -14,7 +14,7 @@ import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
app = pg.mkQApp("Data Slicing Example")
app = QtGui.QApplication([])
## Create window with two ImageView widgets
win = QtGui.QMainWindow()
@ -57,5 +57,8 @@ imv1.setLevels(-0.003, 0.003)
update()
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -22,7 +22,7 @@ def some_func2():
return sys.exc_info()[2]
app = pg.mkQApp("DataTreeWidget Example")
app = QtGui.QApplication([])
d = {
'a list': [1,2,3,4,5,6, {'nested1': 'aaaaa', 'nested2': 'bbbbb'}, "seven"],
'a dict': {
@ -42,5 +42,8 @@ tree.setWindowTitle('pyqtgraph example: DataTreeWidget')
tree.resize(600,600)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -12,7 +12,7 @@ import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtGui
app = pg.mkQApp("DateAxisItem Example")
app = QtGui.QApplication([])
# Create a plot with a date-time axis
w = pg.PlotWidget(axisItems = {'bottom': pg.DateAxisItem()})
@ -26,5 +26,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
app.exec_()

View File

@ -9,20 +9,17 @@ import initExample ## Add path to library (just for examples; you do not need th
import sys
import time
import os
import numpy as np
from PyQt5 import QtWidgets, QtCore, uic
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets, QtCore, loadUiType
pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')
BLUE = pg.mkPen('#1f77b4')
path = os.path.dirname(os.path.abspath(__file__))
uiFile = os.path.join(path, 'DateAxisItem_QtDesigner.ui')
Design, _ = loadUiType(uiFile)
Design, _ = uic.loadUiType('DateAxisItem_QtDesigner.ui')
class ExampleApp(QtWidgets.QMainWindow, Design):
def __init__(self):
@ -37,10 +34,15 @@ class ExampleApp(QtWidgets.QMainWindow, Design):
self.plotWidget.setAxisItems({'bottom': pg.DateAxisItem()})
self.plotWidget.showGrid(x=True, y=True)
app = pg.mkQApp("DateAxisItem_QtDesigner Example")
app = QtWidgets.QApplication(sys.argv)
app.setStyle(QtWidgets.QStyleFactory.create('Fusion'))
app.setPalette(QtWidgets.QApplication.style().standardPalette())
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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
app.exec_()

View File

@ -12,7 +12,7 @@ from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
app = pg.mkQApp("DiffTreeWidget Example")
app = QtGui.QApplication([])
A = {
'a list': [1,2,2,4,5,6, {'nested1': 'aaaa', 'nested2': 'bbbbb'}, "seven"],
'a dict': {
@ -45,5 +45,8 @@ tree.setWindowTitle('pyqtgraph example: DiffTreeWidget')
tree.resize(1000, 800)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -12,7 +12,7 @@ from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("Draw Example")
app = QtGui.QApplication([])
## Create window with GraphicsView widget
w = pg.GraphicsView()
@ -42,5 +42,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -26,5 +26,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,547 +0,0 @@
import keyword
import os
import re
import sys
import subprocess
from argparse import Namespace
import pyqtgraph as pg
from pyqtgraph.Qt import QtWidgets, QtGui, QtCore, QT_LIB
from collections import OrderedDict
from .utils import examples_
from functools import lru_cache
path = os.path.abspath(os.path.dirname(__file__))
sys.path.insert(0, path)
app = pg.mkQApp()
import importlib
ui_template = importlib.import_module(
f'exampleLoaderTemplate_{QT_LIB.lower()}')
# based on https://github.com/art1415926535/PyQt5-syntax-highlighting
QRegularExpression = QtCore.QRegularExpression
QFont = QtGui.QFont
QColor = QtGui.QColor
QTextCharFormat = QtGui.QTextCharFormat
QSyntaxHighlighter = QtGui.QSyntaxHighlighter
def charFormat(color, style='', background=None):
"""
Return a QTextCharFormat with the given attributes.
"""
_color = QColor()
if type(color) is not str:
_color.setRgb(color[0], color[1], color[2])
else:
_color.setNamedColor(color)
_format = QTextCharFormat()
_format.setForeground(_color)
if 'bold' in style:
_format.setFontWeight(QFont.Weight.Bold)
if 'italic' in style:
_format.setFontItalic(True)
if background is not None:
_format.setBackground(pg.mkColor(background))
return _format
class LightThemeColors:
Red = "#B71C1C"
Pink = "#FCE4EC"
Purple = "#4A148C"
DeepPurple = "#311B92"
Indigo = "#1A237E"
Blue = "#0D47A1"
LightBlue = "#01579B"
Cyan = "#006064"
Teal = "#004D40"
Green = "#1B5E20"
LightGreen = "#33691E"
Lime = "#827717"
Yellow = "#F57F17"
Amber = "#FF6F00"
Orange = "#E65100"
DeepOrange = "#BF360C"
Brown = "#3E2723"
Grey = "#212121"
BlueGrey = "#263238"
class DarkThemeColors:
Red = "#F44336"
Pink = "#F48FB1"
Purple = "#CE93D8"
DeepPurple = "#B39DDB"
Indigo = "#9FA8DA"
Blue = "#90CAF9"
LightBlue = "#81D4FA"
Cyan = "#80DEEA"
Teal = "#80CBC4"
Green = "#A5D6A7"
LightGreen = "#C5E1A5"
Lime = "#E6EE9C"
Yellow = "#FFF59D"
Amber = "#FFE082"
Orange = "#FFCC80"
DeepOrange = "#FFAB91"
Brown = "#BCAAA4"
Grey = "#EEEEEE"
BlueGrey = "#B0BEC5"
LIGHT_STYLES = {
'keyword': charFormat(LightThemeColors.Blue, 'bold'),
'operator': charFormat(LightThemeColors.Red, 'bold'),
'brace': charFormat(LightThemeColors.Purple),
'defclass': charFormat(LightThemeColors.Indigo, 'bold'),
'string': charFormat(LightThemeColors.Amber),
'string2': charFormat(LightThemeColors.DeepPurple),
'comment': charFormat(LightThemeColors.Green, 'italic'),
'self': charFormat(LightThemeColors.Blue, 'bold'),
'numbers': charFormat(LightThemeColors.Teal),
}
DARK_STYLES = {
'keyword': charFormat(DarkThemeColors.Blue, 'bold'),
'operator': charFormat(DarkThemeColors.Red, 'bold'),
'brace': charFormat(DarkThemeColors.Purple),
'defclass': charFormat(DarkThemeColors.Indigo, 'bold'),
'string': charFormat(DarkThemeColors.Amber),
'string2': charFormat(DarkThemeColors.DeepPurple),
'comment': charFormat(DarkThemeColors.Green, 'italic'),
'self': charFormat(DarkThemeColors.Blue, 'bold'),
'numbers': charFormat(DarkThemeColors.Teal),
}
class PythonHighlighter(QSyntaxHighlighter):
"""Syntax highlighter for the Python language.
"""
# Python keywords
keywords = keyword.kwlist
# Python operators
operators = [
r'=',
# Comparison
r'==', r'!=', r'<', r'<=', r'>', r'>=',
# Arithmetic
r'\+', r"-", r'\*', r'/', r'//', r'%', r'\*\*',
# In-place
r'\+=', r'-=', r'\*=', r'/=', r'\%=',
# Bitwise
r'\^', r'\|', r'&', r'~', r'>>', r'<<',
]
# Python braces
braces = [
r'\{', r'\}', r'\(', r'\)', r'\[', r'\]',
]
def __init__(self, document):
super().__init__(document)
# Multi-line strings (expression, flag, style)
self.tri_single = (QRegularExpression("'''"), 1, 'string2')
self.tri_double = (QRegularExpression('"""'), 2, 'string2')
rules = []
# Keyword, operator, and brace rules
rules += [(r'\b%s\b' % w, 0, 'keyword')
for w in PythonHighlighter.keywords]
rules += [(o, 0, 'operator')
for o in PythonHighlighter.operators]
rules += [(b, 0, 'brace')
for b in PythonHighlighter.braces]
# All other rules
rules += [
# 'self'
(r'\bself\b', 0, 'self'),
# 'def' followed by an identifier
(r'\bdef\b\s*(\w+)', 1, 'defclass'),
# 'class' followed by an identifier
(r'\bclass\b\s*(\w+)', 1, 'defclass'),
# Numeric literals
(r'\b[+-]?[0-9]+[lL]?\b', 0, 'numbers'),
(r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, 'numbers'),
(r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, 'numbers'),
# Double-quoted string, possibly containing escape sequences
(r'"[^"\\]*(\\.[^"\\]*)*"', 0, 'string'),
# Single-quoted string, possibly containing escape sequences
(r"'[^'\\]*(\\.[^'\\]*)*'", 0, 'string'),
# From '#' until a newline
(r'#[^\n]*', 0, 'comment'),
]
self.rules = rules
self.searchText = None
@property
def styles(self):
app = QtWidgets.QApplication.instance()
return DARK_STYLES if app.property('darkMode') else LIGHT_STYLES
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
# Do other syntax formatting
rules = self.rules.copy()
for expression, nth, format in rules:
format = self.styles[format]
for n, match in enumerate(re.finditer(expression, text)):
if n < nth:
continue
start = match.start()
length = match.end() - start
self.setFormat(start, length, format)
self.applySearchHighlight(text)
self.setCurrentBlockState(0)
# Do multi-line strings
in_multiline = self.match_multiline(text, *self.tri_single)
if not in_multiline:
in_multiline = self.match_multiline(text, *self.tri_double)
def match_multiline(self, text, delimiter, in_state, style):
"""Do highlighting of multi-line strings.
=========== ==========================================================
delimiter (QRegularExpression) for triple-single-quotes or
triple-double-quotes
in_state (int) to represent the corresponding state changes when
inside those strings. Returns True if we're still inside a
multi-line string when this function is finished.
style (str) representation of the kind of style to use
=========== ==========================================================
"""
# If inside triple-single quotes, start at 0
if self.previousBlockState() == in_state:
start = 0
add = 0
# Otherwise, look for the delimiter on this line
else:
match = delimiter.match(text)
start = match.capturedStart()
# Move past this match
add = match.capturedLength()
# As long as there's a delimiter match on this line...
while start >= 0:
# Look for the ending delimiter
match = delimiter.match(text, start + add)
end = match.capturedEnd()
# Ending delimiter on this line?
if end >= add:
length = end - start + add + match.capturedLength()
self.setCurrentBlockState(0)
# No; multi-line string
else:
self.setCurrentBlockState(in_state)
length = len(text) - start + add
# Apply formatting
self.setFormat(start, length, self.styles[style])
# Highlighting sits on top of this formatting
# Look for the next match
match = delimiter.match(text, start + length)
start = match.capturedStart()
self.applySearchHighlight(text)
# Return True if still inside a multi-line string, False otherwise
if self.currentBlockState() == in_state:
return True
else:
return False
def applySearchHighlight(self, text):
if not self.searchText:
return
expr = f'(?i){self.searchText}'
palette: QtGui.QPalette = app.palette()
color = palette.highlight().color()
fgndColor = palette.color(palette.ColorGroup.Current,
palette.ColorRole.Text).name()
style = charFormat(fgndColor, background=color.name())
for match in re.finditer(expr, text):
start = match.start()
length = match.end() - start
self.setFormat(start, length, style)
def unnestedDict(exDict):
"""Converts a dict-of-dicts to a singly nested dict for non-recursive parsing"""
out = {}
for kk, vv in exDict.items():
if isinstance(vv, dict):
out.update(unnestedDict(vv))
else:
out[kk] = vv
return out
class ExampleLoader(QtWidgets.QMainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
self.ui = ui_template.Ui_Form()
self.cw = QtWidgets.QWidget()
self.setCentralWidget(self.cw)
self.ui.setupUi(self.cw)
self.setWindowTitle("PyQtGraph Examples")
self.codeBtn = QtWidgets.QPushButton('Run Edited Code')
self.codeLayout = QtWidgets.QGridLayout()
self.ui.codeView.setLayout(self.codeLayout)
self.hl = PythonHighlighter(self.ui.codeView.document())
app = QtWidgets.QApplication.instance()
app.paletteChanged.connect(self.updateTheme)
policy = QtWidgets.QSizePolicy.Policy.Expanding
self.codeLayout.addItem(QtWidgets.QSpacerItem(100,100, policy, policy), 0, 0)
self.codeLayout.addWidget(self.codeBtn, 1, 1)
self.codeBtn.hide()
textFil = self.ui.exampleFilter
self.curListener = None
self.ui.exampleFilter.setFocus()
def onComboChanged(searchType):
if self.curListener is not None:
self.curListener.disconnect()
self.curListener = textFil.textChanged
if searchType == 'Content Search':
self.curListener.connect(self.filterByContent)
else:
self.hl.searchText = None
self.curListener.connect(self.filterByTitle)
# Fire on current text, too
self.curListener.emit(textFil.text())
self.ui.searchFiles.currentTextChanged.connect(onComboChanged)
onComboChanged(self.ui.searchFiles.currentText())
self.itemCache = []
self.populateTree(self.ui.exampleTree.invisibleRootItem(), examples_)
self.ui.exampleTree.expandAll()
self.resize(1000,500)
self.show()
self.ui.splitter.setSizes([250,750])
self.ui.loadBtn.clicked.connect(self.loadFile)
self.ui.exampleTree.currentItemChanged.connect(self.showFile)
self.ui.exampleTree.itemDoubleClicked.connect(self.loadFile)
# textChanged fires when the highlighter is reassigned the same document. Prevent this
# from showing "run edited code" by checking for actual content change
oldText = self.ui.codeView.toPlainText()
def onTextChange():
nonlocal oldText
newText = self.ui.codeView.toPlainText()
if newText != oldText:
oldText = newText
self.codeEdited()
self.ui.codeView.textChanged.connect(onTextChange)
self.codeBtn.clicked.connect(self.runEditedCode)
def filterByTitle(self, text):
self.showExamplesByTitle(self.getMatchingTitles(text))
self.hl.setDocument(self.ui.codeView.document())
def filterByContent(self, text=None):
# Don't filter very short strings
checkDict = unnestedDict(examples_)
self.hl.searchText = text
# Need to reapply to current document
self.hl.setDocument(self.ui.codeView.document())
titles = []
text = text.lower()
for kk, vv in checkDict.items():
if isinstance(vv, Namespace):
vv = vv.filename
filename = os.path.join(path, vv)
contents = self.getExampleContent(filename).lower()
if text in contents:
titles.append(kk)
self.showExamplesByTitle(titles)
def getMatchingTitles(self, text, exDict=None, acceptAll=False):
if exDict is None:
exDict = examples_
text = text.lower()
titles = []
for kk, vv in exDict.items():
matched = acceptAll or text in kk.lower()
if isinstance(vv, dict):
titles.extend(self.getMatchingTitles(text, vv, acceptAll=matched))
elif matched:
titles.append(kk)
return titles
def showExamplesByTitle(self, titles):
QTWI = QtWidgets.QTreeWidgetItemIterator
flag = QTWI.IteratorFlag.NoChildren
treeIter = QTWI(self.ui.exampleTree, flag)
item = treeIter.value()
while item is not None:
parent = item.parent()
show = (item.childCount() or item.text(0) in titles)
item.setHidden(not show)
# If all children of a parent are gone, hide it
if parent:
hideParent = True
for ii in range(parent.childCount()):
if not parent.child(ii).isHidden():
hideParent = False
break
parent.setHidden(hideParent)
treeIter += 1
item = treeIter.value()
def simulate_black_mode(self):
"""
used to simulate MacOS "black mode" on other platforms
intended for debug only, as it manage only the QPlainTextEdit
"""
# first, a dark background
c = QtGui.QColor('#171717')
p = self.ui.codeView.palette()
p.setColor(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Base, c)
p.setColor(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Base, c)
self.ui.codeView.setPalette(p)
# then, a light font
f = QtGui.QTextCharFormat()
f.setForeground(QtGui.QColor('white'))
self.ui.codeView.setCurrentCharFormat(f)
# finally, override application automatic detection
app = QtWidgets.QApplication.instance()
app.setProperty('darkMode', True)
def updateTheme(self):
self.hl = PythonHighlighter(self.ui.codeView.document())
def populateTree(self, root, examples):
bold_font = None
for key, val in examples.items():
item = QtWidgets.QTreeWidgetItem([key])
self.itemCache.append(item) # PyQt 4.9.6 no longer keeps references to these wrappers,
# so we need to make an explicit reference or else the .file
# attribute will disappear.
if isinstance(val, OrderedDict):
self.populateTree(item, val)
elif isinstance(val, Namespace):
item.file = val.filename
if 'recommended' in val:
if bold_font is None:
bold_font = item.font(0)
bold_font.setBold(True)
item.setFont(0, bold_font)
else:
item.file = val
root.addChild(item)
def currentFile(self):
item = self.ui.exampleTree.currentItem()
if hasattr(item, 'file'):
return os.path.join(path, item.file)
return None
def loadFile(self, edited=False):
qtLib = str(self.ui.qtLibCombo.currentText())
env = None
if qtLib != 'default':
env = dict(os.environ, PYQTGRAPH_QT_LIB=qtLib)
if edited:
path = os.path.abspath(os.path.dirname(__file__))
proc = subprocess.Popen([sys.executable, '-'], stdin=subprocess.PIPE, cwd=path, env=env)
code = str(self.ui.codeView.toPlainText()).encode('UTF-8')
proc.stdin.write(code)
proc.stdin.close()
else:
fn = self.currentFile()
if fn is None:
return
subprocess.Popen([sys.executable, fn], env=env)
def showFile(self):
fn = self.currentFile()
text = self.getExampleContent(fn)
self.ui.codeView.setPlainText(text)
self.ui.loadedFileLabel.setText(fn)
self.codeBtn.hide()
@lru_cache(100)
def getExampleContent(self, filename):
if filename is None:
self.ui.codeView.clear()
return
if os.path.isdir(filename):
filename = os.path.join(filename, '__main__.py')
with open(filename, "r") as currentFile:
text = currentFile.read()
return text
def codeEdited(self):
self.codeBtn.show()
def runEditedCode(self):
self.loadFile(edited=True)
def keyPressEvent(self, event):
ret = super().keyPressEvent(event)
if not QtCore.Qt.KeyboardModifier.ControlModifier & event.modifiers():
return ret
key = event.key()
Key = QtCore.Qt.Key
# Allow quick navigate to search
if key == Key.Key_F:
self.ui.exampleFilter.setFocus()
event.accept()
return
if key not in [Key.Key_Plus, Key.Key_Minus, Key.Key_Underscore, Key.Key_Equal, Key.Key_0]:
return ret
font = self.ui.codeView.font()
oldSize = font.pointSize()
if key == Key.Key_Plus or key == Key.Key_Equal:
font.setPointSize(oldSize + max(oldSize*.15, 1))
elif key == Key.Key_Minus or key == Key.Key_Underscore:
newSize = oldSize - max(oldSize*.15, 1)
font.setPointSize(max(newSize, 1))
elif key == Key.Key_0:
# Reset to original size
font.setPointSize(10)
self.ui.codeView.setFont(font)
event.accept()
def main():
app = pg.mkQApp()
loader = ExampleLoader()
loader.ui.exampleTree.setCurrentIndex(
loader.ui.exampleTree.model().index(0,0)
)
pg.exec()
if __name__ == '__main__':
main()

View File

@ -45,5 +45,8 @@ timer.timeout.connect(update)
timer.start(30)
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -19,7 +19,7 @@ import pyqtgraph as pg
import numpy as np
import pyqtgraph.metaarray as metaarray
app = pg.mkQApp("Flowchart Example")
app = QtGui.QApplication([])
## Create main window with grid layout
win = QtGui.QMainWindow()
@ -75,5 +75,10 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -13,7 +13,7 @@ from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp("Flowchart Custom Node Example")
app = QtGui.QApplication([])
## Create main window with a grid layout inside
win = QtGui.QMainWindow()
@ -150,5 +150,10 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
This example demonstrates the use of GLBarGraphItem.
Demonstrate use of GLLinePlotItem to draw cross-sections of a surface.
"""
## Add path to library (just for examples; you do not need this)
@ -11,11 +11,11 @@ import pyqtgraph.opengl as gl
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp("GLBarGraphItem Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 40
w.show()
w.setWindowTitle('pyqtgraph example: GLBarGraphItem')
w.setCameraPosition(distance=40)
gx = gl.GLGridItem()
gx.rotate(90, 0, 1, 0)
@ -39,5 +39,9 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,43 +0,0 @@
import initExample
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy
app = pg.mkQApp()
w = gl.GLViewWidget()
w.show()
w.setWindowTitle("pyqtgraph example: GLGradientLegendItem")
w.setCameraPosition(distance=60)
gx = gl.GLGridItem()
gx.rotate(90, 0, 1, 0)
w.addItem(gx)
md = gl.MeshData.cylinder(rows=10, cols=20, radius=[5.0, 5], length=20.0)
md._vertexes[:, 2] = md._vertexes[:, 2] - 10
# set color based on z coordinates
color_map = pg.colormap.get("CET-L10")
h = md.vertexes()[:, 2]
# remember these
h_max, h_min = h.max(), h.min()
h = (h - h_min) / (h_max - h_min)
colors = color_map.map(h, mode="float")
md.setFaceColors(colors)
m = gl.GLMeshItem(meshdata=md, smooth=True)
w.addItem(m)
legendLabels = numpy.linspace(h_max, h_min, 5)
legendPos = numpy.linspace(1, 0, 5)
legend = dict(zip(map(str, legendLabels), legendPos))
gll = gl.GLGradientLegendItem(
pos=(10, 10), size=(50, 300), gradient=color_map, labels=legend
)
w.addItem(gll)
## Start Qt event loop unless running in interactive mode.
if __name__ == "__main__":
pg.exec()

View File

@ -1,47 +0,0 @@
"""
Demonstrates use of GLGraphItem
"""
## Add path to library (just for examples; you do not need this)
import initExample
import pyqtgraph as pg
import pyqtgraph.opengl as gl
import numpy as np
app = pg.mkQApp("GLGraphItem Example")
w = gl.GLViewWidget()
w.setCameraPosition(distance=20)
w.show()
edges = np.array([
[0, 2],
[0, 3],
[1, 2],
[1, 3],
[2, 3]
])
nodes = np.array(
[
[0, 0, 0],
[1, 0, 0],
[0, 1, 0],
[1, 1, 1]
]
)
edgeColor=pg.glColor("w")
gi = gl.GLGraphItem(
edges=edges,
nodePositions=nodes,
edgeWidth=1.,
nodeSize=10.
)
w.addItem(gi)
if __name__ == "__main__":
pg.exec()

View File

@ -13,11 +13,11 @@ import pyqtgraph.opengl as gl
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp("GLImageItem Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 200
w.show()
w.setWindowTitle('pyqtgraph example: GLImageItem')
w.setCameraPosition(distance=200)
## create volume data set to slice three images from
shape = (100,100,70)
@ -50,5 +50,8 @@ w.addItem(v3)
ax = gl.GLAxisItem()
w.addItem(ax)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -7,11 +7,11 @@ 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
import numpy as np
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
app = pg.mkQApp("GLIsosurface Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
w.setWindowTitle('pyqtgraph example: GLIsosurface')
@ -22,16 +22,23 @@ 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, np.hypot(x, y))
r = np.sqrt(x**2 + y**2 + z **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
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..")
@ -59,5 +66,10 @@ m2.setGLOptions('additive')
w.addItem(m2)
m2.translate(-25, -25, -50)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -6,15 +6,16 @@ Demonstrate use of GLLinePlotItem to draw cross-sections of a surface.
## Add path to library (just for examples; you do not need this)
import initExample
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp("GLLinePlotItem Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 40
w.show()
w.setWindowTitle('pyqtgraph example: GLLinePlotItem')
w.setCameraPosition(distance=40)
gx = gl.GLGridItem()
gx.rotate(90, 0, 1, 0)
@ -28,16 +29,24 @@ 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 = y[i]
d = np.hypot(x, yi)
yi = np.array([y[i]]*100)
d = (x**2 + yi**2)**0.5
z = 10 * np.cos(d) / (d+1)
pts = np.column_stack([x, np.full_like(x, yi), z])
plt = gl.GLLinePlotItem(pos=pts, color=pg.mkColor((i,n*1.3)), width=(i+1)/10., antialias=True)
pts = np.vstack([x,yi,z]).transpose()
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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -11,7 +11,7 @@ from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
import pyqtgraph.opengl as gl
app = pg.mkQApp("GLMeshItem Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
w.setWindowTitle('pyqtgraph example: GLMeshItem')
@ -117,5 +117,13 @@ m6.rotate(0., 0, 1, 1)
w.addItem(m5)
w.addItem(m6)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,94 +0,0 @@
"""
Demonstrate using QPainter on a subclass of GLGraphicsItem.
"""
## Add path to library (just for examples; you do not need this)
import initExample
import pyqtgraph as pg
import pyqtgraph.opengl
from pyqtgraph.Qt import QtCore, QtGui
import OpenGL.GL as GL
SIZE = 32
class GLPainterItem(pg.opengl.GLGraphicsItem.GLGraphicsItem):
def __init__(self, **kwds):
super().__init__()
glopts = kwds.pop('glOptions', 'additive')
self.setGLOptions(glopts)
def compute_projection(self):
modelview = GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX)
projection = GL.glGetDoublev(GL.GL_PROJECTION_MATRIX)
mvp = projection.T @ modelview.T
mvp = QtGui.QMatrix4x4(mvp.ravel().tolist())
# note that QRectF.bottom() != QRect.bottom()
rect = QtCore.QRectF(self.view().rect())
ndc_to_viewport = QtGui.QMatrix4x4()
ndc_to_viewport.viewport(rect.left(), rect.bottom(), rect.width(), -rect.height())
return ndc_to_viewport * mvp
def paint(self):
self.setupGLState()
painter = QtGui.QPainter(self.view())
self.draw(painter)
painter.end()
def draw(self, painter):
painter.setPen(QtCore.Qt.GlobalColor.white)
painter.setRenderHints(QtGui.QPainter.RenderHint.Antialiasing | QtGui.QPainter.RenderHint.TextAntialiasing)
rect = self.view().rect()
af = QtCore.Qt.AlignmentFlag
painter.drawText(rect, af.AlignTop | af.AlignRight, 'TR')
painter.drawText(rect, af.AlignBottom | af.AlignLeft, 'BL')
painter.drawText(rect, af.AlignBottom | af.AlignRight, 'BR')
opts = self.view().cameraParams()
lines = []
center = opts['center']
lines.append(f"center : ({center.x():.1f}, {center.y():.1f}, {center.z():.1f})")
for key in ['distance', 'fov', 'elevation', 'azimuth']:
lines.append(f"{key} : {opts[key]:.1f}")
xyz = self.view().cameraPosition()
lines.append(f"xyz : ({xyz.x():.1f}, {xyz.y():.1f}, {xyz.z():.1f})")
info = "\n".join(lines)
painter.drawText(rect, af.AlignTop | af.AlignLeft, info)
project = self.compute_projection()
hsize = SIZE // 2
for xi in range(-hsize, hsize+1):
for yi in range(-hsize, hsize+1):
if xi == -hsize and yi == -hsize:
# skip one corner for visual orientation
continue
vec3 = QtGui.QVector3D(xi, yi, 0)
pos = project.map(vec3).toPointF()
painter.drawEllipse(pos, 1, 1)
pg.mkQApp("GLPainterItem Example")
glv = pg.opengl.GLViewWidget()
glv.show()
glv.setWindowTitle('pyqtgraph example: GLPainterItem')
glv.setCameraPosition(distance=50, elevation=90, azimuth=0)
griditem = pg.opengl.GLGridItem()
griditem.setSize(SIZE, SIZE)
griditem.setSpacing(1, 1)
glv.addItem(griditem)
axisitem = pg.opengl.GLAxisItem()
axisitem.setSize(SIZE/2, SIZE/2, 1)
glv.addItem(axisitem)
paintitem = GLPainterItem()
glv.addItem(paintitem)
if __name__ == '__main__':
pg.exec()

View File

@ -7,17 +7,15 @@ Demonstrates use of GLScatterPlotItem with rapidly-updating plots.
## Add path to library (just for examples; you do not need this)
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
app = pg.mkQApp("GLScatterPlotItem Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 20
w.show()
w.setWindowTitle('pyqtgraph example: GLScatterPlotItem')
w.setCameraPosition(distance=20)
g = gl.GLGridItem()
w.addItem(g)
@ -85,10 +83,10 @@ def update():
global phase, sp2, d2
s = -np.cos(d2*2+phase)
color = np.empty((len(d2),4), dtype=np.float32)
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.)
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)
sp2.setData(color=color)
phase -= 0.1
@ -107,5 +105,9 @@ t = QtCore.QTimer()
t.timeout.connect(update)
t.start(50)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -13,7 +13,7 @@ import pyqtgraph.opengl as gl
import numpy as np
## Create a GL View widget to display data
app = pg.mkQApp("GLSurfacePlot Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
w.setWindowTitle('pyqtgraph example: GLSurfacePlot')
@ -92,5 +92,8 @@ timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(30)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,35 +0,0 @@
# -*- coding: utf-8 -*-
"""
Simple examples demonstrating the use of GLTextItem.
"""
import initExample
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui, mkQApp
import pyqtgraph.opengl as gl
app = mkQApp("GLTextItem Example")
gvw = gl.GLViewWidget()
gvw.show()
gvw.setWindowTitle('pyqtgraph example: GLTextItem')
griditem = gl.GLGridItem()
griditem.setSize(10, 10)
griditem.setSpacing(1, 1)
gvw.addItem(griditem)
axisitem = gl.GLAxisItem()
gvw.addItem(axisitem)
txtitem1 = gl.GLTextItem(pos=(0.0, 0.0, 0.0), text='text1')
gvw.addItem(txtitem1)
txtitem2 = gl.GLTextItem()
txtitem2.setData(pos=(1.0, -1.0, 2.0), color=(127, 255, 127, 255), text='text2')
gvw.addItem(txtitem2)
if __name__ == '__main__':
pg.exec()

View File

@ -6,14 +6,14 @@ Very basic 3D graphics example; create a view widget and add a few items.
## Add path to library (just for examples; you do not need this)
import initExample
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
pg.mkQApp("GLViewWidget Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 20
w.show()
w.setWindowTitle('pyqtgraph example: GLViewWidget')
w.setCameraPosition(distance=20)
ax = gl.GLAxisItem()
ax.setSize(5,5,5)
@ -27,5 +27,8 @@ ax2.setParentItem(b)
b.translate(1,1,1)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -7,66 +7,46 @@ 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")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.opts['distance'] = 200
w.show()
w.setWindowTitle('pyqtgraph example: GLVolumeItem')
w.setCameraPosition(distance=200)
#b = gl.GLBoxItem()
#w.addItem(b)
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, np.hypot(x, y))
r = np.sqrt(x**2 + y**2 + z **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
a0 = 2
return (
(1.0 / 81.0)
* 1.0 / (6.0 * np.pi) ** 0.5
* (1.0 / a0) ** (3 / 2)
* (r / a0) ** 2
* np.exp(-r / (3 * a0))
* (3 * np.cos(th) ** 2 - 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
data = np.fromfunction(psi, (100,100,200))
with np.errstate(divide = 'ignore'):
positive = np.log(fn.clip_array(data, 0, data.max())**2)
negative = np.log(fn.clip_array(-data, 0, -data.min())**2)
positive = np.log(np.clip(data, 0, data.max())**2)
negative = np.log(np.clip(-data, 0, -data.min())**2)
d2 = np.empty(data.shape + (4,), dtype=np.ubyte)
# Original Code
# d2[..., 0] = positive * (255./positive.max())
# d2[..., 1] = negative * (255./negative.max())
# Reformulated Code
# Both positive.max() and negative.max() are negative-valued.
# Thus the next 2 lines are _not_ bounded to [0, 255]
positive = positive * (255./positive.max())
negative = negative * (255./negative.max())
# When casting to ubyte, the original code relied on +Inf to be
# converted to 0. On arm64, it gets converted to 255.
# Thus the next 2 lines change +Inf explicitly to 0 instead.
positive[np.isinf(positive)] = 0
negative[np.isinf(negative)] = 0
# When casting to ubyte, the original code relied on the conversion
# to do modulo 256. The next 2 lines do it explicitly instead as
# documentation.
d2[..., 0] = positive.astype(int) % 256
d2[..., 1] = negative.astype(int) % 256
d2[..., 0] = positive * (255./positive.max())
d2[..., 1] = negative * (255./negative.max())
d2[..., 2] = d2[...,1]
d2[..., 3] = d2[..., 0]*0.3 + d2[..., 1]*0.3
d2[..., 3] = (d2[..., 3].astype(float) / 255.) **2 * 255
@ -82,5 +62,8 @@ w.addItem(v)
ax = gl.GLAxisItem()
w.addItem(ax)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -9,12 +9,11 @@ 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
app = pg.mkQApp("GLShaders Example")
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
w.setWindowTitle('pyqtgraph example: GL Shaders')
@ -24,6 +23,9 @@ 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)
@ -99,5 +101,10 @@ w.addItem(m6)
#w.addItem(m2)
#m2.translate(-25, -25, -50)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -7,7 +7,7 @@ from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
app = pg.mkQApp("Gradiant Editor Example")
app = QtGui.QApplication([])
mw = pg.GraphicsView()
mw.resize(800,800)
mw.show()
@ -20,5 +20,9 @@ mw.show()
ge = pg.GradientEditorItem()
mw.setCentralItem(ge)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -12,7 +12,7 @@ import numpy as np
app = pg.mkQApp("Gradiant Widget Example")
app = QtGui.QApplication([])
w = QtGui.QMainWindow()
w.show()
w.setWindowTitle('pyqtgraph example: GradientWidget')
@ -27,18 +27,15 @@ cw.setLayout(l)
w1 = pg.GradientWidget(orientation='top')
w2 = pg.GradientWidget(orientation='right', allowAdd=False)
#w2.setTickColor(1, QtGui.QColor(255,255,255))
w3 = pg.GradientWidget(orientation='bottom', allowAdd=False, allowRemove=False)
w3 = pg.GradientWidget(orientation='bottom')
w4 = pg.GradientWidget(orientation='left')
w4.loadPreset('spectrum')
label = QtGui.QLabel("""
- Click a triangle to change its color
- Drag triangles to move
- Right-click a gradient to load triangle presets
- Click in an empty area to add a new color
(adding is disabled for the bottom-side and right-side widgets)
(adding is disabled for the right-side widget)
- Right click a triangle to remove
(only possible if more than two triangles are visible)
(removing is disabled for the bottom-side widget)
""")
l.addWidget(w1, 0, 1)
@ -47,8 +44,12 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -57,5 +57,11 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -12,7 +12,7 @@ from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import numpy as np
app = pg.mkQApp("Gradiant Layout Example")
app = QtGui.QApplication([])
view = pg.GraphicsView()
l = pg.GraphicsLayout(border=(100,100,100))
view.setCentralItem(l)
@ -78,5 +78,10 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -6,7 +6,7 @@ from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph as pg
from pyqtgraph.GraphicsScene import GraphicsScene
app = pg.mkQApp("GraphicsScene Example")
app = QtGui.QApplication([])
win = pg.GraphicsView()
win.show()
@ -58,5 +58,9 @@ vb.addItem(prox)
g = pg.GridItem()
vb.addItem(g)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -3,49 +3,45 @@
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
from pyqtgraph.Qt import QtGui
app = pg.mkQApp("Histogram Lookup Table Example")
app = QtGui.QApplication([])
win = QtGui.QMainWindow()
win.resize(880, 600)
win.resize(800,600)
win.show()
win.setWindowTitle('pyqtgraph example: Histogram LUT')
cw = QtGui.QWidget()
win.setCentralWidget(cw)
layout = QtGui.QGridLayout()
cw.setLayout(layout)
layout.setSpacing(0)
l = QtGui.QGridLayout()
cw.setLayout(l)
l.setSpacing(0)
view = pg.GraphicsView()
v = pg.GraphicsView()
vb = pg.ViewBox()
vb.setAspectLocked()
view.setCentralItem(vb)
layout.addWidget(view, 0, 1, 3, 1)
hist = pg.HistogramLUTWidget(gradientPosition="left")
layout.addWidget(hist, 0, 2)
v.setCentralItem(vb)
l.addWidget(v, 0, 0, 3, 1)
w = pg.HistogramLUTWidget()
l.addWidget(w, 0, 1)
monoRadio = QtGui.QRadioButton('mono')
rgbaRadio = QtGui.QRadioButton('rgba')
layout.addWidget(monoRadio, 1, 2)
layout.addWidget(rgbaRadio, 2, 2)
l.addWidget(monoRadio, 1, 1)
l.addWidget(rgbaRadio, 2, 1)
monoRadio.setChecked(True)
def setLevelMode():
mode = 'mono' if monoRadio.isChecked() else 'rgba'
hist.setLevelMode(mode)
w.setLevelMode(mode)
monoRadio.toggled.connect(setLevelMode)
data = pg.gaussianFilter(np.random.normal(size=(256, 256, 3)), (20, 20, 0))
@ -56,7 +52,11 @@ img = pg.ImageItem(data)
vb.addItem(img)
vb.autoRange()
hist.setImageItem(img)
w.setImageItem(img)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -9,9 +9,9 @@ import initExample
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
import pyqtgraph as pg
from time import perf_counter
import pyqtgraph.ptime as ptime
app = pg.mkQApp("ImageItem Example")
app = QtGui.QApplication([])
## Create window with GraphicsView widget
win = pg.GraphicsLayoutWidget()
@ -33,30 +33,29 @@ view.setRange(QtCore.QRectF(0, 0, 600, 600))
data = np.random.normal(size=(15, 600, 600), loc=1024, scale=64).astype(np.uint16)
i = 0
updateTime = perf_counter()
elapsed = 0
timer = QtCore.QTimer()
timer.setSingleShot(True)
# not using QTimer.singleShot() because of persistence on PyQt. see PR #1605
updateTime = ptime.time()
fps = 0
def updateData():
global img, data, i, updateTime, elapsed
global img, data, i, updateTime, fps
## Display the data
img.setImage(data[i])
i = (i+1) % data.shape[0]
timer.start(1)
now = perf_counter()
elapsed_now = now - updateTime
QtCore.QTimer.singleShot(1, updateData)
now = ptime.time()
fps2 = 1.0 / (now-updateTime)
updateTime = now
elapsed = elapsed * 0.9 + elapsed_now * 0.1
# print(f"{1 / elapsed:.1f} fps")
fps = fps * 0.9 + fps2 * 0.1
timer.timeout.connect(updateData)
#print "%0.1f fps" % fps
updateData()
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
"""
This example demonstrates the use of ImageView with 3-color image stacks.
ImageView is a high-level widget for displaying and analyzing 2D and 3D data.
ImageView provides:
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
@ -21,7 +20,7 @@ import pyqtgraph as pg
# Interpret image data as row-major instead of col-major
pg.setConfigOptions(imageAxisOrder='row-major')
app = pg.mkQApp("ImageView Example")
app = QtGui.QApplication([])
## Create window with ImageView widget
win = QtGui.QMainWindow()
@ -31,17 +30,22 @@ win.setCentralWidget(imv)
win.show()
win.setWindowTitle('pyqtgraph example: ImageView')
## Create random 3D data set with time varying signals
dataRed = np.ones((100, 200, 200)) * np.linspace(90, 150, 100)[:, np.newaxis, np.newaxis]
dataRed += pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 100
dataGrn = np.ones((100, 200, 200)) * np.linspace(90, 180, 100)[:, np.newaxis, np.newaxis]
dataGrn += pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 100
dataBlu = np.ones((100, 200, 200)) * np.linspace(180, 90, 100)[:, np.newaxis, np.newaxis]
dataBlu += pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 100
## Create random 3D data set with noisy signals
img = pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
img = img[np.newaxis,:,:]
decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
data = np.random.normal(size=(100, 200, 200))
data += img * decay
data += 2
data = np.concatenate(
(dataRed[:, :, :, np.newaxis], dataGrn[:, :, :, np.newaxis], dataBlu[:, :, :, np.newaxis]), axis=3
)
## Add time-varying signal
sig = np.zeros(data.shape[0])
sig[30:] += np.exp(-np.linspace(1,10, 70))
sig[40:] += np.exp(-np.linspace(1,10, 60))
sig[70:] += np.exp(-np.linspace(1,10, 30))
sig = sig[:,np.newaxis,np.newaxis] * 3
data[:,50:60,30:40] += sig
## Display the data and assign each frame a time value from 1.0 to 3.0
@ -59,9 +63,8 @@ colors = [
cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors)
imv.setColorMap(cmap)
# Start up with an ROI
imv.ui.roiBtn.setChecked(True)
imv.roiClicked()
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -9,7 +9,7 @@ import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("InfiniteLine Example")
app = QtGui.QApplication([])
win = pg.GraphicsLayoutWidget(show=True, title="Plotting items examples")
win.resize(1000,600)
@ -32,54 +32,14 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -12,7 +12,7 @@ from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
app = pg.mkQApp("Joystick Button Example")
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
mw.resize(300,50)
mw.setWindowTitle('pyqtgraph example: JoystickButton')
@ -46,5 +46,11 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -7,37 +7,21 @@ import initExample ## Add path to library (just for examples; you do not need th
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui
import numpy as np
win = pg.plot()
win.setWindowTitle('pyqtgraph example: BarGraphItem')
plt = pg.plot()
plt.setWindowTitle('pyqtgraph example: Legend')
plt.addLegend()
#l = pg.LegendItem((100,60), offset=(70,30)) # args are (size, offset)
#l.setParentItem(plt.graphicsItem()) # Note we do NOT call plt.addItem in this case
# # option1: only for .plot(), following c1,c2 for example-----------------------
# win.addLegend(frame=False, colCount=2)
c1 = plt.plot([1,3,2,4], pen='r', symbol='o', symbolPen='r', symbolBrush=0.5, name='red plot')
c2 = plt.plot([2,1,4,3], pen='g', fillLevel=0, fillBrush=(255,255,255,30), name='green plot')
#l.addItem(c1, 'red plot')
#l.addItem(c2, 'green plot')
# bar graph
x = np.arange(10)
y = np.sin(x+2) * 3
bg1 = pg.BarGraphItem(x=x, height=y, width=0.3, brush='b', pen='w', name='bar')
win.addItem(bg1)
# curve
c1 = win.plot([np.random.randint(0,8) for i in range(10)], pen='r', symbol='t', symbolPen='r', symbolBrush='g', name='curve1')
c2 = win.plot([2,1,4,3,1,3,2,4,3,2], pen='g', fillLevel=0, fillBrush=(255,255,255,30), name='curve2')
# scatter plot
s1 = pg.ScatterPlotItem(size=10, pen=pg.mkPen(None), brush=pg.mkBrush(255, 255, 255, 120), name='scatter')
spots = [{'pos': [i, np.random.randint(-3, 3)], 'data': 1} for i in range(10)]
s1.addPoints(spots)
win.addItem(s1)
# # option2: generic method------------------------------------------------
legend = pg.LegendItem((80,60), offset=(70,20))
legend.setParentItem(win.graphicsItem())
legend.addItem(bg1, 'bar')
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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -10,7 +10,7 @@ from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("Log Plot Example")
app = QtGui.QApplication([])
win = pg.GraphicsLayoutWidget(show=True, title="Basic plotting examples")
win.resize(1000,600)
@ -30,5 +30,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,62 +0,0 @@
# -*- coding: utf-8 -*-
"""
This example demonstrates ViewBox and AxisItem configuration to plot a correlation matrix.
"""
## 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 QtWidgets, mkQApp, QtGui
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: Correlation matrix display')
self.resize(600,500)
self.show()
corrMatrix = np.array([
[ 1. , 0.5184571 , -0.70188642],
[ 0.5184571 , 1. , -0.86094096],
[-0.70188642, -0.86094096, 1. ]
])
columns = ["A", "B", "C"]
pg.setConfigOption('imageAxisOrder', 'row-major') # Switch default order to Row-major
correlogram = pg.ImageItem()
# create transform to center the corner element on the origin, for any assigned image:
tr = QtGui.QTransform().translate(-0.5, -0.5)
correlogram.setTransform(tr)
correlogram.setImage(corrMatrix)
plotItem = gr_wid.addPlot() # add PlotItem to the main GraphicsLayoutWidget
plotItem.invertY(True) # orient y axis to run top-to-bottom
plotItem.setDefaultPadding(0.0) # plot without padding data range
plotItem.addItem(correlogram) # display correlogram
# show full frame, label tick marks at top and left sides, with some extra space for labels:
plotItem.showAxes( True, showValues=(True, True, False, False), size=20 )
# define major tick marks and labels:
ticks = [ (idx, label) for idx, label in enumerate( columns ) ]
for side in ('left','top','right','bottom'):
plotItem.getAxis(side).setTicks( (ticks, []) ) # add list of major ticks; no minor ticks
plotItem.getAxis('bottom').setHeight(10) # include some additional space at bottom of figure
colorMap = pg.colormap.get("CET-D1") # choose perceptually uniform, diverging color map
# generate an adjustabled color bar, initially spanning -1 to 1:
bar = pg.ColorBarItem( values=(-1,1), cmap=colorMap)
# link color bar and color map to correlogram, and show it in plotItem:
bar.setImageItem(correlogram, insert_in=plotItem)
mkQApp("Correlation matrix display")
main_window = MainWindow()
## Start Qt event loop
if __name__ == '__main__':
pg.exec()

View File

@ -30,5 +30,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -11,10 +11,8 @@ import initExample
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
from time import perf_counter
# pg.setConfigOptions(useOpenGL=True)
app = pg.mkQApp("MultiPlot Speed Test")
from pyqtgraph.ptime import time
app = QtGui.QApplication([])
plot = pg.plot()
plot.setWindowTitle('pyqtgraph example: MultiPlotSpeedTest')
@ -24,7 +22,7 @@ nPlots = 100
nSamples = 500
curves = []
for idx in range(nPlots):
curve = pg.PlotCurveItem(pen=({'color': (idx, nPlots*1.3), 'width': 1}), skipFiniteCheck=True)
curve = pg.PlotCurveItem(pen=(idx,nPlots*1.3))
plot.addItem(curve)
curve.setPos(0,idx*6)
curves.append(curve)
@ -39,7 +37,7 @@ plot.addItem(rgn)
data = np.random.normal(size=(nPlots*23,nSamples))
ptr = 0
lastTime = perf_counter()
lastTime = time()
fps = None
count = 0
def update():
@ -50,7 +48,7 @@ def update():
curves[i].setData(data[(ptr+i)%data.shape[0]])
ptr += nPlots
now = perf_counter()
now = time()
dt = now - lastTime
lastTime = now
if fps is None:
@ -64,5 +62,8 @@ timer = QtCore.QTimer()
timer.timeout.connect(update)
timer.start(0)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -14,7 +14,7 @@ except:
print("MultiPlot is only used with MetaArray for now (and you do not have the metaarray package)")
exit()
app = pg.mkQApp("MultiPlot Widget Example")
app = QtGui.QApplication([])
mw = QtGui.QMainWindow()
mw.resize(800,800)
pw = MultiPlotWidget()
@ -30,8 +30,11 @@ ma = MetaArray(data, info=[
]},
{'name': 'Time', 'values': linspace(0., 1., 1000), 'units': 's'}
])
pw.plot(ma, pen='y')
pw.plot(ma)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -60,5 +60,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
"""
Display a non-uniform image.
This example displays 2-d data as an image with non-uniformly
distributed sample points.
"""
import initExample ## Add path to library (just for examples; you do not need this)
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import numpy as np
from pyqtgraph.graphicsItems.GradientEditorItem import Gradients
from pyqtgraph.graphicsItems.NonUniformImage import NonUniformImage
RPM2RADS = 2 * np.pi / 60
RADS2RPM = 1 / RPM2RADS
kfric = 1 # [Ws/rad] angular damping coefficient [0;100]
kfric3 = 1.5e-6 # [Ws3/rad3] angular damping coefficient (3rd order) [0;10-3]
psi = 0.2 # [Vs] flux linkage [0.001;10]
res = 5e-3 # [Ohm] resistance [0;100]
v_ref = 200 # [V] reference DC voltage [0;1000]
k_v = 5 # linear voltage coefficient [-100;100]
# create the (non-uniform) scales
tau = np.array([0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 220], dtype=np.float32)
w = np.array([0, 250, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000], dtype=np.float32) * RPM2RADS
v = 380
# calculate the power losses
TAU, W = np.meshgrid(tau, w, indexing='ij')
V = np.ones_like(TAU) * v
P_loss = kfric * W + kfric3 * W ** 3 + (res * (TAU / psi) ** 2) + k_v * (V - v_ref)
P_mech = TAU * W
P_loss[P_mech > 1.5e5] = np.NaN
# green - orange - red
Gradients['gor'] = {'ticks': [(0.0, (74, 158, 71)), (0.5, (255, 230, 0)), (1, (191, 79, 76))], 'mode': 'rgb'}
app = pg.mkQApp("NonUniform Image Example")
win = QtGui.QMainWindow()
cw = pg.GraphicsLayoutWidget()
win.show()
win.resize(600, 400)
win.setCentralWidget(cw)
win.setWindowTitle('pyqtgraph example: Non-uniform Image')
p = cw.addPlot(title="Power Losses [W]", row=0, col=0)
lut = pg.HistogramLUTItem(orientation="horizontal")
p.setMouseEnabled(x=False, y=False)
cw.nextRow()
cw.addItem(lut)
# load the gradient
lut.gradient.loadPreset('gor')
image = NonUniformImage(w * RADS2RPM, tau, P_loss.T)
image.setLookupTable(lut, autoLevel=True)
image.setZValue(-1)
p.addItem(image)
h = image.getHistogram()
lut.plot.setData(*h)
p.showGrid(x=True, y=True)
p.setLabel(axis='bottom', text='Speed [rpm]')
p.setLabel(axis='left', text='Torque [Nm]')
# elevate the grid lines
p.axes['bottom']['item'].setZValue(1000)
p.axes['left']['item'].setZValue(1000)
if __name__ == '__main__':
pg.exec()

View File

@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
"""
Demonstrates very basic use of PColorMeshItem
"""
## Add path to library (just for examples; you do not need this)
import initExample
from pyqtgraph.Qt import QtCore
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("PColorMesh Example")
## Create window with GraphicsView widget
win = pg.GraphicsLayoutWidget()
win.show() ## show widget alone in its own window
win.setWindowTitle('pyqtgraph example: pColorMeshItem')
view = win.addViewBox()
## Create data
# To enhance the non-grid meshing, we randomize the polygon vertices per and
# certain amount
randomness = 5
# x and y being the vertices of the polygons, they share the same shape
# However the shape can be different in both dimension
xn = 50 # nb points along x
yn = 40 # nb points along y
x = np.repeat(np.arange(1, xn+1), yn).reshape(xn, yn)\
+ np.random.random((xn, yn))*randomness
y = np.tile(np.arange(1, yn+1), xn).reshape(xn, yn)\
+ np.random.random((xn, yn))*randomness
x.sort(axis=0)
y.sort(axis=0)
# z being the color of the polygons its shape must be decreased by one in each dimension
z = np.exp(-(x*xn)**2/1000)[:-1,:-1]
## Create image item
edgecolors = None
antialiasing = False
# edgecolors = {'color':'w', 'width':2} # May be uncommened to see edgecolor effect
# antialiasing = True # May be uncommened to see antialiasing effect
pcmi = pg.PColorMeshItem(edgecolors=edgecolors, antialiasing=antialiasing)
view.addItem(pcmi)
## Set the animation
fps = 25 # Frame per second of the animation
# Wave parameters
wave_amplitude = 3
wave_speed = 0.3
wave_length = 10
color_speed = 0.3
timer = QtCore.QTimer()
timer.setSingleShot(True)
# not using QTimer.singleShot() because of persistence on PyQt. see PR #1605
i=0
def updateData():
global i
## Display the new data set
new_x = x
new_y = y+wave_amplitude*np.cos(x/wave_length+i)
new_z = np.exp(-(x-np.cos(i*color_speed)*xn)**2/1000)[:-1,:-1]
pcmi.setData(new_x,
new_y,
new_z)
i += wave_speed
timer.start(1000//fps)
timer.timeout.connect(updateData)
updateData()
if __name__ == '__main__':
pg.exec()

View File

@ -30,5 +30,8 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

View File

@ -11,7 +11,8 @@ from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
import pyqtgraph as pg
app = pg.mkQApp("Plot Auto Range Example")
#QtGui.QApplication.setGraphicsSystem('raster')
app = QtGui.QApplication([])
#mw = QtGui.QMainWindow()
#mw.resize(800,800)
@ -40,5 +41,10 @@ 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__':
pg.exec()
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()

Some files were not shown because too many files have changed in this diff Show More