Я знаю, что приведенный ниже код кажется длинным и слишком сложным для вашей простой задачи, но ручная оптимизация обычно требует больше кода и больше потенциальных ошибок. Поэтому преждевременная оптимизация почти всегда является ошибкой.
В __init__
он устанавливает график, устанавливает ссылки для оси, холста, линии (она начинается с линии, нарисованной за пределами экрана) и фона перед анимацией. Дополнительно __init__
регистрирует обратные вызовы для обработки изменения размера и выключения. Обратный вызов on_resize
необходим для обновления фона (используется для блита) при изменении размера окна. Обратный вызов on_close
использует блокировку для обновления рабочего состояния. Я не устранил все условия гонки с этим, но это работает, чтобы предотвратить _tkinter.TclError
, вызванный попыткой перейти к завершенному приложению Tk. Я тестировал только с Tk, и только на одной машине. YMMV, и я открыт для предложений.
В методе run
я добавил вызов canvas.flush_events()
. Это должно удерживать окно графика от зависания, если вы попытаетесь перетащить окно вокруг и изменить его размер. Цикл while в этом методе вызывает self.get_new_values()
для установки данных на графике. Затем он обновляет график, используя настроенный метод. Если self.blit
имеет значение true, он использует canvas.blit
, иначе он использует pyplot.draw
.
Переменная spf
(выборок на кадр) контролирует, сколько выборок отображается в кадре. Вы можете использовать его в своей реализации get_new_values
для определения количества считываемых байтов (например, 2 * self.spf
для 2 байтов на выборку). Я установил значение по умолчанию 120, что составляет 5 кадров в секунду, если ваша скорость передачи данных составляет 600 выборок в секунду. Вы должны найти точку, которая максимизирует пропускную способность по сравнению с временным разрешением на графике, а также не отставать от входящих данных.
Чтение ваших данных в массив NumPy вместо использования списка Python, скорее всего, ускорит обработку. Кроме того, это даст вам легкий доступ к инструментам для уменьшения и анализа сигнала. Вы можете читать массив NumPy непосредственно из байтовой строки, но убедитесь, что вы правильно указали порядок байтов:
>>> data = b'\x01\xff' #big endian 256 + 255 = 511
>>> np.little_endian #my machine is little endian
True
>>> y = np.fromstring(data, dtype=np.uint16); y #so this is wrong
array([65281], dtype=uint16)
>>> if np.little_endian: y = y.byteswap()
>>> y #fixed
array([511], dtype=uint16)
Код:
from __future__ import division
from matplotlib import pyplot
import threading
class Main(object):
def __init__(self, samples_per_frame=120, blit=True):
self.blit = blit
self.spf = samples_per_frame
pyplot.ion()
self.ax = pyplot.subplot(111)
self.line, = self.ax.plot(range(self.spf), [-1] * self.spf)
self.ax.axis([0, self.spf, 0, 65536])
pyplot.draw()
self.canvas = self.ax.figure.canvas
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.canvas.mpl_connect('resize_event', self.on_resize)
self.canvas.mpl_connect('close_event', self.on_close)
self.lock = threading.Lock()
self.run()
def get_new_values(self):
import time
import random
#simulate receiving data at 9600 bps (no cntrl/crc)
time.sleep(2 * self.spf * 8 / 9600)
y = [random.randrange(65536) for i in range(self.spf)]
self.line.set_ydata(y)
def on_resize(self, event):
self.line.set_ydata([-1] * self.spf)
pyplot.draw()
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
def on_close(self, event):
with self.lock:
self.running = False
def run(self):
with self.lock:
self.running = True
while self.running:
self.canvas.flush_events()
with self.lock:
self.get_new_values()
if self.running:
if self.blit:
#blit for a higher frame rate
self.canvas.restore_region(self.background)
self.ax.draw_artist(self.line)
self.canvas.blit(self.ax.bbox)
else:
#versus a regular draw
pyplot.draw()
if __name__ == '__main__':
Main(samples_per_frame=120, blit=True)