
Security News
Bun 1.2.19 Adds Isolated Installs for Better Monorepo Support
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.
dvg-pyqtgraph-threadsafe
Advanced tools
PyQtGraph library providing thread-safe plot curves with underlying (ring) buffers.
.. image:: https://img.shields.io/pypi/v/dvg-pyqtgraph-threadsafe :target: https://pypi.org/project/dvg-pyqtgraph-threadsafe .. image:: https://img.shields.io/pypi/pyversions/dvg-pyqtgraph-threadsafe :target: https://pypi.org/project/dvg-pyqtgraph-threadsafe .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black .. image:: https://img.shields.io/badge/License-MIT-purple.svg :target: https://github.com/Dennis-van-Gils/python-dvg-pyqtgraph-threadsafe/blob/master/LICENSE.txt
PyQtGraph library providing thread-safe plot curves with underlying (ring) buffers.
Supports PyQt5, PyQt6, PySide2 and PySide6.
Installation::
pip install dvg-pyqtgraph-threadsafe
You must also ensure a Qt library is installed in your Python environment as this library will not install one for you. Pick one (personal recommendation PySide6)::
pip install pyqt5
pip install pyqt6
pip install pyside2
pip install pyside6
Futhermore, you might want to enable OpenGL hardware accelerated plotting by installing PyOpenGL::
pip install pyopengl
demos folder <https://github.com/Dennis-van-Gils/python-dvg-pyqtgraph-threadsafe/tree/master/demos>
__.benchmark folder <https://github.com/Dennis-van-Gils/python-dvg-pyqtgraph-threadsafe/tree/master/benchmark>
__.here <https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo/blob/master/README.rst#pyqtgraph--opengl-performance>
__.Classes HistoryChartCurve
, BufferedPlotCurve
& PlotCurve
wrap around
a pyqtgraph.PlotDataItem
instance, called a curve for convenience. Data
can be safely appended or set from out of any thread.
The (x, y)-curve data is buffered internally to the class, relying on either a circular/ring buffer or a regular array buffer:
HistoryChartCurve
Ring buffer. The plotted x-data will be shifted such that the
right-side is always set to 0. I.e., when x
denotes time, the data is
plotted backwards in time, hence the name history chart. The most
recent data is on the right-side of the ring buffer.
BufferedPlotCurve
Ring buffer. Data will be plotted as is. Can also act as a Lissajous
figure.
PlotCurve
Regular array buffer. Data will be plotted as is.
.. code-block:: python
import sys
from qtpy import QtWidgets
import pyqtgraph as pg
from dvg_pyqtgraph_threadsafe import HistoryChartCurve
class MainWindow(QtWidgets.QWidget):
def __init__(self, parent=None, **kwargs):
super().__init__(parent, **kwargs)
self.gw = pg.GraphicsLayoutWidget()
self.plot_1 = self.gw.addPlot()
# Create a HistoryChartCurve and have it wrap around a new
# PlotDataItem as set by argument `linked_curve`.
self.tscurve_1 = HistoryChartCurve(
capacity=5,
linked_curve=self.plot_1.plot(pen=pg.mkPen('r')),
)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(self.gw)
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
# The following line could have been executed from inside of another thread:
window.tscurve_1.extendData([1, 2, 3, 4, 5], [10, 20, 30, 20, 10])
# Draw the curve from out of the main thread
window.tscurve_1.update()
window.show()
sys.exit(app.exec())
If you intend to use this library to plot 100.000s of points at a high frame rate on your integrated GPU instead of on a dedicated (performance) GPU, you might run into performance issues. Even when OpenGL hardware acceleration is enabled.
I recommend you run the performance benchmark found in the
benchmark folder <https://github.com/Dennis-van-Gils/python-dvg-pyqtgraph-threadsafe/tree/master/benchmark>
.
It can test for different PyQtGraph versions and Qt libraries on your
GPU of choice. I have found that the older pyqtgraph 0.11
library sometimes
results in a better performance than pyqtgraph 0.12
on integrated GPUs.
See benchmark results <https://github.com/Dennis-van-Gils/python-dvg-pyqtgraph-threadsafe/blob/master/benchmark/benchmark_results.rst>
.
.. code-block:: python
ThreadSafeCurve(
capacity: int | None,
linked_curve: pyqtgraph.PlotDataItem,
shift_right_x_to_zero: bool = False,
use_ringbuffer=None,
)
.. Note::
Provides the base class for a thread-safe plot *curve* to which
(x, y)-data can be safely appended or set from out of any thread. It
will wrap around the passed argument ``linked_curve`` of type
``pyqtgraph.PlotDataItem`` and will manage the (x, y)-data buffers
underlying the curve.
Intended multi-threaded operation: One or more threads push new data
into the ``ThreadSafeCurve``-buffers. Another thread performs the GUI
refresh by calling ``update()`` which will redraw the curve according
to the current buffer contents.
Args:
capacity (``int``, optional):
When an integer is supplied it defines the maximum number op points
each of the x-data and y-data buffers can store. The x-data buffer
and the y-data buffer are each a ring buffer. New readings are
placed at the end (right-side) of the buffer, pushing out the oldest
readings when the buffer has reached its maximum capacity (FIFO).
Use methods ``appendData()`` and ``extendData()`` to push in new
data.
When ``None`` is supplied the x-data and y-data buffers are each a
regular array buffer of undefined length. Use method ``setData()``
to set the data.
linked_curve (``pyqtgraph.PlotDataItem``):
Instance of ``pyqtgraph.PlotDataItem`` to plot the buffered
data out into.
shift_right_x_to_zero (``bool``, optional):
When plotting, should the x-data be shifted such that the
right-side is always set to 0? Useful for history charts.
Default: False
use_ringbuffer (``bool``, deprecated):
Deprecated since v3.1.0. Defined for backwards compatibility.
Simply supply a value for ``capacity`` to enable use of a ring
buffer.
Attributes:
x_axis_divisor (``float``):
The x-data in the buffer will be divided by this factor when the
plot curve is drawn. Useful to, e.g., transform the x-axis units
from milliseconds to seconds or minutes.
Default: 1
y_axis_divisor (``float``):
Same functionality as ``x_axis_divisor``.
Default: 1
Methods:
* ``appendData(x, y)``
Append a single (x, y)-data point to the ring buffer.
* ``extendData(x_list, y_list)``
Extend the ring buffer with a list of (x, y)-data points.
* ``setData(x_list, y_list)``
Set the (x, y)-data of the regular array buffer.
* ``update(create_snapshot: bool = True)``
Update the data behind the curve by creating a snapshot of the
current contents of the buffer, and redraw the curve on screen.
You can suppress updating the data behind the curve by setting parameter
``create_snapshot`` to False. The curve will then only be redrawn
based on the old data. This is useful when the plot is paused.
* ``clear()``
Clear the contents of the curve and redraw.
* ``name()``
Get the name of the curve.
* ``isVisible() -> bool``
Is the curve visible?
* ``setVisible(state: bool = True)``
Set the visibility of the curve.
* ``setDownsampling(*args, **kwargs)``
All arguments will be passed onto method
``pyqtgraph.PlotDataItem.setDownsampling()`` of the underlying curve.
Properties:
* ``size -> Tuple[int, int]``:
Number of elements currently contained in the underlying (x, y)-
buffers of the curve. Note that this is not necessarily the number of
elements of the currently drawn curve. Instead, it reflects the current
sizes of the data buffers behind it that will be drawn onto screen by
the next call to ``update()``.
.. code-block:: python
HistoryChartCurve(
capacity: int,
linked_curve: pyqtgraph.PlotDataItem,
)
.. Note::
Inherits from: ``ThreadSafeCurve``
Provides a thread-safe curve with underlying ring buffers for the
(x, y)-data. New readings are placed at the end (right-side) of the
buffer, pushing out the oldest readings when the buffer has reached its
maximum capacity (FIFO). Use methods ``appendData()`` and
``extendData()`` to push in new data.
The plotted x-data will be shifted such that the right-side is always
set to 0. I.e., when ``x`` denotes time, the data is plotted backwards
in time, hence the name *history* chart.
See class ``ThreadSafeCurve`` for more details.
.. code-block:: python
BufferedPlotCurve(
capacity: int,
linked_curve: pyqtgraph.PlotDataItem,
)
.. Note::
Inherits from: ``ThreadSafeCurve``
Provides a thread-safe curve with underlying ring buffers for the
(x, y)-data. New readings are placed at the end (right-side) of the
buffer, pushing out the oldest readings when the buffer has reached its
maximum capacity (FIFO). Use methods ``appendData()`` and
``extendData()`` to push in new data.
See class ``ThreadSafeCurve`` for more details.
.. code-block:: python
PlotCurve(
capacity: int,
linked_curve: pyqtgraph.PlotDataItem,
)
.. Note::
Inherits from: ``ThreadSafeCurve``
Provides a thread-safe curve with underlying regular array buffers
for the (x, y)-data. Use method ``setData()`` to set the data.
See class ``ThreadSafeCurve`` for more details.
.. code-block:: python
LegendSelect(
linked_curves: Sequence[pyqtgraph.PlotDataItem | ThreadSafeCurve],
hide_toggle_button: bool = False,
box_bg_color: QtGui.QColor = QtGui.QColor(0, 0, 0),
box_width: int = 40,
box_height: int = 23,
parent=None,
)
.. Note:: Inherits from: PyQt5.QtCore.QObject
Creates and manages a legend of all passed curves with checkboxes to
show or hide each curve. The legend ends with a push button to show or
hide all curves in one go. The full set of GUI elements is contained in
attribute ``grid`` of type ``PyQt5.QtWidget.QGridLayout`` to be added to
your GUI.
Example grid::
□ Curve 1 [ / ]
□ Curve 2 [ / ]
□ Curve 3 [ / ]
[ Show / Hide all]
The initial visibility, name and pen of each curve will be retrieved
from the members within the passed curves, i.e.:
* ``curve.isVisible()``
* ``curve.name()``
* ``curve.opts["pen"]``
Args:
linked_curves (``Sequence[pyqtgraph.PlotDataItem | ThreadSafeCurve]``):
Sequence of ``pyqtgraph.PlotDataItem`` or ``ThreadSafeCurve``
instances to be controlled by the legend.
hide_toggle_button (``bool``, optional):
Default: False
box_bg_color (``QtGui.QColor``, optional):
Background color of the legend boxes.
Default: ``QtGui.QColor(0, 0, 0)``
box_width (``int``, optional):
Default: 40
box_height (``int``, optional):
Default: 23
Attributes:
chkbs (``List[PyQt5.QtWidgets.QCheckbox]``):
List of checkboxes to control the visiblity of each curve.
painted_boxes (``List[PyQt5.QtWidgets.QWidget]``):
List of painted boxes illustrating the pen of each curve.
qpbt_toggle (``PyQt5.QtWidgets.QPushButton``):
Push button instance that toggles showing/hiding all curves in
one go.
grid (``PyQt5.QtWidgets.QGridLayout``):
The full set of GUI elements combined into a grid to be added
to your GUI.
.. code-block:: python
PlotManager(
parent=None,
)
.. Note:: Inherits from: PyQt5.QtCore.QObject
Creates and manages a collection of pushbuttons with predefined actions
operating on the linked plots and curves. The full set of pushbuttons is
contained in attribute ``grid`` of type ``PyQt5.QtWidget.QGridLayout`` to be
added to your GUI.
Example grid::
[ Full range ]
[auto x] [auto y]
[ 0:30 ]
[ 1:00 ]
[ 3:00 ]
[ 10:00 ]
[ Clear ]
The grid starts empty and is build up by calling the following methods:
- ``add_autorange_buttons()``: Adds the [Full range], [auto x] and
[auto y] buttons.
- ``add_preset_buttons()``: Adds presets on the x-axis range to zoom to.
- ``add_clear_button()``: Adds the 'Clear' button.
Args:
parent (``PyQt5.QtWidgets.QWidget``):
Needs to be set to the parent ``QWidget`` for the ``QMessageBox`` as
fired by button ``Clear`` to appear centered and modal to.
Attributes:
grid (``PyQt5.QtWidgets.QGridLayout``):
The full set of pushbuttons combined into a grid to be added
to your GUI.
Code quality improvements:
qtpy
library instead of my own Qt5/6 mechanismPlotManager
.linked_curves
and linked_plots
are hinted as Sequence
instead
of as List
Added support:
ImportError
instead of general Exception
x
and y
to set the curve data and set
the flag skipFiniteCheck=True
. Both save (marginal) CPU time.isfinite
boolean return array was operating as mask on the RingBuffer, which
fails. The boolean return array now always operates on numpy.ndarray.paintGL
of pyqtgraph\graphicsItems\PlotCurveItem.py
:
Added explicit check to ensure that the curve is only drawn when it has more
than 1 point.python=3.10
and pyqtgraph==0.13.1
QMessageBox.Yes
--> QMessageBox.StandardButton.Yes
capacity
from init PlotCurve()
. It is
not using a ringbuffer and, hence, does not need a capacity.Bug-fixes:
typing.TypedDict
broke support under Python 3.6
and 3.7. Fixed by conditional import typing_extensions
.setClipToView(True)
and the curve
data extended past the viewbox limits, when not using OpenGL. The cause was
my incorrect calculation of connect
. Fixed by commenting out connect
again. Curves will now show continuously (linear interpolation) whenever a NaN
is encountered, instead of as correctly fragmented. That's acceptable.LegendSelect.curves
to
LegendSelect.linked_curves
LegendSelect()
from QWidget
to QObject
PlotManager()
PyQt5 >= 5.12.3
causes a bug in PyQtGraph
where a
curve won't render if it contains NaNs (but only in the case when OpenGL is
disabled). The curve will now be displayed correctly, i.e., fragmented
whenever a NaN is encountered. When OpenGL is enabled, linear interpolation
will occur at the gaps as per pyqtgraph.plotCurveItem.paintGL()
.PyQtGraph
naming style. I.e.
setData()
vs. set_data()
, etc.PyQtGraph
are no longer being overwritten.LegendSelect
FAQs
PyQtGraph library providing thread-safe plot curves with underlying (ring) buffers.
We found that dvg-pyqtgraph-threadsafe demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Bun 1.2.19 introduces isolated installs for smoother monorepo workflows, along with performance boosts, new tooling, and key compatibility fixes.
Security News
Popular npm packages like eslint-config-prettier were compromised after a phishing attack stole a maintainer’s token, spreading malicious updates.
Security News
/Research
A phishing attack targeted developers using a typosquatted npm domain (npnjs.com) to steal credentials via fake login pages - watch out for similar scams.