matplotlib - 直接在画布上绘图

4 投票
1 回答
20253 浏览
提问于 2025-04-17 22:06

由于动态更新时性能问题,我需要在画布上直接绘制很多矩形,也就是说不使用 matplotlib.patches,就像我们在传统的图形用户界面中那样。

更具体来说,我只想绘制一个矩形,而不是整个图形。

这可能吗?

这是我使用 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 O$ 上,我得到了以下消息。

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 上重新安装了 Maverick 操作系统,没有安装任何软件,这叫做干净安装,然后我安装了 Anaconda 和 XQuark(查看这里)。

为了让动画正常工作,你只需要在导入其他 matplotlib 库之前使用以下两行代码。

import matplotlib
matplotlib.use('TkAgg')

我认为这对任何支持 Anaconda 的 Mac 操作系统都有效。使用 XQuark 仅在 Maverick 系统上需要。

1 个回答

1

你的代码现在有两个问题。

  1. 你在没有先画出画布的情况下就想要进行绘制。你遇到的错误是和后端有关的,但简单来说,就是画布的渲染器还没有初始化好。
  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()

撰写回答