Каков наилучший вид графического виджета в режиме реального времени для wxPython?

Я хотел бы показать график реального времени с одной или двумя кривыми до 50 выборок в секунду с использованием Python и wxPython. Виджет должен поддерживать как платформы Win32, так и Linux.

Любые подсказки приветствуются.

Отредактировано для добавления:

Мне не нужно обновлять дисплей со скоростью 50 кадров в секунду, но нужно отображать до 50 выборок данных на обеих кривых с разумной скоростью обновления для дисплея (5,10 кадров в секунду должно быть в порядке).

Отредактировано для добавления:

Я использовал mathplotlib в проекте с хорошим успехом. Затем я решил использовать wx.lib.plot для других проектов, которые, по моему мнению, были более простыми, но несколько проще в использовании и потребляли меньше циклов процессора. Поскольку wx.lib поставляется как часть стандартного дистрибутива wxPython, он особенно прост в использовании.

Ответы

Ответ 1

Если вы хотите высокую производительность с минимальным размером кода, не смотрите дальше, чем встроенная библиотека построения tython на основе Python. Не нужно писать специальный код C/С++ или использовать большой пакет построения, чтобы получить производительность намного лучше, чем 50 кадров в секунду.

Screenshot

Следующий код прокручивает ленточную диаграмму 1000x200 с частотой 400 кадров в секунду на Core 2 Duo 2.2 ГГц, 1000 кадров в секунду на 3,4 ГГц Core i3. Центральная рутина "scrollstrip" отображает набор точек данных и соответствующих цветов на правом краю вместе с дополнительной вертикальной сеткой, затем прокручивает стриптизер влево на 1. Для построения горизонтальных сетчатых полос просто включают их в данные и цвет массивы как константы вместе с вашими переменными точками данных.

from tkinter import *
import math, random, threading, time

class StripChart:

    def __init__(self, root):
        self.gf = self.makeGraph(root)
        self.cf = self.makeControls(root)
        self.gf.pack()
        self.cf.pack()
        self.Reset()

    def makeGraph(self, frame):
        self.sw = 1000
        self.h = 200
        self.top = 2
        gf = Canvas(frame, width=self.sw, height=self.h+10,
                    bg="#002", bd=0, highlightthickness=0)
        gf.p = PhotoImage(width=2*self.sw, height=self.h)
        self.item = gf.create_image(0, self.top, image=gf.p, anchor=NW)
        return(gf)

    def makeControls(self, frame):
        cf = Frame(frame, borderwidth=1, relief="raised")
        Button(cf, text="Run", command=self.Run).grid(column=2, row=2)
        Button(cf, text="Stop", command=self.Stop).grid(column=4, row=2)
        Button(cf, text="Reset", command=self.Reset).grid(column=6, row=2)
        self.fps = Label(cf, text="0 fps")
        self.fps.grid(column=2, row=4, columnspan=5)
        return(cf)

    def Run(self):
        self.go = 1
        for t in threading.enumerate():
            if t.name == "_gen_":
                print("already running")
                return
        threading.Thread(target=self.do_start, name="_gen_").start()

    def Stop(self):
        self.go = 0
        for t in threading.enumerate():
            if t.name == "_gen_":
                t.join()

    def Reset(self):
        self.Stop()
        self.clearstrip(self.gf.p, '#345')

    def do_start(self):
        t = 0
        y2 = 0
        tx = time.time()
        while self.go:
            y1 = 0.2*math.sin(0.02*math.pi*t)
            y2 = 0.9*y2 + 0.1*(random.random()-0.5)
            self.scrollstrip(self.gf.p,
               (0.25+y1,   0.25, 0.7+y2,   0.6,     0.7,   0.8),
               ( '#ff4', '#f40', '#4af', '#080', '#0f0', '#080'),
                 "" if t % 65 else "#088")

            t += 1
            if not t % 100:
                tx2 = time.time()
                self.fps.config(text='%d fps' % int(100/(tx2 - tx)))
                tx = tx2
#            time.sleep(0.001)

    def clearstrip(self, p, color):  # Fill strip with background color
        self.bg = color              # save background color for scroll
        self.data = None             # clear previous data
        self.x = 0
        p.tk.call(p, 'put', color, '-to', 0, 0, p['width'], p['height'])

    def scrollstrip(self, p, data, colors, bar=""):   # Scroll the strip, add new data
        self.x = (self.x + 1) % self.sw               # x = double buffer position
        bg = bar if bar else self.bg
        p.tk.call(p, 'put', bg, '-to', self.x, 0,
                  self.x+1, self.h)
        p.tk.call(p, 'put', bg, '-to', self.x+self.sw, 0,
                  self.x+self.sw+1, self.h)
        self.gf.coords(self.item, -1-self.x, self.top)  # scroll to just-written column
        if not self.data:
            self.data = data
        for d in range(len(data)):
            y0 = int((self.h-1) * (1.0-self.data[d]))   # plot all the data points
            y1 = int((self.h-1) * (1.0-data[d]))
            ya, yb = sorted((y0, y1))
            for y in range(ya, yb+1):                   # connect the dots
                p.put(colors[d], (self.x,y))
                p.put(colors[d], (self.x+self.sw,y))
        self.data = data            # save for next call

def main():
    root = Tk()
    root.title("StripChart")
    app = StripChart(root)
    root.mainloop()

main()

Ответ 2

Не сложно создать виджет С++, который будет читать из вашего источника данных, и действительно обновлять его с частотой 50 FPS. Замечательная вещь в этом подходе заключается в том, что очень маленький (если есть) код Python будет выполняться с частотой 50FPS, все это будет на С++, в зависимости от того, как вы передадите свои обновленные данные в виджет.

Вы даже можете нажать обработчик событий в пользовательский просмотрщик данных в режиме реального времени со стороны Python, обработать все события мыши и взаимодействие с пользователем и оставить только рендеринг в С++.

Это будет небольшой класс С++, который расширяет класс wxWidget wxWidow

класс RealtimeDataViewer: public wxWindow {      ...

и переопределить OnPaint

void OnPaint (wxPaintEvent & WXUNUSED (событие)) {      ....

Затем он получит контекст устройства и начнет рисовать линии и фигуры...

Затем вам нужно взять файл .h и скопировать его на .i и немного изменить его, чтобы сделать его определением, которое SWIG может использовать для расширения wxPython.

Процесс сборки может обрабатываться собственными distutils Python, используя следующий параметр для настройки:

  ext_modules=[Extension('myextension', sources, 
                          include_dirs=includeDirs
                          library_dirs=usual_libs,
                          )],

Было бы несколько дней работать над тем, чтобы он выглядел великолепно и хорошо работал... Но это, вероятно, один из вариантов, который действительно ускорит ваш проект в будущем.

И все это хорошо работает на Mac, Windows и Linux.

wxPython - действительно скрытый Gem, который действительно возьмет на себя мир с более профессионально поддерживаемыми инструментами IDE/Designer.

Тем не менее, сначала попробуйте matplotlib, у него много красивого оптимизированного рендеринга, и он также может делать обновления в режиме реального времени.

Ответ 3

Если вы хотите действительно что-то быстро с 50 кадрами в секунду, я думаю, вам нужно что-то вроде PyGame и своего рода разговор непосредственно с дисплеем, а не с графическим модулем.

Проверьте связанные потоки:

Ответ 4

Я использую PyQtGraph для такого рода вещей. Он намного быстрее, чем Matplotlib для построения в реальном времени, и имеет множество приятных удобных функций, таких как контекстное меню на холсте с автоматическим масштабированием и прокруткой без дополнительной работы.

Ответ 5

Возможно Chaco? Я не знаю, может ли он делать 50 кадров в секунду, но я видел в демонстрации, как он сделал очень плавное отображение в реальном времени. Это определенно будет быстрее, чем matplotlib.