在子图内时可拖动注释框

问题描述 投票:0回答:1

我想用 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
呢?

python matplotlib draggable figure plot-annotations
1个回答
0
投票

我从头开始写的。代码分为 2 个文件:

  1. main.py - 主要代码在哪里
  2. annotationbox.py - 找到注释框类的位置。

这是在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()

希望这对您有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.