Этот вопрос фактически основывается на моем вопросе, который я разместил на прошлой неделе. ( Как выполнить частичное заполнение_бетона в matplotlib, как разными цветами для разных значений )
Я пытаюсь записать количество данных в секунду на CANBus и отобразить это на графике. Я заранее извиняюсь за большое количество кода, но три метода, которые я попробовал, несколько похожи.
Это статическая ситуация, которая работает:
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib import pyplot as plt
import sys
from collections import deque
import logging
import time
logging.basicConfig(level=logging.INFO)
buffer_size = 120
lvl = buffer_size * [100]
llvl = buffer_size * [95]
t = [t for t in range(buffer_size)]
bitthrough = deque(buffer_size*[0], buffer_size)
y = [107, 108, 105, 109, 107, 106, 107, 109, 106, 106, 94, 93, 94, 93, 93, 94, 95, 106, 108, 109, 107, 107, 106, 108, 105, 108, 107, 106, 107, 97, 93, 96, 94, 96, 95, 94, 104, 107, 106, 108, 107, 107, 106, 107, 105, 107, 108, 105, 107, 100, 93, 94, 93, 95, 104, 107, 107, 108, 108, 107, 107, 107, 107, 104, 94, 96, 95, 96, 94, 95, 94, 100, 107, 107, 105, 107, 107, 109, 107, 108, 107, 105, 108, 108, 106, 97, 94, 94, 94, 94, 95, 94, 94, 94, 96, 108, 108, 107, 106, 107, 107, 108, 107, 106, 95, 95, 95, 94, 94, 96, 105, 108, 107, 106, 106, 108, 107, 108, 106, 107]
bitthrough = y
fig, ax = plt.subplots()
ax.set_ylim(0, 120)
ax.set_xlim(0, 120)
ln, = plt.plot([], color='black')
plt.ion()
plt.show()
while True:
plt.pause(1)
ln.set_xdata(range(buffer_size))
ln.set_ydata(bitthrough)
wh_green = [a <= b for a,b in zip(bitthrough, llvl)]
wh_orange = [a > b and a <= c for a, b, c in zip(bitthrough, llvl, lvl)]
wh_red = [a > b for a, b, in zip(bitthrough, lvl)]
ax.fill_between(t, 0, bitthrough, where=wh_red, color='red', interpolate=True)
ax.fill_between(t, 0, bitthrough, where=wh_orange, color='orange', interpolate=True)
ax.fill_between(t, 0, bitthrough, where=wh_green, color='green', interpolate=True)
fig.canvas.draw_idle()
Это приводит кследующий график: (что хорошо для меня, сейчас)
Но когда я хочу обновить график в реальном времени, используя потоки, вещи ломаются. Это код, который у меня есть. canbus
- это модуль, который я сделал для контроля CANBus. PCAN.throughput()
- функция сопрограммы asyncio, которая подсчитывает количество сообщений в секунду. После того, как второй пройден, он возвращается с количеством килобайт, полученных им в автобусе. Терминал показывает содержимое bitthrough
, которое заполняется в отдельном потоке.
from canbus import PCAN
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib import pyplot as plt
import sys
import asyncio
from collections import deque
import logging
import time
import threading
from matplotlib.figure import Figure
logging.basicConfig(level=logging.INFO)
buffer_size = 120
lvl = buffer_size * [100]
llvl = buffer_size * [95]
t = [t for t in range(buffer_size)]
loop = asyncio.get_event_loop()
cn = PCAN()
loop.run_until_complete(cn.poll_ids())
loop.run_until_complete(cn.get_names())
print(cn.names)
bitthrough = deque(buffer_size*[0], buffer_size)
def get_bt(loop):
asyncio.set_event_loop(loop)
while True:
task = loop.run_until_complete(cn.throughput())
bitthrough.append(task[0] / 33. * 100.)
print(bitthrough)
thread = threading.Thread(target=get_bt, args=(loop,))
thread.daemon = True
thread.start()
fig, ax = plt.subplots()
ax.set_ylim(0, 120)
ax.set_xlim(0, 120)
ln, = plt.plot([], color='black')
plt.ion()
plt.show()
while True:
plt.pause(1)
ln.set_xdata(range(buffer_size))
ln.set_ydata(bitthrough)
wh_green = [a <= b for a,b in zip(bitthrough, llvl)]
wh_orange = [a > b and a <= c for a, b, c in zip(bitthrough, llvl, lvl)]
wh_red = [a > b for a, b, in zip(bitthrough, lvl)]
ax.fill_between(t, 0, bitthrough, where=wh_red, color='red', interpolate=True)
ax.fill_between(t, 0, bitthrough, where=wh_orange, color='orange', interpolate=True)
ax.fill_between(t, 0, bitthrough, where=wh_green, color='green', interpolate=True)
fig.canvas.draw_idle()
Этот код приводит к следующему:
Обратите внимание, что красный цвет остается на самом высоком уровне, который был раньше.
Я также пытался реализовать его с помощью Qt backend matplotlib. Тем не менее, я только что получил пустой график, который не может быть обновлен. (Без потоков этот метод работал, но он зависал на моем компьютере, оставляя меня неспособным сделать что-то еще)
from canbus import PCAN
# import numpy as np
import matplotlib
matplotlib.use('Qt5Agg')
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import sys
import asyncio
from collections import deque
import logging
import time
import threading
import datetime
from matplotlib.backends.qt_compat import QtCore, QtWidgets
from matplotlib.backends.backend_qt5agg import (
FigureCanvas, NavigationToolbar2QT as NavigationToolbar)
from matplotlib.figure import Figure
logging.basicConfig(level=logging.INFO)
buffer_size = 120
lvl = buffer_size * [100]
llvl = buffer_size * [95]
t = [t for t in range(buffer_size)]
loop = asyncio.get_event_loop()
cn = PCAN()
loop.run_until_complete(cn.poll_ids())
loop.run_until_complete(cn.get_names())
print(cn.names)
bitthrough = deque(buffer_size*[0], buffer_size)
def get_bt(loop):
asyncio.set_event_loop(loop)
while True:
task = loop.run_until_complete(cn.throughput())
bitthrough.append(task[0] / 33. * 100.)
print(bitthrough)
thread = threading.Thread(target=get_bt, args=(loop,))
thread.daemon = True
thread.start()
class ApplicationWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
layout = QtWidgets.QVBoxLayout(self._main)
dynamic_canvas = FigureCanvas(Figure(figsize=(5, 3)))
layout.addWidget(dynamic_canvas)
self._dynamic_ax = dynamic_canvas.figure.subplots()
self._update_canvas()
def _update_canvas(self):
# p = loop.run_until_complete(cn.throughput())[0] / 500. * 100.
# bitthrough.append(p)
wh_green = [a <= b for a,b in zip(bitthrough, llvl)]
wh_orange = [a > b and a <= c for a, b, c in zip(bitthrough, llvl, lvl)]
wh_red = [a > b for a, b, in zip(bitthrough, lvl)]
self._dynamic_ax.clear()
self._dynamic_ax.fill_between(t, 0, bitthrough, where=wh_red, color='red', interpolate=True)
self._dynamic_ax.fill_between(t, 0, bitthrough, where=wh_orange,color='orange', interpolate=True)
self._dynamic_ax.fill_between(t, 0, bitthrough, where=wh_green, color='green', interpolate=True)
self._dynamic_ax.plot(t, bitthrough, color="black")
self._dynamic_ax.set_ylim(0, 120)
logging.info("redrawing graph")
self._dynamic_ax.figure.canvas.draw()
if __name__ == "__main__":
qapp = QtWidgets.QApplication(sys.argv)
app = ApplicationWindow()
app.show()
qapp.exec_()
while True:
app._update_canvas()
time.sleep(1)
, который дал мне это:
Тоже нехорошо, и сейчас я немного растерялся.