在 Tkinter 中可缓慢滚动多个绘图

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

这里的主要问题是使用

FigureCanvasTkAgg
绘制太多数据(~36 个子图)时,当用户滚动时,Tkinter 的
Scrollbar
过于迟缓和缓慢。这个问题有什么解决方案吗,我不希望我的用户遇到这个问题。谢谢。

您可以在这里尝试此代码:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk

class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Scrollable Matplotlib Plot with 9 Subplots")

        # Create a frame for the canvas and scrollbars
        frame = tk.Frame(root)
        frame.pack(fill=tk.BOTH, expand=True)

        # Create a canvas for the Matplotlib figure
        self.canvas = tk.Canvas(frame)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # Create vertical scrollbar
        self.v_scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=self.canvas.yview)
        self.v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Create horizontal scrollbar
        self.h_scrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=self.canvas.xview)
        self.h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)

        # Configure canvas with scrollbars
        self.canvas.configure(yscrollcommand=self.v_scrollbar.set)
        self.canvas.configure(xscrollcommand=self.h_scrollbar.set)

        # Create a figure and multiple subplots
        self.fig, self.axs = plt.subplots(6, 6, figsize=(10, 10))  # 3x3 grid of subplots

        # Generate some data for the plots
        x = np.linspace(0, 10, 100)
        for i, ax in enumerate(self.axs.flat):
            ax.plot(x, np.sin(x + i), label=f'Sine Wave {i+1}')
            ax.set_title(f'Plot {i+1}')
            ax.legend()

        # Add the Matplotlib figure to the canvas
        self.figure_canvas = FigureCanvasTkAgg(self.fig, master=self.canvas)
        self.figure_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # Bind the canvas to scrolling
        self.canvas.create_window((0, 0), window=self.figure_canvas.get_tk_widget(), anchor='nw')

        # Update the scroll region
        self.update_scroll_region()

        # Bind the resize event to update scroll region
        self.root.bind("<Configure>", self.on_resize)

    def update_scroll_region(self):
        """Update the scroll region based on the canvas content."""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def on_resize(self, event):
        """Update the scroll region when the window is resized."""
        self.update_scroll_region()

# Create the main window
root = tk.Tk()
app = App(root)
root.mainloop()
python tkinter
1个回答
0
投票

速度变慢的原因是滚动条触发了画布上的重绘,这对底层 matplotlib 绘图进行了不必要的重绘,而 matploltib 速度很慢。

您可以通过子类化

FigureCanvasTkAgg
来跳过 matplotlib 重绘,并在画布滚动时跳过下一次重绘,因为重绘发生在将来,而不是在滚动回调内。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
import time

class MyCanvas(FigureCanvasTkAgg):
    def __init__(self, *args, **kwargs):
        super().__init__(*args,**kwargs)
        self._skip_redraw = False
    def skip_redraw(self):
        self._skip_redraw = True
    def draw(self):
        if self._skip_redraw:
            self._skip_redraw = False
            return
        super().draw()


class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Scrollable Matplotlib Plot with 9 Subplots")
        self.last_update_time = time.time()
        # Create a frame for the canvas and scrollbars
        frame = tk.Frame(root)
        frame.pack(fill=tk.BOTH, expand=True)

        # Create a canvas for the Matplotlib figure
        self.canvas = tk.Canvas(frame)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        # Create vertical scrollbar
        self.v_scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=self.canvas.yview)
        self.v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # Create horizontal scrollbar
        self.h_scrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=self.canvas.xview)
        self.h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)

        # Configure canvas with scrollbars
        def my_h_scroll_command(*args):
            self.figure_canvas.skip_redraw()
            self.canvas.xview(*args)
        def my_v_scroll_command(*args):
            self.figure_canvas.skip_redraw()
            self.canvas.yview(*args)
        self.v_scrollbar.configure(command=my_v_scroll_command)
        self.h_scrollbar.configure(command=my_h_scroll_command)
        self.canvas.configure(yscrollcommand=self.v_scrollbar.set)
        self.canvas.configure(xscrollcommand=self.h_scrollbar.set)

        # Create a figure and multiple subplots
        self.fig, self.axs = plt.subplots(6, 6, figsize=(10, 10))  # 3x3 grid of subplots

        # Generate some data for the plots
        x = np.linspace(0, 10, 100)
        for i, ax in enumerate(self.axs.flat):
            ax.plot(x, np.sin(x + i), label=f'Sine Wave {i+1}')
            ax.set_title(f'Plot {i+1}')
            ax.legend()

        # Add the Matplotlib figure to the canvas
        self.figure_canvas = MyCanvas(self.fig, master=self.canvas)
        self.figure_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # Bind the canvas to scrolling
        self.canvas.create_window((0, 0), window=self.figure_canvas.get_tk_widget(), anchor='nw')

        # Update the scroll region
        self.update_scroll_region()

        # Bind the resize event to update scroll region
        self.root.bind("<Configure>", self.on_resize)

    def update_scroll_region(self):
        """Update the scroll region based on the canvas content."""
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))
    def on_resize(self, event):
        """Update the scroll region when the window is resized."""
        self.update_scroll_region()

# Create the main window
root = tk.Tk()
app = App(root)
root.mainloop()
© www.soinside.com 2019 - 2024. All rights reserved.