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
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.lines import Line2D
|
import numpy as np
|
||||||
|
|
||||||
class DynamicSeries:
|
class DynamicSeries:
|
||||||
""" AutomaticTimeSeries handles drawing a line in an efficient way
|
def __init__(self, fig, ax, x_lim = [0,10], y_lim = [0,10]):
|
||||||
onto a matplotlib canvas. """
|
|
||||||
def __init__(self, fig, ax):
|
|
||||||
self.fig = fig
|
self.fig = fig
|
||||||
self.ax = ax
|
self.ax = ax
|
||||||
|
|
||||||
# Line associated with the figure used to draw the next line.
|
self.fig.show()
|
||||||
self.dummyLine = ax.plot([0],[0], animated=True)[0]
|
# 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.x_lim = x_lim
|
||||||
self._bg = None
|
self.y_lim = y_lim
|
||||||
|
self.ax.set_xlim(*self.x_lim)
|
||||||
|
self.ax.set_ylim(*self.y_lim)
|
||||||
|
|
||||||
# Redraw time series on reveal.
|
# Redraw the figure when matplotlib requests a redraw.
|
||||||
fig.canvas.mpl_connect("draw_event", self.on_draw)
|
self.fig.canvas.mpl_connect("draw_event", self.on_draw)
|
||||||
|
|
||||||
|
# Initialize data fed into the series
|
||||||
self.x_ax = []
|
self.x_ax = []
|
||||||
self.y_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):
|
def draw_event(self, *args):
|
||||||
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):
|
|
||||||
self.on_invalidated()
|
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, ax = plt.subplots()
|
||||||
fig.show()
|
d = DynamicSeries(fig, ax)
|
||||||
ats = AutomaticTimeSeries(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