matplotlib - 直接在画布上绘制

4

由于动态更新的性能问题,我需要以非常低的级别直接在画布上绘制许多矩形,也就是说,不使用matplotlib.patches,而且我们必须处理经典GUI。

更准确地说,我只想绘制一个矩形,而不是整个图形。

这是我使用Joe Kington提供的链接进行测试的代码。

#!/usr/bin/env python3

from random import randint, choice
import time

import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.colors as colors
import matplotlib

back_color = "black"
colors     = ['red', 'green', 'cyan', 'yellow']

width  = 16
height = 16

ax     = plt.subplot(111)
canvas = ax.figure.canvas

ax.set_xlim([0, width])
ax.set_ylim([0, height])

def update():
    global ax, canvas, colors, width, height

    x = randint(0, width - 1)
    y = randint(0, height - 1)

    rect = mpatches.Rectangle(
        (x, y), 1, 1,
        facecolor = choice(colors),
        edgecolor = back_color
    )

    start = time.time()
    ax.draw_artist(rect)
    canvas.blit(ax.bbox)
    print("draw >>>", time.time() - start)

timer = canvas.new_timer(interval = 1)
timer.add_callback(update)
timer.start()

plt.show()

在 Mac OS 下,我获得了以下消息。
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/site-packages/matplotlib/backend_bases.py", line 1203, in _on_timer
    ret = func(*args, **kwargs)
  File "/Users/xxx/test.py", line 86, in update
    ax.draw_artist(rect)
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/site-packages/matplotlib/axes.py", line 2100, in draw_artist
    a.draw(self._cachedRenderer)
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/site-packages/matplotlib/artist.py", line 56, in draw_wrapper
    draw(artist, renderer, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/site-packages/matplotlib/patches.py", line 393, in draw
    gc = renderer.new_gc()
  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/site-packages/matplotlib/backends/backend_macosx.py", line 97, in new_gc
    self.gc.save()
RuntimeError: CGContextRef is NULL

针对Maverick用户

我有一个好消息要告诉那些想使用以下代码和动画的Mac用户。我在我的Mac上进行了干净安装,也就是没有任何软件,然后安装了Anaconda和XQuark(请参见此处)。

为了使动画起作用,您只需在任何其他matplotlib导入之前使用以下两行。

import matplotlib
matplotlib.use('TkAgg')

我认为这适用于Anaconda支持的任何Mac OS。只有在Maverick上需要使用XQuark。


2
听起来你只想使用blitting。我现在没有时间给出完整的答案,但是请看这里“Animating selected plot elements”部分:http://wiki.scipy.org/Cookbook/Matplotlib/Animations#head-3d51654b8306b1585664e7fe060a60fc76e5aa08 - Joe Kington
不,它也适用于补丁。你能展示一下你正在尝试的例子吗? - Joe Kington
我已经放置了一个带有错误信息的小型测试代码。我正在使用Mac O$操作系统。 - user1054158
1个回答

1

您当前的代码存在两个问题。

  1. 在绘制画布之前,您尝试进行blit操作。您收到的错误是特定于后端的,但基本上它表示画布的渲染器尚未初始化。
  2. 您没有将矩形图形添加到轴中。因此,在调用ax.draw_artist(rect)时,它不会被绘制。

这里是一个可行的示例:

from random import randint, choice
import time
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

back_color = "black"
colors     = ['red', 'green', 'cyan', 'yellow']
width, height = 16, 16

fig, ax = plt.subplots()
ax.set(xlim=[0, width], ylim=[0, height]) # Or use "ax.axis([x0,x1,y0,y1])"

# Be sure to draw the canvas once before we start blitting. Otherwise
# a) the renderer doesn't exist yet, and b) there's noting to blit onto
fig.canvas.draw()

def update():
    x = randint(0, width - 1)
    y = randint(0, height - 1)

    rect = mpatches.Rectangle(
        (x, y), 1, 1,
        facecolor = choice(colors),
        edgecolor = back_color
    )
    ax.add_artist(rect)

    start = time.time()
    ax.draw_artist(rect)
    fig.canvas.blit(ax.bbox)
    print("draw >>>", time.time() - start)

timer = fig.canvas.new_timer(interval=1)
timer.add_callback(update)
timer.start()

plt.show()

然而,现在最好的做法是不要每次添加新的艺术家。相反,添加初始矩形并每次更新它。例如:

from random import randint, choice
import time
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

back_color = "black"
colors     = ['red', 'green', 'cyan', 'yellow']

width, height = 16, 16

fig, ax = plt.subplots()
ax.set(xlim=[0, width], ylim=[0, height]) # Or use "ax.axis([x0,x1,y0,y1])"

rect = mpatches.Rectangle(
    (0, 0), 1, 1,
    facecolor = choice(colors),
    edgecolor = back_color
)
ax.add_artist(rect)

# Be sure to draw the canvas once before we start blitting. Otherwise
# a) the renderer doesn't exist yet, and b) there's noting to blit onto
fig.canvas.draw()

def update():
    x = randint(0, width - 1)
    y = randint(0, height - 1)
    rect.set(xy=[x,y], facecolor=choice(colors))

    start = time.time()
    ax.draw_artist(rect)
    fig.canvas.blit(ax.bbox)
    print("draw >>>", time.time() - start)

timer = fig.canvas.new_timer(interval=1)
timer.add_callback(update)
timer.start()

plt.show()

非常感谢您的建议,但这在我的Mac OS上仍然无法工作。您是否在Linux或Windows下测试过它? - user1054158
我刚在Windows上测试成功了。所以对于Mac OS,存在一个大的bug。我将把它发布到“matplotlib”邮件列表中。 - user1054158
是的,OSX后端可能有些古怪。如果可以的话,最好使用TkAggQtAggGtkAgg等替代方案。(即在import matplotlib.pyplot as plt之前执行import matplotlib; matplotlib.use('TkAgg'))如果您能够发布具体错误信息,可能会有简单的解决方法。 - Joe Kington
真是一团糟!在我理想的Python世界里并不是这样的...我还在努力克服这个问题。 - user1054158
我已经找到了如何让你的代码在我的Mac上运行。我已经更新了我的问题。 - user1054158

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接