[Solution has been added to the EDIT sections in this post]
2 animated subplots are stacked vertically.
I would like to show a black vertical line through them according to the mouse position.
Up to now I can only completely mess the figure when moving the mouse...
How to clear the old vertical lines between updates?
(Just out of curiosity: since mouse movement control, my PC fan goes crazy when executing the code even without moving the mouse. Is mouse so "calculation expensive"?!?)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from time import sleep
val1 = np.zeros(100)
val2 = np.zeros(100)
level1 = 0.2
level2 = 0.5
fig, ax = plt.subplots()
ax1 = plt.subplot2grid((2,1),(0,0))
lineVal1, = ax1.plot(np.zeros(100))
ax1.set_ylim(-0.5, 1.5)
ax2 = plt.subplot2grid((2,1),(1,0))
lineVal2, = ax2.plot(np.zeros(100), color = "r")
ax2.set_ylim(-0.5, 1.5)
def onMouseMove(event):
ax1.axvline(x=event.xdata, color="k")
ax2.axvline(x=event.xdata, color="k")
def updateData():
global level1, val1
global level2, val2
clamp = lambda n, minn, maxn: max(min(maxn, n), minn)
level1 = clamp(level1 + (np.random.random()-.5)/20.0, 0.0, 1.0)
level2 = clamp(level2 + (np.random.random()-.5)/10.0, 0.0, 1.0)
# values are appended to the respective arrays which keep the last 100 readings
val1 = np.append(val1, level1)[-100:]
val2 = np.append(val2, level2)[-100:]
yield 1 # FuncAnimation expects an iterator
def visualize(i):
lineVal1.set_ydata(val1)
lineVal2.set_ydata(val2)
return lineVal1,lineVal2
fig.canvas.mpl_connect('motion_notify_event', onMouseMove)
ani = animation.FuncAnimation(fig, visualize, updateData, interval=50)
plt.show()
Edit1
As solved by Ophir:
def onMouseMove(event):
ax1.lines = [ax1.lines[0]]
ax2.lines = [ax2.lines[0]]
ax1.axvline(x=event.xdata, color="k")
ax2.axvline(x=event.xdata, color="k")
Edit2
In case there are more datasets in the same plot such as in:
ax1 = plt.subplot2grid((2,1),(0,0))
lineVal1, = ax1.plot(np.zeros(100))
lineVal2, = ax2.plot(np.zeros(100), color = "r")
ax1.set_ylim(-0.5, 1.5)
each dataset's line is stored in ax1.lines[]
:
ax1.lines[0]
is lineVal1
ax1.lines[1]
is lineVal2
ax1.lines[2]
is the vertical line if you already drew it.This means onMouseMove
has to be changed to:
def onMouseMove(event):
ax1.lines = ax1.lines[:2] # keep the first two lines
ax1.axvline(x=event.xdata, color="k") # then draw the vertical line
Plt.subplots(nrows, ncols) The two integer arguments to this function specify the number of rows and columns of the subplot grid. The function returns a figure object and a tuple containing axes objects equal to nrows*ncols.
Instead of adding new axvline
s to the plot you simply change the data of the existing one. You only need to store the return value of the axvline
call to keep the handle on it. The data format is ([x, x], [0, 1])
, which can be changed using set_data
.
(For axhlines the format is ([0, 1], [y, y])
by the way.)
Add the following global variables:
axvline1 = ax1.axvline(x=0., color="k")
axvline2 = ax2.axvline(x=0., color="k")
and change the conMouseMove handler to:
def onMouseMove(event):
axvline1.set_data([event.xdata, event.xdata], [0, 1])
axvline2.set_data([event.xdata, event.xdata], [0, 1])
A minor drawback is that you start with the vlines at x=0 from the start.
Full code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from time import sleep
val1 = np.zeros(100)
val2 = np.zeros(100)
level1 = 0.2
level2 = 0.5
fig, ax = plt.subplots()
ax1 = plt.subplot2grid((2,1),(0,0))
lineVal1, = ax1.plot(np.zeros(100))
ax1.set_ylim(-0.5, 1.5)
ax2 = plt.subplot2grid((2,1),(1,0))
lineVal2, = ax2.plot(np.zeros(100), color = "r")
ax2.set_ylim(-0.5, 1.5)
axvline1 = ax1.axvline(x=0., color="k")
axvline2 = ax2.axvline(x=0., color="k")
def onMouseMove(event):
axvline1.set_data([event.xdata, event.xdata], [0, 1])
axvline2.set_data([event.xdata, event.xdata], [0, 1])
def updateData():
global level1, val1
global level2, val2
clamp = lambda n, minn, maxn: max(min(maxn, n), minn)
level1 = clamp(level1 + (np.random.random()-.5)/20.0, 0.0, 1.0)
level2 = clamp(level2 + (np.random.random()-.5)/10.0, 0.0, 1.0)
# values are appended to the respective arrays which keep the last 100 readings
val1 = np.append(val1, level1)[-100:]
val2 = np.append(val2, level2)[-100:]
yield 1 # FuncAnimation expects an iterator
def visualize(i):
lineVal1.set_ydata(val1)
lineVal2.set_ydata(val2)
return lineVal1,lineVal2
fig.canvas.mpl_connect('motion_notify_event', onMouseMove)
ani = animation.FuncAnimation(fig, visualize, updateData, interval=50)
plt.show()
replace your onMouseMove
with the following one:
(I used How to remove lines in a Matplotlib plot)
def onMouseMove(event):
ax1.lines = [ax1.lines[0]]
ax2.lines = [ax2.lines[0]]
ax1.axvline(x=event.xdata, color="k")
ax2.axvline(x=event.xdata, color="k")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With