多个人物的点击事件

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

我想通过单击图中的值来提取图形的值,同时还在此时添加标记/删除旧标记。我设法对单个数字执行此操作,但在我的实践案例中,我有多个数字。

当我有多个数字时,单击事件仅适用于最新的数字。单击其他图形将打印出最后一个图形的值/在最后一个图形中添加标记。

import numpy as np
import matplotlib.pyplot as plt

%matplotlib qt


# Function to get the closest actual value
def find_nearest(array,value):
    idx = (np.abs(array-value)).argmin()
    return array[idx]

# Function to ectract the value from the graph by clicking
def onclick(event,x,y,fig):
    if event.inaxes is not None:
        ix = find_nearest(x,event.xdata )
        iy = y[np.where(x==ix)[0][0]]

        text = (f'x = {ix},   y = {iy}\n')

        ax1 = fig.get_axes()[0]
        # Update the plots
        if len(ax1.get_lines()) > 1: 
            delete = ax1.get_lines()[-1]
            delete.remove()
        ax1.plot([ix],[iy],marker="x",color="b")
        fig.canvas.draw_idle()
        print(text)

# test case
x = np.arange(0,10,1)
y1 = x
y2 = x**2
y3 = x**3

f1 = [x,y1]
f2 = [x,y2]
f3 = [x,y3]
functions = [f1,f2,f3]

for f in functions:
    fig, ax1 = plt.subplots(1)
    ax1.plot(f[0],f[1])
    #plt.show()
    cid = fig.canvas.mpl_connect('button_press_event', lambda event: onclick(event,f[0],f[1],fig))

如果有人知道如何修复它,我们将不胜感激。

我已经尝试将Fig、ax1和cid放入字典中并调整函数参数,但这并没有改变任何东西。

有趣的是,如果我在没有循环的情况下绘制和调用函数,但单独进行 3 次,至少可以检索函数的坐标部分。只有添加/删除标记不起作用...

我发现了一个类似的问题here,但是我不太明白答案。

python matplotlib event-handling
2个回答
0
投票

我尝试为多个图编写代码,但我认为这是不可能的。所以,我想出了一个想法 - 当你运行代码时,所有的图都会显示出来:

然后您可以单击其中之一。例如,如果您单击第二个图,它将像这样打开,

您可以单击该图,它将显示标记并在终端中输出坐标。如果您想访问其他图,只需关闭此窗口即可。将出现 3 个图。然后您可以选择例如第三个。然后,如果你想终止代码,只需关闭原始窗口(包含所有 3 个子图的窗口)

这是我的代码:

# Import modules
import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib.animation import FuncAnimation
# ------------------------------------------------------------------------------------------------------------------------------------------- #

# Test data
x = np.arange(0.0, 10.0, 1.0)
y1 = x
y2 = x**2
y3 = x**3
# 
x_data = [x, x, x]
y_data = [y1, y2, y3]
#
colors = ["red", "blue", "green"]  # color to identify which data is viewed
# ------------------------------------------------------------------------------------------------------------------------------------------- #

while True:

     # Define the figure and axes of each subplot
     # Documentation https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html
     fig, axs = plt.subplots(1, len(x_data), figsize=(16,16))

     # Axis added when viewing one subplot
     ax_added = None

     # Plot the data in each plot
     for i in range(len(x_data)):
          axs[i].plot(x_data[i], y_data[i], color=colors[i])
     # ------------------------------------------------------------------------------------------------------------------------------------------- #

     # Variable that stores which subplot is selected
     ax_selected_index = None
     ax_selected_index_copy = None

     # Boolean that keeps track if only one plot is displayed
     selected = False

     # Function to retrieve which subplot has been clicked
     def onClick1(event):
          global axs, ax_selected_index, ax_selected_index_copy

          # Iterate through each plot
          for i in range(len(axs)):

               # Check which plot has been clicked
               # Answer from https://stackoverflow.com/questions/25047846/determine-button-clicked-subplot
               if (event.inaxes == axs[i]):
                    
                    ax_selected_index = i
                    ax_selected_index_copy = i

                    return
     # ----- #

     # Define the coordinates of the mouse when clicked
     mouse_pos_click = [None, None]

     # Function to get mouse coordinates
     def onClick2(event):
          global mouse_pos_click

          # Saves x and y coordinates of mouse
          mouse_pos_click = [event.xdata, event.ydata]
     # ----- #

     def on_close(event):
          global selected

          # Close on first window
          if not selected:
               exit()
     # ----- #

     def animate(i):
          global axs, ax_selected_index, selected, mouse_pos_click, ax_selected_index_copy, ax_added

          # Exit if the user close the first window
          fig.canvas.mpl_connect('close_event', on_close)

          # Retrieve the clicked mouse coordinates
          # If mouse is click --> run onClick1 event
          plt.connect('button_press_event', onClick1)

          # Check whether user has clicked a subplot
          if ax_selected_index != None:
               
               for ax in axs:
                    # Hide X and Y axes label marks
                    ax.xaxis.set_tick_params(labelbottom=False)
                    ax.yaxis.set_tick_params(labelleft=False)

                    # Hide X and Y axes tick marks
                    ax.set_xticks([])
                    ax.set_yticks([])

               # Add another plot "on top" of axs
               ax_added = fig.add_subplot()
               # Plot the required graph
               ax_added.plot(x_data[ax_selected_index], y_data[ax_selected_index], color=colors[ax_selected_index]) 

               # Reset the selected index 
               ax_selected_index = None

               # Update the boolean
               selected = True

          if selected:

               # Retrieve the coordinates of the mouse
               plt.connect('button_press_event', onClick2)

               # Check if user has clicked
               if mouse_pos_click[0] != None:

                    # Find the closest actual value
                    indx = (np.abs(x_data[ax_selected_index_copy]-mouse_pos_click[0])).argmin()

                    # Clear the plot
                    ax_added.clear()
                    # Plot the line
                    ax_added.plot(x_data[ax_selected_index_copy], y_data[ax_selected_index_copy], color=colors[ax_selected_index_copy])
                    # Scatter the point
                    ax_added.scatter(x_data[ax_selected_index_copy][indx], y_data[ax_selected_index_copy][indx], color="black", s=50)

                    # Reset the mouse position
                    mouse_pos_click = [None, None]
                    
                    # Output the coordinates
                    print(f"Figure : {ax_selected_index_copy}")
                    print(f"x = {x_data[ax_selected_index_copy][indx]}, y = {y_data[ax_selected_index_copy][indx]}")
                    print("\n")
          

     animation = FuncAnimation(fig, animate, interval=50)

     # Display the plot
     plt.show()
     # ------------------------------------------------------------------------------------------------------------------------------------------- #

希望这对您有帮助!


0
投票

我能够弄清楚。不知何故,问题出在循环中......我不知道到底为什么,但是当我将数字放入字典中然后单独执行单击事件时,它起作用了。但是,当我尝试通过循环来完成它时,它就不起作用了。如果有人知道这种奇怪行为的原因,我会很高兴得到解释。

但是,我最终解决这个问题的方法是添加一个执行点击事件的函数。通过这样做,我可以再次使用循环。

def access_click_event(figure,x,y):
    figure.canvas.mpl_connect('button_press_event', lambda event: onclick(event,x,y,figure))


figures = {}
for n,f in enumerate(functions):
    fig, ax1 = plt.subplots(1)
    figures[n] = fig
    ax1.plot(f[0],f[1])
    access_click_event(figures[n],f[0],f[1])
© www.soinside.com 2019 - 2024. All rights reserved.