From 6289bc52b5f11af2172315a3555f1b722f093c29 Mon Sep 17 00:00:00 2001
From: Dennis van Gils <37263907+Dennis-van-Gils@users.noreply.github.com>
Date: Thu, 30 Jul 2020 22:14:27 +0200
Subject: [PATCH] Bump dependence dvg-pyqtgraph-threadsafe==1.0
---
DvG_pyqt_ChartHistory.py | 131 --------------------------
README.rst | 1 +
demo_A_GUI_full.py | 80 ++++++++--------
demo_B_GUI_minimal.py | 27 +++---
demo_C_singlethread_for_comparison.py | 68 ++++++-------
demo_D_no_GUI.py | 6 +-
demo_E_no_GUI.py | 6 +-
list_conda.txt | 1 +
list_pip.txt | 1 +
requirements.txt | 3 +-
10 files changed, 93 insertions(+), 231 deletions(-)
delete mode 100644 DvG_pyqt_ChartHistory.py
diff --git a/DvG_pyqt_ChartHistory.py b/DvG_pyqt_ChartHistory.py
deleted file mode 100644
index 6ea4739..0000000
--- a/DvG_pyqt_ChartHistory.py
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/env python3
-# -*- coding: utf-8 -*-
-"""Class ChartHistory manages two data arrays `x` and `y` that serve as history
-buffers for displaying e.g. a strip chart or Lissajous curve. It is
-thread-safe and relies on the PyQtGraph library for fast plotting to screen.
-Intended multithreaded operation: One thread does the data acquisition and
-pushes new data points into the history buffers by calling `add_new_reading(s)`,
-and another thread performs the GUI refresh and redraws the data behind the plot
-by calling `update_curve`.
-
-New readings are stored in an history array of fixed size. Newest readings
-are placed at the end of the array. Array full? -> FIFO.
-
-Class:
- ChartHistory(chart_history_length, plot_data_item):
- Args:
- chart_history_length:
- Number of data points to store in each history buffer.
- plot_data_item:
- Instance of `pyqtgraph.PlotDataItem` to plot out the buffered
- data to.
-
- Methods:
- apply_downsampling(...):
- Calls the downsampling routines of PyQtGraph, but in the future
- I will provide my own routines for downsampling here.
- add_new_reading(...):
- Add single data point (x, y) to the history buffers.
- add_new_readings(...):
- Add lists of data points (list_x, list_y) to the history
- buffers.
- update_curve():
- Update the data behind the curve and redraw.
- clear():
- Clear buffers.
-
- Important member:
- x_axis_divisor:
- If the x-data is time, you can use this divisor value to
- transform the x-axis units from e.g. milliseconds to seconds or
- minutes.
- y_axis_divisor:
- Same functionality as x_axis_divisor
-"""
-__author__ = "Dennis van Gils"
-__authoremail__ = "vangils.dennis@gmail.com"
-__url__ = "https://github.com/Dennis-van-Gils/DvG_PyQt_misc"
-__date__ = "14-09-2018"
-__version__ = "1.0.0"
-
-import collections
-
-import numpy as np
-from PyQt5 import QtCore
-import pyqtgraph as pg
-
-class ChartHistory(object):
- def __init__(self,
- chart_history_length,
- plot_data_item: pg.PlotDataItem=None):
- self.chart_history_length = chart_history_length
- self.curve = plot_data_item # Instance of [pyqtgraph.PlotDataItem]
- self.mutex = QtCore.QMutex() # For the case of multithreaded access
-
- # If the x-data is time, you can use this divisor value to transform
- # the x-axis units from e.g. milliseconds to seconds or minutes.
- self.x_axis_divisor = 1
- self.y_axis_divisor = 1
-
- self._x = collections.deque(maxlen=chart_history_length)
- self._y = collections.deque(maxlen=chart_history_length)
- self._x_snapshot = [0]
- self._y_snapshot = [0]
-
- # Performance boost: Do not plot data outside of visible range
- self.curve.clipToView = True
-
- # Default to no downsampling
- self.curve.setDownsampling(ds=1, auto=False, method='mean')
-
- def apply_downsampling(self, do_apply=True, ds=4):
- if do_apply:
- # Speed up plotting, needed for keeping the GUI responsive when
- # using large datasets
- self.curve.setDownsampling(ds=ds, auto=False, method='mean')
- else:
- self.curve.setDownsampling(ds=1, auto=False, method='mean')
-
- def add_new_reading(self, x, y):
- locker = QtCore.QMutexLocker(self.mutex)
- self._x.append(x)
- self._y.append(y)
- locker.unlock()
-
- def add_new_readings(self, x_list, y_list):
- locker = QtCore.QMutexLocker(self.mutex)
- self._x.extend(x_list)
- self._y.extend(y_list)
- locker.unlock()
-
- def update_curve(self):
- """Creates a snapshot of the buffered data, which is a fast operation,
- followed by updating the data behind the curve and redrawing it, which
- is a slow operation. Hence, the use of a snapshot creation, which is
- locked my a mutex, followed by a the mutex unlocked redrawing.
- """
-
- # First create a snapshot of the buffered data. Fast.
- locker = QtCore.QMutexLocker(self.mutex)
- self._x_snapshot = np.copy(self._x)
- self._y_snapshot = np.copy(self._y)
- #print("numel x: %d, numel y: %d" %
- # (self._x_snapshot.size, self._y_snapshot.size))
- locker.unlock()
-
- # Now update the data behind the curve and redraw the curve. Slow
- if self.curve is not None:
- if ((len(self._x_snapshot) == 0) or
- (np.alltrue(np.isnan(self._y_snapshot)))):
- self.curve.setData([0], [0])
- else:
- self.curve.setData((self._x_snapshot - self._x_snapshot[-1])
- / float(self.x_axis_divisor),
- self._y_snapshot
- / float(self.y_axis_divisor))
-
- def clear(self):
- locker = QtCore.QMutexLocker(self.mutex)
- self._x.clear()
- self._y.clear()
- locker.unlock()
diff --git a/README.rst b/README.rst
index 65f73d5..21055fb 100644
--- a/README.rst
+++ b/README.rst
@@ -24,6 +24,7 @@ Other depencies you'll need for this demo can be installed by running::
* `dvg-debug-functions `_
* `dvg-qdeviceio `_
* `dvg-devices `_
+* `dvg-pyqtgraph-threadsafe `_
* `psutil `_
* `pySerial `_
* `NumPy `_
diff --git a/demo_A_GUI_full.py b/demo_A_GUI_full.py
index 4d34cc9..5cf043a 100644
--- a/demo_A_GUI_full.py
+++ b/demo_A_GUI_full.py
@@ -6,8 +6,8 @@
__author__ = "Dennis van Gils"
__authoremail__ = "vangils.dennis@gmail.com"
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
-__date__ = "24-07-2020"
-__version__ = "5.0"
+__date__ = "30-07-2020"
+__version__ = "6.0"
# pylint: disable=bare-except, broad-except
import os
@@ -23,11 +23,11 @@
import pyqtgraph as pg
from dvg_debug_functions import tprint, dprint, print_fancy_traceback as pft
+from dvg_pyqtgraph_threadsafe import HistoryChartCurve
from dvg_devices.Arduino_protocol_serial import Arduino
from dvg_qdeviceio import QDeviceIO
from DvG_pyqt_FileLogger import FileLogger
-from DvG_pyqt_ChartHistory import ChartHistory
from DvG_pyqt_controls import create_Toggle_button, SS_GROUP
try:
@@ -184,7 +184,7 @@ def __init__(self, parent=None, **kwargs):
# Bottom frame
# -------------------------
- # Create PlotItem
+ # GraphicsWindow
self.gw_chart = pg.GraphicsWindow()
self.gw_chart.setBackground([20, 20, 20])
self.pi_chart = self.gw_chart.addPlot()
@@ -209,10 +209,12 @@ def __init__(self, parent=None, **kwargs):
self.pi_chart.getAxis("left").setStyle(tickTextOffset=20)
self.pi_chart.getAxis("left").setWidth(120)
- # Create ChartHistory and PlotDataItem and link them together
- PEN_01 = pg.mkPen(color=[255, 255, 90], width=3)
- num_samples = round(CHART_HISTORY_TIME * 1e3 / DAQ_INTERVAL_MS)
- self.CH_1 = ChartHistory(num_samples, self.pi_chart.plot(pen=PEN_01))
+ self.history_chart_curve = HistoryChartCurve(
+ capacity=round(CHART_HISTORY_TIME * 1e3 / DAQ_INTERVAL_MS),
+ linked_curve=self.pi_chart.plot(
+ pen=pg.mkPen(color=[255, 255, 90], width=3)
+ ),
+ )
# 'Readings'
p = {"readOnly": True}
@@ -300,7 +302,7 @@ def process_qpbt_clear_chart(self):
)
if reply == QtWid.QMessageBox.Yes:
- self.CH_1.clear()
+ self.history_chart_curve.clear()
@QtCore.pyqtSlot()
def process_qpbt_record(self):
@@ -309,6 +311,10 @@ def process_qpbt_record(self):
else:
file_logger.stopping = True
+ @QtCore.pyqtSlot(str)
+ def set_text_qpbt_record(self, text_str):
+ self.qpbt_record.setText(text_str)
+
@QtCore.pyqtSlot()
def process_qpbt_wave_sine(self):
qdev_ard.send(ard.write, "sine")
@@ -321,37 +327,25 @@ def process_qpbt_wave_square(self):
def process_qpbt_wave_sawtooth(self):
qdev_ard.send(ard.write, "sawtooth")
- @QtCore.pyqtSlot(str)
- def set_text_qpbt_record(self, text_str):
- self.qpbt_record.setText(text_str)
-
-
-# ------------------------------------------------------------------------------
-# update_GUI
-# ------------------------------------------------------------------------------
-
-
-@QtCore.pyqtSlot()
-def update_GUI():
- str_cur_date, str_cur_time, _ = get_current_date_time()
- window.qlbl_cur_date_time.setText("%s %s" % (str_cur_date, str_cur_time))
- window.qlbl_update_counter.setText("%i" % qdev_ard.update_counter_DAQ)
- window.qlbl_DAQ_rate.setText("DAQ: %.1f Hz" % qdev_ard.obtained_DAQ_rate_Hz)
- window.qlin_reading_t.setText("%.3f" % state.time)
- window.qlin_reading_1.setText("%.4f" % state.reading_1)
-
-
-# ------------------------------------------------------------------------------
-# update_chart
-# ------------------------------------------------------------------------------
-
+ @QtCore.pyqtSlot()
+ def update_GUI(self):
+ str_cur_date, str_cur_time, _ = get_current_date_time()
+ self.qlbl_cur_date_time.setText(
+ "%s %s" % (str_cur_date, str_cur_time)
+ )
+ self.qlbl_update_counter.setText("%i" % qdev_ard.update_counter_DAQ)
+ self.qlbl_DAQ_rate.setText(
+ "DAQ: %.1f Hz" % qdev_ard.obtained_DAQ_rate_Hz
+ )
+ self.qlin_reading_t.setText("%.3f" % state.time)
+ self.qlin_reading_1.setText("%.4f" % state.reading_1)
-@QtCore.pyqtSlot()
-def update_chart():
- if DEBUG:
- tprint("update_curve")
+ @QtCore.pyqtSlot()
+ def update_chart(self):
+ if DEBUG:
+ tprint("update_curve")
- window.CH_1.update_curve()
+ self.history_chart_curve.update()
# ------------------------------------------------------------------------------
@@ -430,8 +424,8 @@ def DAQ_function():
if use_PC_time:
state.time = time.perf_counter()
- # Add readings to chart histories
- window.CH_1.add_new_reading(state.time, state.reading_1)
+ # Add readings to chart history
+ window.history_chart_curve.append_data(state.time, state.reading_1)
# Logging to file
if file_logger.starting:
@@ -515,14 +509,14 @@ def DAQ_function():
qdev_ard.create_worker_DAQ(
DAQ_function = DAQ_function,
DAQ_interval_ms = DAQ_INTERVAL_MS,
- critical_not_alive_count = 3,
+ critical_not_alive_count = 1,
debug = DEBUG,
)
# fmt: on
qdev_ard.create_worker_jobs(debug=DEBUG)
# Connect signals to slots
- qdev_ard.signal_DAQ_updated.connect(update_GUI)
+ qdev_ard.signal_DAQ_updated.connect(window.update_GUI)
qdev_ard.signal_connection_lost.connect(notify_connection_lost)
# Start workers
@@ -534,7 +528,7 @@ def DAQ_function():
timer_chart = QtCore.QTimer()
# timer_chart.setTimerType(QtCore.Qt.PreciseTimer)
- timer_chart.timeout.connect(update_chart)
+ timer_chart.timeout.connect(window.update_chart)
timer_chart.start(CHART_INTERVAL_MS)
# --------------------------------------------------------------------------
diff --git a/demo_B_GUI_minimal.py b/demo_B_GUI_minimal.py
index 05c8c31..7b10593 100644
--- a/demo_B_GUI_minimal.py
+++ b/demo_B_GUI_minimal.py
@@ -6,8 +6,8 @@
__author__ = "Dennis van Gils"
__authoremail__ = "vangils.dennis@gmail.com"
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
-__date__ = "24-07-2020"
-__version__ = "5.0"
+__date__ = "30-07-2020"
+__version__ = "6.0"
# pylint: disable=bare-except, broad-except
import os
@@ -23,11 +23,10 @@
import pyqtgraph as pg
from dvg_debug_functions import dprint, print_fancy_traceback as pft
+from dvg_pyqtgraph_threadsafe import HistoryChartCurve
from dvg_devices.Arduino_protocol_serial import Arduino
from dvg_qdeviceio import QDeviceIO
-from DvG_pyqt_ChartHistory import ChartHistory
-
try:
import OpenGL.GL as gl # pylint: disable=unused-import
except:
@@ -98,7 +97,7 @@ def __init__(self, parent=None, **kwargs):
self.setGeometry(350, 50, 800, 660)
self.setWindowTitle("Arduino & PyQt multithread demo")
- # Create PlotItem
+ # GraphicsWindow
self.gw_chart = pg.GraphicsWindow()
self.gw_chart.setBackground([20, 20, 20])
self.pi_chart = self.gw_chart.addPlot()
@@ -113,10 +112,12 @@ def __init__(self, parent=None, **kwargs):
disableAutoRange=True,
)
- # Create ChartHistory and PlotDataItem and link them together
- PEN_01 = pg.mkPen(color=[255, 255, 90], width=3)
- num_samples = round(CHART_HISTORY_TIME * 1e3 / DAQ_INTERVAL_MS)
- self.CH_1 = ChartHistory(num_samples, self.pi_chart.plot(pen=PEN_01))
+ self.history_chart_curve = HistoryChartCurve(
+ capacity=round(CHART_HISTORY_TIME * 1e3 / DAQ_INTERVAL_MS),
+ linked_curve=self.pi_chart.plot(
+ pen=pg.mkPen(color=[255, 255, 90], width=3)
+ ),
+ )
vbox = QtWid.QVBoxLayout(self)
vbox.addWidget(self.gw_chart, 1)
@@ -175,8 +176,8 @@ def DAQ_function():
if use_PC_time:
state.time = time.perf_counter()
- # Add readings to chart histories
- window.CH_1.add_new_reading(state.time, state.reading_1)
+ # Add readings to chart history
+ window.history_chart_curve.append_data(state.time, state.reading_1)
return True
@@ -234,7 +235,7 @@ def DAQ_function():
qdev_ard.create_worker_DAQ(
DAQ_function = DAQ_function,
DAQ_interval_ms = DAQ_INTERVAL_MS,
- critical_not_alive_count = 3,
+ critical_not_alive_count = 1,
debug = DEBUG,
)
# fmt: on
@@ -247,7 +248,7 @@ def DAQ_function():
# --------------------------------------------------------------------------
timer_chart = QtCore.QTimer()
- timer_chart.timeout.connect(window.CH_1.update_curve)
+ timer_chart.timeout.connect(window.history_chart_curve.update)
timer_chart.start(CHART_INTERVAL_MS)
# --------------------------------------------------------------------------
diff --git a/demo_C_singlethread_for_comparison.py b/demo_C_singlethread_for_comparison.py
index ce287a6..f05955a 100644
--- a/demo_C_singlethread_for_comparison.py
+++ b/demo_C_singlethread_for_comparison.py
@@ -12,8 +12,8 @@
__author__ = "Dennis van Gils"
__authoremail__ = "vangils.dennis@gmail.com"
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
-__date__ = "24-07-2020"
-__version__ = "5.0"
+__date__ = "30-07-2020"
+__version__ = "6.0"
# pylint: disable=bare-except, broad-except
import os
@@ -29,10 +29,10 @@
import pyqtgraph as pg
from dvg_debug_functions import tprint, dprint, print_fancy_traceback as pft
+from dvg_pyqtgraph_threadsafe import HistoryChartCurve
from dvg_devices.Arduino_protocol_serial import Arduino
from DvG_pyqt_FileLogger import FileLogger
-from DvG_pyqt_ChartHistory import ChartHistory
from DvG_pyqt_controls import create_Toggle_button, SS_GROUP
try:
@@ -158,7 +158,7 @@ def __init__(self, parent=None, **kwargs):
# Bottom frame
# -------------------------
- # Create PlotItem
+ # GraphicsWindow
self.gw_chart = pg.GraphicsWindow()
self.gw_chart.setBackground([20, 20, 20])
self.pi_chart = self.gw_chart.addPlot()
@@ -173,10 +173,12 @@ def __init__(self, parent=None, **kwargs):
disableAutoRange=True,
)
- # Create ChartHistory and PlotDataItem and link them together
- PEN_01 = pg.mkPen(color=[255, 255, 90], width=3)
- num_samples = round(CHART_HISTORY_TIME * 1e3 / DAQ_INTERVAL_MS)
- self.CH_1 = ChartHistory(num_samples, self.pi_chart.plot(pen=PEN_01))
+ self.history_chart_curve = HistoryChartCurve(
+ capacity=round(CHART_HISTORY_TIME * 1e3 / DAQ_INTERVAL_MS),
+ linked_curve=self.pi_chart.plot(
+ pen=pg.mkPen(color=[255, 255, 90], width=3)
+ ),
+ )
# 'Readings'
p = {"readOnly": True}
@@ -264,7 +266,7 @@ def process_qpbt_clear_chart(self):
)
if reply == QtWid.QMessageBox.Yes:
- self.CH_1.clear()
+ self.history_chart_curve.clear()
@QtCore.pyqtSlot()
def process_qpbt_record(self):
@@ -285,33 +287,23 @@ def process_qpbt_wave_square(self):
def process_qpbt_wave_sawtooth(self):
ard.write("sawtooth")
+ @QtCore.pyqtSlot()
+ def update_GUI(self):
+ str_cur_date, str_cur_time, _ = get_current_date_time()
+ self.qlbl_cur_date_time.setText(
+ "%s %s" % (str_cur_date, str_cur_time)
+ )
+ self.qlbl_update_counter.setText("%i" % state.update_counter_DAQ)
+ self.qlbl_DAQ_rate.setText("DAQ: %.1f Hz" % state.obtained_DAQ_rate_Hz)
+ self.qlin_reading_t.setText("%.3f" % state.time)
+ self.qlin_reading_1.setText("%.4f" % state.reading_1)
-# ------------------------------------------------------------------------------
-# update_GUI
-# ------------------------------------------------------------------------------
-
-
-@QtCore.pyqtSlot()
-def update_GUI():
- str_cur_date, str_cur_time, _ = get_current_date_time()
- window.qlbl_cur_date_time.setText("%s %s" % (str_cur_date, str_cur_time))
- window.qlbl_update_counter.setText("%i" % state.update_counter_DAQ)
- window.qlbl_DAQ_rate.setText("DAQ: %.1f Hz" % state.obtained_DAQ_rate_Hz)
- window.qlin_reading_t.setText("%.3f" % state.time)
- window.qlin_reading_1.setText("%.4f" % state.reading_1)
-
-
-# ------------------------------------------------------------------------------
-# update_chart
-# ------------------------------------------------------------------------------
-
-
-@QtCore.pyqtSlot()
-def update_chart():
- if DEBUG:
- tprint("update_curve")
+ @QtCore.pyqtSlot()
+ def update_chart(self):
+ if DEBUG:
+ tprint("update_curve")
- window.CH_1.update_curve()
+ self.history_chart_curve.update()
# ------------------------------------------------------------------------------
@@ -368,6 +360,7 @@ def DAQ_function():
"'%s' reports IOError @ %s %s"
% (ard.name, str_cur_date, str_cur_time)
)
+ sys.exit(0)
# Parse readings into separate state variables
try:
@@ -379,6 +372,7 @@ def DAQ_function():
"'%s' reports IOError @ %s %s"
% (ard.name, str_cur_date, str_cur_time)
)
+ sys.exit(0)
# Use Arduino time or PC time?
use_PC_time = True
@@ -386,7 +380,7 @@ def DAQ_function():
state.time = time.perf_counter()
# Add readings to chart histories
- window.CH_1.add_new_reading(state.time, state.reading_1)
+ window.history_chart_curve.append_data(state.time, state.reading_1)
# Logging to file
if file_logger.starting:
@@ -404,7 +398,7 @@ def DAQ_function():
file_logger.write("%.3f\t%.4f\n" % (log_elapsed_time, state.reading_1))
# We update the GUI right now because this is a singlethread demo
- update_GUI()
+ window.update_GUI()
# ------------------------------------------------------------------------------
@@ -464,7 +458,7 @@ def DAQ_function():
timer_chart = QtCore.QTimer()
# timer_chart.setTimerType(QtCore.Qt.PreciseTimer)
- timer_chart.timeout.connect(update_chart)
+ timer_chart.timeout.connect(window.update_chart)
timer_chart.start(CHART_INTERVAL_MS)
# --------------------------------------------------------------------------
diff --git a/demo_D_no_GUI.py b/demo_D_no_GUI.py
index 0f6d6d9..6889747 100644
--- a/demo_D_no_GUI.py
+++ b/demo_D_no_GUI.py
@@ -7,8 +7,8 @@
__author__ = "Dennis van Gils"
__authoremail__ = "vangils.dennis@gmail.com"
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
-__date__ = "24-07-2020"
-__version__ = "5.0"
+__date__ = "30-07-2020"
+__version__ = "6.0"
# pylint: disable=bare-except, broad-except
import os
@@ -172,7 +172,7 @@ def DAQ_function():
qdev_ard.create_worker_DAQ(
DAQ_function = DAQ_function,
DAQ_interval_ms = DAQ_INTERVAL_MS,
- critical_not_alive_count = 3,
+ critical_not_alive_count = 1,
debug = DEBUG,
)
# fmt: on
diff --git a/demo_E_no_GUI.py b/demo_E_no_GUI.py
index 4fc6089..ff54c91 100644
--- a/demo_E_no_GUI.py
+++ b/demo_E_no_GUI.py
@@ -8,8 +8,8 @@
__author__ = "Dennis van Gils"
__authoremail__ = "vangils.dennis@gmail.com"
__url__ = "https://github.com/Dennis-van-Gils/DvG_Arduino_PyQt_multithread_demo"
-__date__ = "24-07-2020"
-__version__ = "5.0"
+__date__ = "30-07-2020"
+__version__ = "6.0"
# pylint: disable=bare-except, broad-except
import os
@@ -192,7 +192,7 @@ def tick(self):
DAQ_trigger = DAQ_TRIGGER.INTERNAL_TIMER,
DAQ_function = sync.tick,
DAQ_interval_ms = DAQ_INTERVAL_MS,
- critical_not_alive_count = 3,
+ critical_not_alive_count = 1,
debug = DEBUG,
)
# fmt: on
diff --git a/list_conda.txt b/list_conda.txt
index a2bcd5e..812bb61 100644
--- a/list_conda.txt
+++ b/list_conda.txt
@@ -28,6 +28,7 @@ docutils 0.16 py38_1
dvg-debug-functions 2.1.1 pypi_0 pypi
dvg-devices 0.1.0 pypi_0 pypi
dvg-pid-controller 2.0.0 pypi_0 pypi
+dvg-pyqtgraph-threadsafe 1.0.0 pypi_0 pypi
dvg-qdeviceio 0.3.0 pypi_0 pypi
dvg-ringbuffer 1.0.1 pypi_0 pypi
fftw 3.3.8 nompi_h24e91a8_1111 conda-forge
diff --git a/list_pip.txt b/list_pip.txt
index 4dfbdfe..460bcdb 100644
--- a/list_pip.txt
+++ b/list_pip.txt
@@ -25,6 +25,7 @@ docutils 0.16
dvg-debug-functions 2.1.1
dvg-devices 0.1.0
dvg-pid-controller 2.0.0
+dvg-pyqtgraph-threadsafe 1.0.0
dvg-qdeviceio 0.3.0
dvg-ringbuffer 1.0.1
future 0.18.2
diff --git a/requirements.txt b/requirements.txt
index 49afee4..26e1b5f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,8 +2,9 @@ psutil~=5.6
pyserial~=3.4
numpy~=1.15
pyqt5~=5.12
-pyqtgraph~=0.10
+pyqtgraph~=0.11
dvg-debug-functions==2.1.1
dvg-qdeviceio==0.3.0
dvg-devices==0.1.0
+dvg-pyqtgraph-threadsafe==1.0.0