Блинтинг с матплотлибом в Python GTK3 - PullRequest
0 голосов
/ 31 августа 2018

Моя цель - создавать графики в режиме реального времени в matplotlib и встраивать эти графики в графический интерфейс GTK3, написанный с помощью pygtk (или pygobject, никогда не запомните, какой именно). Один из способов сделать это эффективно - использовать «blitting», и этот пример покажет, как это сделать, сохранив фоновую область, восстановив ее после каждого обновления и перерисовав линию исполнителя. После нескольких попыток smacking-head-on-keyboard я обнаружил, что стандартные примеры matplotlib , в которых описывается, как выполнять построение графиков с помощью matplotlib в GTK, используют рендерер GTK3Cairo, которого было недостаточно (так как этого не происходит). поддержка blitting) и что мне нужно было переключить на GTK3Agg. Это сработало успешно, и я могу подражать приведенному выше примеру, встраивая его в виджет GTK ScrolledWindow. Однако графики, кажется, рисуют друг над другом в нижней части каждого графика.

Я проследил это до пары вопросов. Похоже, что сохраненная фоновая область расположена неправильно и слегка смещена. Если я изменю вызов функции restore_region(background) (см. Ниже) и вместо этого попытаюсь изменить значения bbox и xy, я могу сместить область фона вокруг, но это не решит проблему нижней части графика, рисующего над собой (смещения ниже приведены только произвольные числа).

x1, y1, x2, y2 = background.get_extents()
restore_region(background, bbox=(x1, y1-20, x2, y2), xy=(x1+9, y1+10))

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

В конце концов, окончательное использование не будет обновляться с timeout_add(), я буду обновлять данные, полученные из отдельного потока обработки.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

import numpy as np

import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas

class TestPlot(Gtk.ScrolledWindow):
    def __init__(self):
        Gtk.ScrolledWindow.__init__(self)

        self.x = np.arange(0, 2*np.pi, 0.1)
        self.y = np.sin(self.x)

        self.fig, self.axes = plt.subplots(nrows=6)
        self.canvas = FigureCanvas(self.fig)
        self.add_with_viewport(self.canvas)

        self.fig.canvas.draw()

        styles = ['r-', 'g-', 'y-', 'm-', 'k-', 'c-']
        def plot(ax, style):
            return ax.plot(self.x, self.y, style, animated=True)[0]
        self.lines = [plot(ax, style) for ax, style in zip(self.axes, styles)]

        self.backgrounds = [self.fig.canvas.copy_from_bbox(ax.bbox) for ax in self.axes]

        self.i = 1
        GObject.timeout_add(10, self.update_graph)

    def update_graph(self):
        try:
            items = enumerate(zip(self.lines, self.axes, self.backgrounds), start=1)
            for j, (line, ax, background) in items:
                line.set_ydata(np.sin(j*self.x + self.i/10.))
                self.fig.canvas.restore_region(background)
                ax.draw_artist(line)
                self.fig.canvas.blit(ax.bbox)
            self.i += 1
        except:
            pass
        return True

class TestWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="TestWindow")
        self.set_default_size(700, 500)
        self.graph = TestPlot()
        self.add(self.graph)

win = TestWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

Вот как выглядит пример: Пример GIF

Это то, что приведенный выше пример кода производит для меня: GIF приведенного выше примера кода

Линии графика также отображаются неправильно.

Любая помощь будет принята с благодарностью!

Редактировать 1:

Я решил проблему перерисовки графа над собой, а вызов restore_background() не стирает все. Сначала я установил self.backgrounds = None и изменил update_graph(), чтобы сначала проверить, были ли сохранены фоны, и сохранить их, если они не были. Это дает основному потоку GTK время для правильного обновления и изменения размеров окон и графиков, а затем сохраняет фоны, как только это будет сделано.

def update_graph(self):
    if self.backgrounds is None:
        self.backgrounds = [self.fig.canvas.copy_from_bbox(ax.bbox) for ax in self.axes]

    try:
        items = enumerate(zip(self.lines, self.axes, self.backgrounds), start=1)
        for j, (line, ax, background) in items:
            line.set_ydata(np.sin(j*self.x + self.i/10.))
            self.fig.canvas.restore_region(background)
            ax.draw_artist(line)
            self.fig.canvas.blit(ax.bbox)
        self.i += 1
    except:
        pass
    return True

У меня все еще есть проблема, заключающаяся в том, что граница графика и галочки не перерисовываются, если интервал обновления превышает 130 мс. Если timeout_add() использует значение меньше 130, отметки и границы графика будут отображены неправильно. Я предполагаю, что это потому, что перерисовывание тиков занимает больше 130 мс, поэтому может потребоваться сохранить его как другой набор «фонов».

Редактировать 2:

Еще один прорыв! Решена проблема интервала обновления с помощью этого ответа . Приведенный ниже код может обновляться с интервалом в 2 мс, но я заметил, что он все еще имеет некоторые проблемы, если я пытаюсь изменить размер окна. Это также имеет дополнительное преимущество: практически нет загрузки процессора! Чтобы решить эту проблему, я сохранил частоту обновления на 10 мс. Целью этого является обеспечение асинхронного обновления графика в любом случае вместо регулярных интервалов обновления для теста, показанного ниже.

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject

import numpy as np

import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas

class TestPlot(Gtk.ScrolledWindow):
    def __init__(self):
        Gtk.ScrolledWindow.__init__(self)

        self.background = None
        self.x = np.arange(0, 2*np.pi, 0.1)
        self.y = np.sin(self.x)
        self.z = np.cos(self.x)

        self.fig = Figure()
        self.canvas = FigureCanvas(self.fig)
        self.ax = self.fig.add_subplot(111, axisbg="black")

        self.add_with_viewport(self.canvas)

        self.red_line,   = self.ax.plot(self.x, self.y, "r-", animated=True)
        self.green_line, = self.ax.plot(self.x, self.z, "lime", animated=True)

        self.i = 1
        self.canvas.mpl_connect("draw_event", self.on_draw)
        GObject.timeout_add(10, self.update_graph)

    def update_graph(self):
        try:
            self.red_line.set_ydata(np.sin(self.x + self.i/10.))
            self.green_line.set_ydata(np.cos(self.x + self.i/10.))
            self.fig.canvas.restore_region(self.background)
            self.ax.draw_artist(self.red_line)
            self.ax.draw_artist(self.green_line)
            self.fig.canvas.blit(self.ax.clipbox)
            self.i += 1
        except:
            pass
        return True

    def save_bg(self):
        self.background = self.fig.canvas.copy_from_bbox(self.ax.get_figure().bbox)

    def on_draw(self, *args):
        self.save_bg()
        return False

class TestWindow(Gtk.Window):
    def __init__(self):
        Gtk.Window.__init__(self, title="TestWindow")
        self.set_default_size(700, 500)
        self.graph = TestPlot()
        self.add(self.graph)

win = TestWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...