在Python GTK3中使用matplotlib发动攻击

时间:2018-08-31 18:57:42

标签: python matplotlib

我的目标是在matplotlib中进行实时绘图,并将这些绘图嵌入用pygtk(或pygobject,永远记不清哪一个)编写的GTK3 GUI中。有效执行此操作的一种方法是使用“ blitting”和this example通过保存背景区域,在每次更新后还原它并重画艺术家线条来演示如何进行操作。经过相当多的敲击操作,我发现default matplotlib examples文档介绍了如何在GTK中使用matplotlib进行图形绘制,但是使用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))

这是我正在处理的示例,其中我尝试使用保存的背景区域并通过点按以提高绘图速度和减少CPU负载来集成上述链接的示例。

最后,最终用途将不是从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()

这是示例示例: Example GIF

这是上面的代码示例为我生成的: GIF of above code example

图形线也无法正确呈现。

任何帮助将不胜感激!

编辑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

如果更新间隔快于130ms,我仍然有一个问题,即图形边框和刻度线不被重绘。如果timeout_add()使用的数字小于130,则图形刻度和边框将无法正确绘制。我认为这是因为重绘刻度线需要花费超过130ms的时间,因此可能需要另存为另一组“背景”。

编辑2:

另一个突破!在this answer的帮助下解决了更新间隔问题。下面的代码能够以2ms的间隔刷新,但是我注意到如果尝试调整窗口大小,它仍然存在一些问题。它还具有几乎没有CPU负载的额外好处!为了解决这个问题,我将刷新率保持在10ms。这样做的目的是无论如何都要为图形提供异步更新,而不是下面显示的测试的定期更新间隔。

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()

0 个答案:

没有答案