You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌入tkinter的Matplotlib通过RectangleSelector触发回调时FuncAnimation重复创建

问题根因
  • Figure创建方式错误:使用plt.subplots()生成的图表会同时被matplotlib的pyplot全局管理器托管,嵌入Tkinter后形成两条独立的事件响应链路,RectangleSelector的触发事件会被两条链路各处理一次,导致回调执行两次;而Tk原生按钮的事件仅走Tk事件循环,不会触发pyplot全局链路,因此运行正常。
  • 动画曲线创建逻辑不当:使用plt.plot()创建动画曲线时会隐式触发全局图表的重绘逻辑,进一步加剧了重复执行的问题。
修复方案
  1. 替换plt.subplots()为matplotlib原生无全局托管的Figure创建方式,切断pyplot全局事件链路
  2. 将动画曲线的创建从plt.plot()改为绑定到指定axes的self.ax.plot(),避免触发全局重绘
修复后完整代码
import os
import threading
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (
    FigureCanvasTkAgg)
from matplotlib.widgets import RectangleSelector
import numpy as np
import matplotlib
from matplotlib import animation

class LineAnimation:
    def __init__(self,  fig, ax):
        print(' enter LineAnimation ctor')

        # Parameters
        self.ax = ax
        self.fig = fig
        self.xdata, self.ydata = [], []
        # 替换plt.plot为绑定当前axes的plot
        self.ln, = self.ax.plot([], [], 'ro')

        # Print figures list
        figures = [manager.canvas.figure
                   for manager in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
        print('figures BEFORE animation: ', figures)

        self.animation = animation.FuncAnimation(fig=self.fig,
                                                 func=self.update,
                                                 init_func=self.init,
                                                 frames=np.linspace(0, 2 * np.pi, 128),
                                                 interval=25,
                                                 blit=True, repeat=False,
                                                 cache_frame_data=False)

        self.fig.canvas.draw()
        # Print figures list
        figures = [manager.canvas.figure
                   for manager in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
        print('figures AFTER animation: ', figures, '\n')

    def init(self):
        # Prints for debugging
        print('\nenter init animate')
        print('Thread id: ', threading.get_ident())
        print('Process id: ', os.getpid(), '\n')


        # Init
        self.ax.set_xlim(0, 2*np.pi)
        self.ax.set_ylim(-1, 1)
        return self.ln,

    def update(self, frame):
        self.xdata.append(frame)
        self.ydata.append(np.sin(frame))
        self.ln.set_data(self.xdata, self.ydata)
        return self.ln,

class Example:

    def __init__(self):

        # init window
        self.root = tk.Tk(className=' Species segmentation')
        # 替换plt.subplots为原生Figure创建方式
        self.fig = Figure()
        self.ax = self.fig.add_subplot()

        # init sine audio file
        self.fs = 44100
        self.dur = 2
        self.freq = 440
        self.x = np.sin(2*np.pi*np.arange(self.fs*self.dur)*self.freq/self.fs)

        # Embedd in tk
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)  # A tk.DrawingArea.
        self.canvas.draw()
        self.canvas.get_tk_widget().grid()

        # Plot something
        self.N = 100000
        self.xp = np.linspace(0, 10, self.N)
        self.ax.plot(self.xp, np.sin(2*np.pi*self.xp))
        self.ax.set_title(
            "Plot for demonstration purpuse")

        # init Rectangle Selector
        self.RS = RectangleSelector(self.ax, self.line_select_callback,
                                   drawtype='box', useblit=True,
                                   button=[1, 3],  # avoid using middle button
                                   minspanx=5, minspany=5,
                                   spancoords='pixels', interactive=True,
                                   rectprops={'facecolor': 'yellow', 'edgecolor': 'black', 'alpha': 0.15, 'fill': True})

        self.canvas.draw()

        tk.mainloop()

    def line_select_callback(self, eclick, erelease):
        print('enter line_select_callback')

        self.anim = LineAnimation(
            self.fig,
            self.ax)

        self.fig.canvas.draw()

Example()

内容的提问来源于stack exchange,提问作者Ronen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 04:18:04