Reputation: 41
I need to interact with the following figure. I want to add two sliders to change the (x,y) position of the red point in the following figure. The dash line and circle should also be changed with the action of the red point, while the blue point should stay there.
I have found some demos of sliders under matplotlib, while all of them updated a curve of the figure. My figure is a bit more complex than them, I do not know how to update parts of it.
import numpy as np
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import rcParams
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
ax.axis([-5,15,-5,15])
ax.set_aspect(1) # x y轴等比例显示
anchorNum = 4
anchorX = [0,10,0,10]
anchorY = [0,0,10,10]
anchorName = ['A0', 'A1', 'A2', 'A3']
colorArray = ['blue', 'green', 'magenta', 'green']
ax.scatter(anchorX, anchorY, s=200) # plot stable blue point
for i, txt in enumerate(anchorName):
ax.annotate(txt, (anchorX[i], anchorY[i]), fontsize = 20)
initTagX = np.random.random_sample()*10
initTagY = np.random.random_sample()*10
tagPos = [initTagX, initTagY]
ax.scatter(tagPos[0], tagPos[1], c='red', s=300) # plot the red point
# plot the dash line
for i in range(anchorNum):
anchorPos = (anchorX[i], anchorY[i])
diffVector = [(tagPos[0] - anchorX[i]), (tagPos[1] - anchorY[i])]
dist = np.linalg.norm(diffVector,ord = 2) # 计算二阶范数 平方和开根号
circle = plt.Circle(anchorPos, dist, color = colorArray[i], fill = False, linestyle='--', linewidth=4)
ax.add_artist(circle)
ax.plot([anchorX[i], tagPos[0]], [anchorY[i], tagPos[1]], linestyle = '--', color = colorArray[i], linewidth=4)
plt.show()
Upvotes: 1
Views: 739
Reputation: 69164
The only difference between your figure and the examples you might have seen is that you need to update several things at once when the sliders are changed.
When the x or y position is changed, you need to update the x or y position of the red point, the radius of the 4 circles, and the position of the 4 dashed lines.
The code below does all that. When the sliders are moved, either update_rp_x
or update_rp_y
is called. That in turn called the function update_plot
with the updated coordinates, and then moved all the relevant plot features around. For the red point, I switched from using ax.scatter
to ax.plot
, because its a little simpler to update its position (e.g. rp.set_xdata()
). The lines and circles are stored in lists when first plotted, so when we update them, we can just loop overs those lists and update each one in turn. The circles just need to change their radius, which we can do with Circle.set_radius
, and the lines with set_xdata
and set_ydata
again.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
fig, ax = plt.subplots()
ax.axis([-5,15,-5,15])
ax.set_aspect(1) # x y
# Make room at the bottom for sliders
fig.subplots_adjust(bottom=0.3)
# Initial tag location
initTagX = np.random.random_sample()*10
initTagY = np.random.random_sample()*10
tagPos = [initTagX, initTagY]
# Set up the anchors
anchorNum = 4
anchorX = [0,10,0,10]
anchorY = [0,0,10,10]
anchorName = ['A0', 'A1', 'A2', 'A3']
colorArray = ['blue', 'green', 'magenta', 'green']
# Plot the anchors
ax.scatter(anchorX, anchorY, s=100) # plot stable blue point
# Label the anchors
for i, txt in enumerate(anchorName):
ax.annotate(txt, (anchorX[i], anchorY[i]), fontsize = 10)
# Plot initial location of red point
rp, = ax.plot(tagPos[0], tagPos[1], c='red', marker='o', ms=10) # plot the red point
# Store circles and lines for update later
circles = []
lines = []
# Plot initial circles
for i in range(anchorNum):
anchorPos = (anchorX[i], anchorY[i])
diffVector = [(tagPos[0] - anchorX[i]), (tagPos[1] - anchorY[i])]
dist = np.linalg.norm(diffVector,ord = 2)
circle = plt.Circle(anchorPos, dist, color = colorArray[i], fill = False, linestyle='--', linewidth=2)
circles.append(ax.add_artist(circle))
lines.append(ax.plot([anchorX[i], tagPos[0]], [anchorY[i], tagPos[1]], linestyle = '--',
color = colorArray[i], linewidth=2)[0])
def update_plot(xpos, ypos):
'''
This function updates the radius of the circles
and the position of the dashed lines
'''
# Update the tag position
tagPos = [xpos, ypos]
rp.set_xdata(xpos)
rp.set_ydata(ypos)
for i in range(anchorNum):
anchorPos = (anchorX[i], anchorY[i])
diffVector = [(tagPos[0] - anchorX[i]), (tagPos[1] - anchorY[i])]
dist = np.linalg.norm(diffVector,ord = 2)
# Now we actually update the circles and dashed lines
circles[i].set_radius(dist)
lines[i].set_xdata([anchorX[i], tagPos[0]])
lines[i].set_ydata([anchorY[i], tagPos[1]])
return tagPos
def update_rp_x(xpos):
''' This function updates the x position of the red point '''
global tagPos
tagPos = update_plot(xpos, tagPos[1])
fig.canvas.draw_idle()
def update_rp_y(ypos):
''' This function updates the y position of the red point '''
global tagPos
tagPos = update_plot(tagPos[0], ypos)
fig.canvas.draw_idle()
# Create Axes for the sliders
axcolor = '#909090'
sax_x = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)
sax_y = plt.axes([0.25, 0.15, 0.65, 0.03], facecolor=axcolor)
# Create sliders
sx = Slider(sax_x, 'x position', -5, 15, valinit=initTagX)
sy = Slider(sax_y, 'y position', -5, 15, valinit=initTagY)
# Tell sliders which function to call when changed
sx.on_changed(update_rp_x)
sy.on_changed(update_rp_y)
plt.show()
Upvotes: 2