dynamic series properly updates on redraw
This commit is contained in:
parent
21c2421c5e
commit
515c83d412
125
dynamicseries.py
125
dynamicseries.py
|
@ -1,58 +1,99 @@
|
|||
import matplotlib
|
||||
from matplotlib.lines import Line2D
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
class DynamicSeries:
|
||||
""" AutomaticTimeSeries handles drawing a line in an efficient way
|
||||
onto a matplotlib canvas. """
|
||||
def __init__(self, fig, ax):
|
||||
def __init__(self, fig, ax, x_lim = [0,10], y_lim = [0,10]):
|
||||
self.fig = fig
|
||||
self.ax = ax
|
||||
|
||||
# Line associated with the figure used to draw the next line.
|
||||
self.dummyLine = ax.plot([0],[0], animated=True)[0]
|
||||
self.fig.show()
|
||||
# Create the one line that will be drawn onto the plot.
|
||||
# ``plot`` returns a list of the things plotted, so get
|
||||
# the only element of the list, the line.
|
||||
self.ln = ax.plot([0,1], [0,1], 'r-', animated=True)[0]
|
||||
|
||||
# Bitmap of previous background
|
||||
self._bg = None
|
||||
self.x_lim = x_lim
|
||||
self.y_lim = y_lim
|
||||
self.ax.set_xlim(*self.x_lim)
|
||||
self.ax.set_ylim(*self.y_lim)
|
||||
|
||||
# Redraw time series on reveal.
|
||||
fig.canvas.mpl_connect("draw_event", self.on_draw)
|
||||
# Redraw the figure when matplotlib requests a redraw.
|
||||
self.fig.canvas.mpl_connect("draw_event", self.on_draw)
|
||||
|
||||
# Initialize data fed into the series
|
||||
self.x_ax = []
|
||||
self.y_ax = []
|
||||
|
||||
self.current_x_lims = 10
|
||||
def set_color(self, *args, **kwargs):
|
||||
self.ln.set_color(*args, **kwargs)
|
||||
|
||||
def insert(self, x, y):
|
||||
self.x_ax.append([x])
|
||||
self.y_ax.append([y])
|
||||
|
||||
if len(self.x_ax) == 1:
|
||||
return None
|
||||
|
||||
prev_x = self.x_ax[-1]
|
||||
prev_y = self.y_ax[-1]
|
||||
self.dummyLine.set_data([(prev_x, prev_y), (x, y)])
|
||||
|
||||
if self._bg is not None:
|
||||
self.fig.canvas.restore_region(self._bg)
|
||||
|
||||
self.fig.draw_artist(self.dummyLine)
|
||||
self.fig.canvas.blit(self.fig.bbox)
|
||||
self.fig.canvas.flush_events()
|
||||
|
||||
self._bg = self.fig.canvas.copy_from_bbox(self.fig.bbox)
|
||||
|
||||
def on_invalidated(self):
|
||||
""" Redraw the entire canvas on invalidation. """
|
||||
self.ax.clear()
|
||||
self.ax.set_xlim(0, self.current_x_lims)
|
||||
self.ax.plot(self.x_ax, self.y_ax, color="blue")
|
||||
|
||||
def on_draw(self, event):
|
||||
def draw_event(self, *args):
|
||||
self.on_invalidated()
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
def on_invalidate(self):
|
||||
""" This function redraws all data to the screen whenever the
|
||||
screen is "invalidated". This could mean a user resize,
|
||||
or an automatic axis resize. """
|
||||
|
||||
self.ax.clear()
|
||||
self.ax.set_xlim(*self.x_lim)
|
||||
self.ax.set_ylim(*self.y_lim)
|
||||
#self.fig.canvas.draw()
|
||||
|
||||
self.ln.set_xdata(self.x_ax)
|
||||
self.ln.set_ydata(self.y_ax)
|
||||
self.ax.draw_artist(self.ln)
|
||||
self.fig.canvas.blit(self.fig.bbox)
|
||||
def on_draw(self, *args, **kwargs):
|
||||
self.on_invalidate()
|
||||
|
||||
def add(self, x, y):
|
||||
""" Add two points to the graph and redraw it.
|
||||
Returns True if the graph was redrawn. """
|
||||
haveToRedraw = False
|
||||
|
||||
# If there were no previous points, do nothing. There is not
|
||||
# enough data to draw a line.
|
||||
if len(self.x_ax) == 0:
|
||||
self.x_ax = [x]
|
||||
self.y_ax = [y]
|
||||
return False
|
||||
|
||||
# If there was a previous point, use it to draw a line to the
|
||||
# current point. Store the current point as data.
|
||||
prev_x = self.x_ax[-1]
|
||||
prev_y = self.y_ax[-1]
|
||||
self.x_ax.append(x)
|
||||
self.y_ax.append(y)
|
||||
|
||||
# If the point is outside of the limits of the graph, change the
|
||||
# limits and redraw the graph.
|
||||
if x < self.x_lim[0]:
|
||||
self.x_lim[0] = x - 0.01*x
|
||||
haveToRedraw = True
|
||||
elif x > self.x_lim[1]:
|
||||
self.x_lim[1] = x + 0.01*x
|
||||
haveToRedraw = True
|
||||
|
||||
if y < self.y_lim[0]:
|
||||
self.y_lim[0] = y - 0.01*y
|
||||
haveToRedraw = True
|
||||
elif y > self.y_lim[1]:
|
||||
self.y_lim[1] = y + 0.01*y
|
||||
haveToRedraw = True
|
||||
|
||||
if haveToRedraw:
|
||||
self.fig.canvas.draw()
|
||||
return True
|
||||
|
||||
print((prev_x, x), (prev_y, y))
|
||||
self.ln.set_xdata([prev_x, x])
|
||||
self.ln.set_ydata([prev_y, y])
|
||||
self.ax.draw_artist(self.ln)
|
||||
self.fig.canvas.blit(self.fig.bbox)
|
||||
self.fig.canvas.flush_events()
|
||||
return False
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
fig.show()
|
||||
ats = AutomaticTimeSeries(fig, ax)
|
||||
d = DynamicSeries(fig, ax)
|
||||
|
||||
|
|
81
test.py
81
test.py
|
@ -1,81 +0,0 @@
|
|||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
x = np.linspace(0, 2 * np.pi, 100)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.set_xlim(0, 100)
|
||||
ax.set_ylim(0, 10000)
|
||||
|
||||
# animated=True tells matplotlib to only draw the artist when we
|
||||
# explicitly request it
|
||||
(ln,) = ax.plot([0,1], [0,1], 'r-', animated=True)
|
||||
|
||||
# make sure the window is raised, but the script keeps going
|
||||
plt.show(block=False)
|
||||
|
||||
# stop to admire our empty window axes and ensure it is rendered at
|
||||
# least once.
|
||||
#
|
||||
# We need to fully draw the figure at its final size on the screen
|
||||
# before we continue on so that :
|
||||
# a) we have the correctly sized and drawn background to grab
|
||||
# b) we have a cached renderer so that ``ax.draw_artist`` works
|
||||
# so we spin the event loop to let the backend process any pending operations
|
||||
plt.pause(0.1)
|
||||
ax.draw_artist(ln)
|
||||
|
||||
# get copy of entire figure (everything inside fig.bbox) sans animated artist
|
||||
bg = fig.canvas.copy_from_bbox(fig.bbox)
|
||||
# draw the animated artist, this uses a cached renderer
|
||||
ax.draw_artist(ln)
|
||||
# show the result to the screen, this pushes the updated RGBA buffer from the
|
||||
# renderer to the GUI framework so you can see it
|
||||
fig.canvas.blit(fig.bbox)
|
||||
|
||||
x_ax = []
|
||||
y_ax = []
|
||||
|
||||
for j in range(100):
|
||||
# reset the background back in the canvas state, screen unchanged
|
||||
#fig.canvas.restore_region(bg)
|
||||
# update the artist, neither the canvas state nor the screen have changed
|
||||
x_ax.append(j)
|
||||
y_ax.append(j*j)
|
||||
|
||||
ln.set_data([j,j+1], [j*j,(j+1)*(j+1)])
|
||||
# re-render the artist, updating the canvas state, but not the screen
|
||||
ax.draw_artist(ln)
|
||||
# copy the image to the GUI state, but screen might not be changed yet
|
||||
fig.canvas.blit(fig.bbox)
|
||||
# flush any pending GUI events, re-painting the screen if needed
|
||||
fig.canvas.flush_events()
|
||||
# you can put a pause in if you want to slow things down
|
||||
plt.pause(.1)
|
||||
|
||||
ax.clear()
|
||||
ax.set_xlim(0, 200)
|
||||
ax.set_ylim(0, 200*200)
|
||||
(ln,) = ax.plot(x_ax, y_ax, 'r-', animated = True)
|
||||
plt.pause(0.1)
|
||||
ax.draw_artist(ln)
|
||||
bg = fig.canvas.copy_from_bbox(fig.bbox)
|
||||
fig.canvas.blit(fig.bbox)
|
||||
|
||||
for j in range(100, 200):
|
||||
# reset the background back in the canvas state, screen unchanged
|
||||
#fig.canvas.restore_region(bg)
|
||||
# update the artist, neither the canvas state nor the screen have changed
|
||||
x_ax.append(j)
|
||||
y_ax.append(j*j)
|
||||
|
||||
ln.set_data([j,j+1], [j*j,(j+1)*(j+1)])
|
||||
# re-render the artist, updating the canvas state, but not the screen
|
||||
ax.draw_artist(ln)
|
||||
# copy the image to the GUI state, but screen might not be changed yet
|
||||
fig.canvas.blit(fig.bbox)
|
||||
# flush any pending GUI events, re-painting the screen if needed
|
||||
fig.canvas.flush_events()
|
||||
# you can put a pause in if you want to slow things down
|
||||
plt.pause(.1)
|
Loading…
Reference in New Issue