我想用 Matplotlib 创建一个可拖动的注释框。但是,我的
ax
是在 SubFigure
对象而不是 Figure
上定义的。
以下简短代码显示了在这种情况下如何无法拖动文本框(除非它位于绘图区域“外部”):
import matplotlib.pyplot as plt
fig = plt.figure()
subfig = fig.subfigures()
ax = subfig.add_subplot()
bbox_args = dict(boxstyle="round", facecolor="wheat")
an1 = ax.annotate("Text is outside, so draggable", xy=(0.5, 1.1), xycoords=ax.transAxes, bbox=bbox_args)
an2 = ax.annotate("Text is inside, so not draggable", xy=(0.3, 0.5), xycoords=ax.transAxes, bbox=bbox_args)
an1.draggable()
an2.draggable()
plt.show()
事实上,一旦你拖动外面的文本框并将其留在里面,它就会永远卡在那里!
如果不依赖
subfig
,两个盒子都是可以拖动的,但是如何使用subfig
呢?
我从头开始写的。代码分为 2 个文件:
这是在annotationbox.py中找到的类:
# Import modules
import matplotlib.pyplot as plt
# ------------------------------------------------- #
class Annotation_Box:
# Constructor function
def __init__(self, bottom_left_coords_box, dim_box, box_color, \
box_thickness, text, top_left_coords_text, text_color, text_size):
# Define global variables
self.text = text
self.dim_box = dim_box
self.box_color = box_color
self.text_size = text_size
self.text_color = text_color
self.box_thickness = box_thickness
self.top_left_coords_text = top_left_coords_text
self.bottom_left_coords_box = bottom_left_coords_box
def show(self, ax):
# Add the box
ax.add_patch(plt.Rectangle(xy=self.bottom_left_coords_box, width=self.dim_box[0], height=self.dim_box[1], \
edgecolor=self.box_color, facecolor="None", linewidth=self.box_thickness))
# Add the text
ax.text(x=self.top_left_coords_text[0], y=self.top_left_coords_text[1], s=self.text, fontsize=self.text_size)
def point_in_rect(self, point):
# Define the points of the vertices of rectangle
x1, y1 = self.bottom_left_coords_box
x2, y2 = self.bottom_left_coords_box[0] + self.dim_box[0], \
self.bottom_left_coords_box[1] + self.dim_box[1]
# Check if point is in the rectangle
if (point[0] >= x1 and point[0] <= x2):
if (point[1] >= y1 and point[1] <= y2):
return True
return False
您必须对坐标进行硬编码。
这是主要代码:
# Import modules
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
# ------------------------------------------ #
# Import classes
from annotationbox import Annotation_Box
# ------------------------------------------------------------------------------------------------------------------------------------------- #
# Define the figure
fig = plt.figure(figsize=(16,16))
# Define the axes
subfig = fig.subfigures()
ax = subfig.add_subplot()
# ------------------------------------------------------------------------------------------------------------------------------------------- #
box = Annotation_Box(bottom_left_coords_box=[0.1, 0.09], dim_box=[0.19, 0.05], box_color="Black", \
box_thickness=2.0, text="Box is draggable !", top_left_coords_text=[0.11, 0.1], text_color="Black", text_size=20.0)
# Display the box
# in first frame of animation
box.show(ax)
# ------------------------------------------------------------------------------------------------------------------------------------------- #
# Coordinates of mouse when clicked
mouse_pos_click = [None, None]
# Coordinates of mouse when released
mouse_pos_release = [None, None]
# Coordinates of mouse when hovered
mouse_pos_hover = [None, None]
# Function to get mouse coordinates
def onClick(event):
global mouse_pos_click
# Saves x and y coordinates of mouse
mouse_pos_click = [event.xdata, event.ydata]
def onRelease(event):
global mouse_pos_release
# Saves x and y coordinates of mouse
mouse_pos_release = [event.xdata, event.ydata]
def onHover(event):
global mouse_pos_hover
# Saves x and y coordinates of mouse
mouse_pos_hover = [event.xdata, event.ydata]
# Boolean that keeps track if the box has been clicked
click_box = False
# ------------------------------------------------------------------------------------------------------------------------------------------- #
def animate(i):
global box, mouse_pos_click, mouse_pos_hover, mouse_pos_release, click_box
if not click_box:
# If mouse is click --> run onClick event
plt.connect('button_press_event', onClick)
# Check if the mouse has clicked been clicked
if mouse_pos_click[0]:
# Check if the clicked pos is inside the box
if box.point_in_rect(mouse_pos_click):
# Update the boolean
click_box = True
else:
# Reset the click coords
mouse_pos_click = [None, None]
else:
# If mouse is released --> run onRelease event
plt.connect('button_release_event', onRelease)
# Check if the mouse has been released
if mouse_pos_release[0]:
# Reset the click boolean
click_box = False
# Reset the click coords
mouse_pos_click = [None, None]
# Reset the released coords
mouse_pos_release = [None, None]
# Check if mouse has not been released
else:
# Check position where mouse is hovering
plt.connect('motion_notify_event', onHover)
# Prevent error on line 96 and 97
if (mouse_pos_hover[0] == None):
mouse_pos_hover = mouse_pos_click
# Calculate the change if x and y
delta_x = mouse_pos_hover[0] - mouse_pos_click[0]
delta_y = mouse_pos_hover[1] - mouse_pos_click[1]
# Update the pos of box and text
box.bottom_left_coords_box[0] += delta_x
box.bottom_left_coords_box[1] += delta_y
box.top_left_coords_text[0] += delta_x
box.top_left_coords_text[1] += delta_y
# Update the click coordinates
mouse_pos_click = mouse_pos_hover
# Clear the axes
ax.clear()
# Display the box
box.show(ax)
animation = FuncAnimation(fig, animate, interval=60)
# Display the plot
plt.show()
希望这对您有帮助!