Python 如何使tkinter Main循环等待matplotlib单击事件

Python 如何使tkinter Main循环等待matplotlib单击事件,python,matplotlib,tkinter,click,wait,Python,Matplotlib,Tkinter,Click,Wait,我正在使用Tkinter和matplotlib构建带有嵌入式绘图的GUI。我已经在我的窗口中嵌入了一个图形,现在希望使用matplotlib的事件处理程序从图形中获取两组x,y坐标,然后使用这些坐标创建一条直线,该直线从图形中的数据中减去。代码的简化版本如下所示: import matplotlib matplotlib.use("TkAgg") from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, Navigation

我正在使用Tkinter和matplotlib构建带有嵌入式绘图的GUI。我已经在我的窗口中嵌入了一个图形,现在希望使用matplotlib的事件处理程序从图形中获取两组x,y坐标,然后使用这些坐标创建一条直线,该直线从图形中的数据中减去。代码的简化版本如下所示:

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import tkinter as tk

#ideally this uses matplotlib's event handler and also waits for a click before registering the cooridnates
def choose_points():
    points = []
    window.bind("<Button-1>", on_click)
    points.append(graph_xy)
    window.bind("<Button-1>", on_click)
    points.append(graph_xy)
    return points

def on_click(event):
    window.unbind("<Button-1")
    window.config(cursor="arrow")
    graph_xy[0]=event.x
    graph_xy[1]=event.y

def line(x1=0,y1=0,x2=1,y2=1000):
    m=(y2-y1)/(x2-x1)
    c=y2-m*x2
    line_data=[]
    for val in range(0,20):
        line_data.append(val*m + c)
    return line_data

def build_line():
    points = []
    points = choose_points()
    #store line in line_list
    line_list=line(points[0],points[1],points[2],points[3])

#lists needed
line_list=[]
graph_xy=[0,0]

#GUI
window=tk.Tk()
window.title("IPES Graphing Tool")
window.geometry('1150x840')

#Make a frame for the graph
plot_frame = tk.Frame(window)
plot_frame.pack(side = tk.TOP,padx=5,pady=5)

#Button for making the straight line
line_btn = ttk.Button(plot_frame,text="Build line", command = build_line)
line_btn.grid(row=4, column=2,sticky='w')

#make empty figure
fig1=plt.figure(figsize=(9,7))
ax= fig1.add_axes([0.1,0.1,0.65,0.75])

#embed matplotlib figure
canvas = FigureCanvasTkAgg(fig1, plot_frame)
mpl_canvas=canvas.get_tk_widget()
canvas.get_tk_widget().pack(padx=20,side=tk.BOTTOM, fill=tk.BOTH, expand=False)
toolbar = NavigationToolbar2Tk(canvas, plot_frame)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=False)

window.mainloop()
导入matplotlib
matplotlib.use(“TkAgg”)
从matplotlib.backends.backend_tkagg导入图CAVASTKAGG,导航工具栏2TK
从matplotlib.figure导入图形
将tkinter作为tk导入
#理想情况下,这将使用matplotlib的事件处理程序,并在注册Cooridate之前等待单击
def选择_点():
点数=[]
window.bind(“,单击时)
点。追加(图形_xy)
window.bind(“,单击时)
点。追加(图形_xy)
返回点
单击时的def(事件):

window.unbind(“您应该使用
canvas.mpl\u connect
来触发您的,然后检索
xdata
ydata
来绘制线。请参见下面的示例:

import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import tkinter as tk

window=tk.Tk()
window.title("IPES Graphing Tool")
window.geometry('1150x840')

plot_frame = tk.Frame(window)
plot_frame.pack(side = tk.TOP,padx=5,pady=5)

fig1=Figure(figsize=(9,7))
ax= fig1.add_axes([0.1,0.1,0.65,0.75])

canvas = FigureCanvasTkAgg(fig1, window)
canvas.get_tk_widget().pack(padx=20,side=tk.TOP, fill=tk.BOTH, expand=False)
toolbar = NavigationToolbar2Tk(canvas, window)
toolbar.update()

class DrawLine: # a simple class to store previous cords
    def __init__(self):
        self.x = None
        self.y = None

    def get_cords(self, event):
        if self.x and self.y:
            ax.plot([self.x, event.xdata], [self.y, event.ydata])
            canvas.draw_idle()
        self.x, self.y = event.xdata, event.ydata

draw = DrawLine()
canvas.mpl_connect('button_press_event', draw.get_cords)

window.mainloop()

因此,通过调整@Henry Yik的答案,我成功地获得了我想要的功能。通过使用
canvas.mpl\u disconnect(cid)
解决了这个问题

我基本上使用了一个按钮来使用一个名为
choose\u cords()
的函数,该函数将接收一个名为
DrawLine
的对象,该对象的类型为
DrawLine
,包含x和y坐标来构建线条。然后该函数将发出命令
canvas.mpl\u connect('button\u press\u event',draw.get\u cords)
开始侦听图形上的单击。注册两次单击后,matplotlib事件处理程序将从
draw
对象中断开连接。代码如下所示


import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import tkinter as tk

window=tk.Tk()
window.title("IPES Graphing Tool")
window.geometry('1150x840')

plot_frame = tk.Frame(window)
plot_frame.pack(side = tk.TOP,padx=5,pady=5)

fig1=Figure(figsize=(9,7))
ax= fig1.add_axes([0.1,0.1,0.65,0.75])

canvas = FigureCanvasTkAgg(fig1, window)
canvas.get_tk_widget().pack(padx=20,side=tk.TOP, fill=tk.BOTH, expand=False)
toolbar = NavigationToolbar2Tk(canvas, window)
toolbar.update()

def choose_cords(draw):
    draw.cid=canvas.mpl_connect('button_press_event', draw.get_cords)

class DrawLine: # a simple class to store previous cords
    def __init__(self):
        self.x = None
        self.y = None
        self.cid = None

    def get_cords(self, event):
        if self.x and self.y:
            ax.plot([self.x, event.xdata], [self.y, event.ydata])
            canvas.draw_idle()
            canvas.mpl_disconnect(self.cid)
            self.__init__()
            return
        self.x, self.y = event.xdata, event.ydata

draw = DrawLine()
draw_btn = tk.Button(window, text="Draw the Line", command=lambda:choose_cords(draw)).pack(side=tk.TOP,padx=5,pady=5)

window.mainloop()
我在绘制直线后添加了一个回车,因为我每次都想要一条新线,而不是一个连续线


再次感谢Henry给出的答案,这是一个

感谢Henry Yik给出的答案,但这不是我想要的。我举的例子将问题简化了。我的目标是让用户点击一个按钮,让程序进入一个从图表中取两个点的例程(由单击事件指定)然后用它们找到一条直线,然后从图表中的数据中减去。我有减法例程工作,但我不能让程序在进入这个减法例程之前等待用户的点击事件。希望这是有意义的。将编辑问题的想法是一样的-你等待两条线被拾取然后执行你喜欢的任何函数。你可以使用about类来存储初始值,并在第二次选择后执行你的函数。我很确定我明白你的意思,但我的问题有点不同。我不想一直执行这个。我希望这只会在我按下某个按钮后发生。我我一直在尝试使用你的想法,但我没有任何运气。我想我离这越来越近了。我创建了一个按钮,它发出一个函数
choose\u coords(draw)
,它获取DrawLine对象,然后调用
canvas.mpl\u connect('button\u press\u event',draw.get\u cords')
开始监听图形上的点击(函数存储连接的id)。我调整了
get\u cords
方法,从点击中获取坐标,使用它们,然后进行画布。mpl\u断开连接(cid)并重新初始化DrawLine对象。现在它可以工作了!