Skip to main content

PyQtGraph library providing thread-safe plot curves with underlying (ring) buffers.

Project description

https://img.shields.io/pypi/v/dvg-pyqtgraph-threadsafe https://img.shields.io/pypi/pyversions/dvg-pyqtgraph-threadsafe Requirements Status https://img.shields.io/badge/code%20style-black-000000.svg https://img.shields.io/badge/License-MIT-purple.svg

DvG_PyQtGraph_ThreadSafe

PyQtGraph library providing thread-safe plot curves with underlying (ring) buffers.

Installation:

pip install dvg-pyqtgraph-threadsafe
https://raw.githubusercontent.com/Dennis-van-Gils/python-dvg-pyqtgraph-threadsafe/master/demos/demo_pyqtgraph_threadsafe.png

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.

Usage:

import sys
from PyQt5 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_())

API

class ThreadSafeCurve(capacity: int, linked_curve: pg.PlotDataItem, shift_right_x_to_zero: bool = False, use_ringbuffer: bool = True)

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):
Maximum number of (x, y)-data points the buffer can store.
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, optional):

When True, the (x, y)-data buffers 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 False, the (x, y)-data buffers are each a regular array buffer. Use method setData() to set the data.

Default: True

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

  • setVisible(state: bool = True)

  • 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().

class HistoryChartCurve(capacity: int, linked_curve: pg.PlotDataItem)

Bases: 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.

class BufferedPlotCurve(capacity: int, linked_curve: pg.PlotDataItem)

Bases: 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.

class PlotCurve(capacity: int, linked_curve: pg.PlotDataItem)

Bases: 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.

class LegendSelect(curves: List[Union[pg.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)

Bases: 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.

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"]

Example grid:

□ Curve 1  [  /  ]
□ Curve 2  [  /  ]
□ Curve 3  [  /  ]
[ Show / Hide all]
Args:
linked_curves (List[Union[pyqtgraph.PlotDataItem, ThreadSafeCurve]]):
List of pyqtgraph.PlotDataItem or ThreadSafeCurve 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.

Changelog

3.0.1 (2020-08-07)

Bug-fixes:

  • The use of typing.TypedDict broke support under Python 3.6 and 3.7. Fixed by conditional import typing_extensions.
  • Curve plotting was broken when 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.

3.0.0 (2020-08-07)

  • Renamed parameter LegendSelect.curves to LegendSelect.linked_curves
  • Changed base of class LegendSelect() from QWidget to QObject
  • Added class PlotManager()

2.0.1 (2020-08-03)

  • Workaround: 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().

2.0.0 (2020-08-02)

  • Method names are now conform the PyQtGraph naming style. I.e. setData() vs. set_data(), etc.
  • The default values of PyQtGraph are no longer being overwritten.
  • Added class LegendSelect

1.0.0 (2020-07-30)

  • First release on PyPI

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for dvg-pyqtgraph-threadsafe, version 3.0.1
Filename, size File type Python version Upload date Hashes
Filename, size dvg_pyqtgraph_threadsafe-3.0.1-py3-none-any.whl (13.6 kB) File type Wheel Python version py3 Upload date Hashes View
Filename, size dvg-pyqtgraph-threadsafe-3.0.1.tar.gz (90.8 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page