Reputation:
Due to performance issues for dynamical updates, I need to draw directly a lot of rectangle on the canvas as very low level, that is to say without using matplotlib.patches
and as we have to do with classical GUI.
More precisely, I would like to only draw one rectangle and not only all the figure.
Is it possible ?
Here is my testing code using the link given by 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()
Under Mac O$, I obtain the following message.
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
I have a good news for the Mac users who want to play with the following codes, and also with animations. I've reinstalled Maverick OS on my Mac without any softwares, this is called a clean installation, and then I've installed Anaconda, and XQuark (see this).
To make animations work, you just have to use the following two lines before any other matplotlib imports.
import matplotlib
matplotlib.use('TkAgg')
I think this will work for any Mac OS supported by Anaconda. The use of XQuark is only needed for Maverick.
Upvotes: 4
Views: 20519
Reputation: 284750
You have two problems with your current code.
ax.draw_artist(rect)
.Here's a working example:
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()
At this point, though, it would make a lot more sense not to add a new artist each time. Instead, add the initial rectangle and update it each time. For example:
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()
Upvotes: 1