Matplotlib 我可以在嵌入式图形的顶部绘制TKinter对象吗?

Matplotlib 我可以在嵌入式图形的顶部绘制TKinter对象吗?,matplotlib,tkinter,tkinter-canvas,Matplotlib,Tkinter,Tkinter Canvas,简说: 我正在创建一个快速的TkinterAPI,首先生成一个tk.Canvas 我正在嵌入一个上面带有master=tk.canvas的FigureCanvasTkAgg画布 有了它,我可以通过Matplotlib显示图像 现在我想在FigureCastKagg画布上绘制TKinter对象(例如矩形或按钮) 这可能吗?或者是否有任何特别建议(即仅使用一种画布或另一种) 下面是一些快速代码: import tkinter as tk from matplotlib.backends.back

简说:

  • 我正在创建一个快速的TkinterAPI,首先生成一个tk.Canvas
  • 我正在嵌入一个上面带有master=tk.canvas的FigureCanvasTkAgg画布
  • 有了它,我可以通过Matplotlib显示图像
  • 现在我想在FigureCastKagg画布上绘制TKinter对象(例如矩形或按钮)
这可能吗?或者是否有任何特别建议(即仅使用一种画布或另一种)

下面是一些快速代码:

import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure

class MyApp(tk.Tk):
  def __init__(self):
    tk.Tk.__init__(self)
    self.canvas = tk.Canvas(self, width=500, height=500, cursor="cross")
    self.canvas.pack(side="top", fill="both", expand=True)

  def draw_image_and_button(self):
    self.figure_obj = Figure()
    a = self.figure_obj.add_axes([0, 0, 1, 1])
    imgplot = a.imshow(some_preloaded_data_array, cmap='gray')
    # create tkagg canvas
    self.canvas_agg = FigureCanvasTkAgg(self.figure_obj, master=self.canvas)
    self.canvas_agg.draw()
    self.canvas_agg.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
    # attempt to draw rectangle
    self.rectangle = self.canvas.create_rectangle(0, 0, 100, 100, fill='red')

if __name__ == "__main__":
    app = MyApp()
    app.draw_image()
    app.mainloop()
我的意思是,我看到矩形是在图像之前绘制的。也许是我不了解FigureCastKagg是如何连接到tk.canvas的


谢谢大家!

好的,这是我最近开发的一个应用程序,其中有matplotlib小部件和鼠标事件。您也可以使用tkinter小部件,但我没有找到将它们放在matplolib画布上的方法。就我个人而言,我更喜欢matplotlib小部件而不是tkinter小部件,所以我认为它还不错

您必须采取的唯一预先步骤是修改matplotlib源代码,因为您需要将画布传递给小部件类,而默认情况下,小部件采用图形画布,嵌入tk时将不起作用(按钮将无响应)。修改实际上很简单,但我们还是按顺序进行吧

  • 在matplotlib文件夹中打开“widgets.py”(具体取决于安装位置,在我的示例中,它位于“C:\Program Files\Python37\Lib\site packages\matplotlib”中)
  • 转到
    类AxesWidget(Widget)
    (第90行左右)并使用以下代码修改
    \uuuu init\uuuuu
    方法:
  • 如您所见,与原始代码相比,我添加了一个关键字参数
    canvas=None
    。通过这种方式,原始功能得到了维护,但现在您可以将画布传递给小部件

  • 要在嵌入tk的matplolib画布上有一个响应按钮,现在创建一个小部件,并传递使用
    FigureCanvasTkAgg创建的matplolib画布。例如,对于
    按钮
    ,您可以编写
  • 好了,现在我们已经具备了在tk中嵌入matplolib画布上的matplolib小部件所需的所有功能,此外,您还可以拥有鼠标和按键事件,我想这涵盖了您从GUI所期望的95%。请注意,如果不想修改原始源代码,当然可以创建自己的类来复制
    AxesWidget
    class

    您可以在这里找到所有可用的matplolib小部件

    这是您的应用程序的修改版本,我们将所有内容放在一起:

    将tkinter作为tk导入 从matplotlib.backends.backend_tkagg导入图CAVASTKAGG,导航工具栏2TK 从matplotlib.figure导入图形 从matplotlib.widgets导入按钮 将numpy作为np导入

    class MyApp(tk.Tk):
        def __init__(self):
            tk.Tk.__init__(self)
            self.canvas = tk.Canvas(self, width=500, height=500, cursor="cross")
            self.canvas.pack(side="top", fill="both", expand=True)
    
        def draw_image_and_button(self):
            self.figure_obj = Figure()
            self.ax = self.figure_obj.add_subplot()
            self.figure_obj.subplots_adjust(bottom=0.25)
            some_preloaded_data_array = np.zeros((600,600))
            imgplot = self.ax.imshow(some_preloaded_data_array, cmap='gray')
            # create tkagg canvas
            self.canvas_agg = FigureCanvasTkAgg(self.figure_obj, master=self.canvas)
            self.canvas_agg.draw()
            self.canvas_agg.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
            # add matplolib toolbar
            toolbar = NavigationToolbar2Tk(self.canvas_agg, self.canvas)
            toolbar.update()
            self.canvas_agg._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
            # add matplolib widgets
            self.ax_ok_B = self.figure_obj.add_subplot(position=[0.2, 0.2, 0.1, 0.03]) # axes position doesn't really matter here because we have the resize event that adjusts widget position
            self.ok_B = Button(self.ax_ok_B, 'Ok', canvas=self.canvas_agg)
            # add tkinter widgets (outside of the matplolib canvas)
            button = tk.Button(master=self, text="Quit", command=self._quit)
            button.pack(side=tk.BOTTOM)
            # Connect to Events
            self.ok_B.on_clicked(self.ok)
            self.canvas_agg.mpl_connect('button_press_event', self.press)
            self.canvas_agg.mpl_connect('button_release_event', self.release)
            self.canvas_agg.mpl_connect('resize_event', self.resize)
            self.canvas_agg.mpl_connect("key_press_event", self.on_key_press)
            self.protocol("WM_DELETE_WINDOW", self.abort_exec)
    
        def abort_exec(self):
            print('Closing with \'x\' is disabled. Please use quit button')
    
        def _quit(self):
            print('Bye bye')
            self.quit()
            self.destroy()
    
        def ok(self, event):
            print('Bye bye')
            self.quit()
            self.destroy()
    
        def press(self, event):
            button = event.button
            print('You pressed button {}'.format(button))
            if event.inaxes == self.ax and event.button == 3:
                self.xp = int(event.xdata)
                self.yp = int(event.ydata)
                self.cid = (self.canvas_agg).mpl_connect('motion_notify_event',
                                                                self.draw_line)
                self.pltLine = Line2D([self.xp, self.xp], [self.yp, self.yp])
    
        def draw_line(self, event):
            if event.inaxes == self.ax and event.button == 3:
                self.yd = int(event.ydata)
                self.xd = int(event.xdata)
                self.pltLine.set_visible(False)
                self.pltLine = Line2D([self.xp, self.xd], [self.yp, self.yd], color='r')
                self.ax.add_line(self.pltLine)
                (self.canvas_agg).draw_idle()
    
        def release(self, event):
            button = event.button
            (self.canvas_agg).mpl_disconnect(self.cid)
            print('You released button {}'.format(button))
    
        def on_key_press(self, event):
            print("you pressed {}".format(event.key))
    
        # Resize event is needed if you want your widget to move together with the plot when you resize the window
        def resize(self, event):
            ax_ok_left, ax_ok_bottom, ax_ok_right, ax_ok_top = self.ax.get_position().get_points().flatten()
            B_h = 0.08 # button width
            B_w = 0.2 # button height
            B_sp = 0.08 # space between plot and button
            self.ax_ok_B.set_position([ax_ok_right-B_w, ax_ok_bottom-B_h-B_sp, B_w, B_h])
            print('Window was resized')
    
    
    if __name__ == "__main__":
        app = MyApp()
        app.draw_image_and_button()
        app.mainloop()
    
    好的,让我们看看这个应用程序的功能:

    • 按键盘上的一个键→ 打印按下的键
    • 按鼠标键→ 打印按下的按钮(1=左,2=轮,3=右)
    • 释放鼠标按钮→ 打印已发布的按钮
    • 在绘图的任意点上按右键,并在按住鼠标键的同时画一条线
    • 按“确定”或“退出”关闭应用程序
    • 按“x”关闭窗口被禁用
    • 调整窗口大小→ 绘图并相应缩放
    我还随意添加了经典的matplotlib工具栏,用于缩放等其他功能

    请注意,图像打印是通过添加调整大小功能的
    add\u suplot()
    方法添加的。这样,当您调整窗口大小时,绘图将相应缩放

    我实现的大部分内容都可以在matplotlib关于如何嵌入tk()的官方教程中找到


    如果这回答了你的问题,请告诉我。我想和大家分享,因为几天前我开发了一些非常类似的东西

    您需要Tkinter对象还是可以使用matplotlib小部件?或者,我想制作一个用于可视化图像的应用程序,我预计需要按钮、文本字段、可能的缩放功能、鼠标和键盘事件以及使用自己的事件功能定义对象的可能性。您尝试过在绘图上绘制一些东西吗?它看起来像是
    self.canvas\u agg.get\u tk\u widget()
    可能会返回一个画布。没错,它确实返回了一个画布对象,我可以调用该方法从中绘制矩形,但它不会出现!谢谢,我现在用FigureCanvasTkAgg和matplotlib小部件制定了大部分代码,我已经可以解决我的许多必要问题了。
    from matplotlib.widgets import Button
    
    ok_button = Button(ax_ok_button, 'Ok', canvas=canvas)  # canvas created with FigureCanvasTkAgg
    
    class MyApp(tk.Tk):
        def __init__(self):
            tk.Tk.__init__(self)
            self.canvas = tk.Canvas(self, width=500, height=500, cursor="cross")
            self.canvas.pack(side="top", fill="both", expand=True)
    
        def draw_image_and_button(self):
            self.figure_obj = Figure()
            self.ax = self.figure_obj.add_subplot()
            self.figure_obj.subplots_adjust(bottom=0.25)
            some_preloaded_data_array = np.zeros((600,600))
            imgplot = self.ax.imshow(some_preloaded_data_array, cmap='gray')
            # create tkagg canvas
            self.canvas_agg = FigureCanvasTkAgg(self.figure_obj, master=self.canvas)
            self.canvas_agg.draw()
            self.canvas_agg.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
            # add matplolib toolbar
            toolbar = NavigationToolbar2Tk(self.canvas_agg, self.canvas)
            toolbar.update()
            self.canvas_agg._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
            # add matplolib widgets
            self.ax_ok_B = self.figure_obj.add_subplot(position=[0.2, 0.2, 0.1, 0.03]) # axes position doesn't really matter here because we have the resize event that adjusts widget position
            self.ok_B = Button(self.ax_ok_B, 'Ok', canvas=self.canvas_agg)
            # add tkinter widgets (outside of the matplolib canvas)
            button = tk.Button(master=self, text="Quit", command=self._quit)
            button.pack(side=tk.BOTTOM)
            # Connect to Events
            self.ok_B.on_clicked(self.ok)
            self.canvas_agg.mpl_connect('button_press_event', self.press)
            self.canvas_agg.mpl_connect('button_release_event', self.release)
            self.canvas_agg.mpl_connect('resize_event', self.resize)
            self.canvas_agg.mpl_connect("key_press_event", self.on_key_press)
            self.protocol("WM_DELETE_WINDOW", self.abort_exec)
    
        def abort_exec(self):
            print('Closing with \'x\' is disabled. Please use quit button')
    
        def _quit(self):
            print('Bye bye')
            self.quit()
            self.destroy()
    
        def ok(self, event):
            print('Bye bye')
            self.quit()
            self.destroy()
    
        def press(self, event):
            button = event.button
            print('You pressed button {}'.format(button))
            if event.inaxes == self.ax and event.button == 3:
                self.xp = int(event.xdata)
                self.yp = int(event.ydata)
                self.cid = (self.canvas_agg).mpl_connect('motion_notify_event',
                                                                self.draw_line)
                self.pltLine = Line2D([self.xp, self.xp], [self.yp, self.yp])
    
        def draw_line(self, event):
            if event.inaxes == self.ax and event.button == 3:
                self.yd = int(event.ydata)
                self.xd = int(event.xdata)
                self.pltLine.set_visible(False)
                self.pltLine = Line2D([self.xp, self.xd], [self.yp, self.yd], color='r')
                self.ax.add_line(self.pltLine)
                (self.canvas_agg).draw_idle()
    
        def release(self, event):
            button = event.button
            (self.canvas_agg).mpl_disconnect(self.cid)
            print('You released button {}'.format(button))
    
        def on_key_press(self, event):
            print("you pressed {}".format(event.key))
    
        # Resize event is needed if you want your widget to move together with the plot when you resize the window
        def resize(self, event):
            ax_ok_left, ax_ok_bottom, ax_ok_right, ax_ok_top = self.ax.get_position().get_points().flatten()
            B_h = 0.08 # button width
            B_w = 0.2 # button height
            B_sp = 0.08 # space between plot and button
            self.ax_ok_B.set_position([ax_ok_right-B_w, ax_ok_bottom-B_h-B_sp, B_w, B_h])
            print('Window was resized')
    
    
    if __name__ == "__main__":
        app = MyApp()
        app.draw_image_and_button()
        app.mainloop()