微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

有没有办法延迟对 matplotlib 滑块的 on_changed() 调用?

如何解决有没有办法延迟对 matplotlib 滑块的 on_changed() 调用?

我目前正在创建一个 tkinter GUI,它显示一个带有两个滑块的 matplotlib 图。滑块用于选择数据范围(它们连接到图中的垂直线)。

我遇到的问题是,当我快速更改滑块几次时,GUI 会冻结。我相信这是由于我用来更新滑块的 on_changed() 调用。当您快速移动滑块时,我相信 on_changed() 以比程序可以跟上的速度更快的速度被多次调用,从而导致冻结。

有什么方法可以为 on_changed() 调用添加延迟,以便它只查看滑块是否在每个给定的时间间隔(例如 100 毫秒)发生更改?

下面是我发现的示例代码,它以我描述的方式冻结。

import tkinter as tk
from tkinter.ttk import Notebook
from tkinter import Canvas
from tkinter import messageBox as msg

import numpy as np
from matplotlib.figure import figure
from matplotlib.backends.backend_tkagg import figureCanvasTkAgg

from matplotlib.widgets import Slider,Button,RadioButtons



class LukeOutline(tk.Tk):

    def __init__(self):
        # Inherit from tk.Tk
        super().__init__()

        # Title and size of the window
        self.title('Luke Outline')
        self.geometry('600x400')

        # Create the drop down menus
        self.menu = tk.Menu(self,bg='lightgrey',fg='black')

        self.file_menu = tk.Menu(self.menu,tearoff=0,fg='black')
        self.file_menu.add_command(label='Add Project',command=self.unfinished)

        self.menu.add_cascade(label='File',menu=self.file_menu)

        self.config(menu=self.menu)

        # Create the tabs (Graph,File Explorer,etc.)
        self.notebook = Notebook(self)

        graph_tab = tk.Frame(self.notebook)
        file_explorer_tab = tk.Frame(self.notebook)

        # Sets the Graph Tab as a Canvas where figures,images,etc. can be added
        self.graph_tab = tk.Canvas(graph_tab)
        self.graph_tab.pack(side=tk.TOP,expand=1)

        # Sets the file explorer tab as a text Box (change later)
        self.file_explorer_tab = tk.Text(file_explorer_tab,bg='white',fg='black')
        self.file_explorer_tab.pack(side=tk.TOP,expand=1)

        # Add the tabs to the GUI
        self.notebook.add(graph_tab,text='Graph')
        self.notebook.add(file_explorer_tab,text='Files')

        self.notebook.pack(fill=tk.BOTH,expand=1)

        # Add the graph to the graph tab
        self.fig = figure()
        graph = figureCanvasTkAgg(self.fig,self.graph_tab)
        graph.get_tk_widget().pack(side='top',fill='both',expand=True)
        EllipseSlider(self.fig)

    #------------------------------------------------------
    def quit(self):
        '''
        Quit the program
        '''
        self.destroy()

    #------------------------------------------------------
    def unfinished(self):
        '''
        MessageBox for unfinished items
        '''
        msg.showinfo('Unfinished','This feature has not been finished')

    #------------------------------------------------------
    def random_graph(self):
        x = list(range(0,10))
        y = [i**3 for i in x]

        fig = figure()
        axes = fig.add_subplot(111)
        axes.plot(x,y,label=r'$x^3$')
        axes.legend()

        return fig

#----------------------------------------------------------

class EllipseSlider():

    #------------------------------------------------------
    def __init__(self,fig):
        self.fig = fig

        # Initial values
        self.u = 0.     #x-position of the center
        self.v = 0.     #y-position of the center
        self.a = 2.     #radius on the x-axis
        self.b = 1.5    #radius on the y-axis

        # Points to plot against
        self.t = np.linspace(0,2*np.pi,100)

        # Set up figure with centered axes and grid
        self.ax = self.fig.add_subplot(111)
        self.ax.set_aspect(aspect='equal')
        self.ax.spines['left'].set_position('center')
        self.ax.spines['bottom'].set_position('center')
        self.ax.spines['right'].set_color('none')
        self.ax.spines['top'].set_color('none')
        self.ax.xaxis.set_ticks_position('bottom')
        self.ax.yaxis.set_ticks_position('left')
        self.ax.set_xlim(-2,2)
        self.ax.set_ylim(-2,2)
        self.ax.grid(color='lightgray',linestyle='--')

        # Initial plot
        self.l,= self.ax.plot(self.u+self.a*np.cos(self.t),self.v+self.b*np.sin(self.t),'k')

        # Slider setup
        self.axcolor = 'lightgoldenrodyellow'
        self.axb = self.fig.add_axes([0.25,0.1,0.65,0.03],facecolor=self.axcolor)
        self.axa = self.fig.add_axes([0.25,0.15,facecolor=self.axcolor)

        self.sb = Slider(self.axb,'Y Radius',2.0,valinit=self.b)
        self.sa = Slider(self.axa,'X Radius',valinit=self.a)

        # Call update as slider is changed
        self.sb.on_changed(self.update)
        self.sa.on_changed(self.update)

        # Reset if reset button is pushed
        self.resetax = self.fig.add_axes([0.8,0.025,0.04])
        self.button = Button(self.resetax,'Reset',color=self.axcolor,hovercolor='0.975')

        self.button.on_clicked(self.reset)

        # Color button setup
        self.rax = self.fig.add_axes([0.025,0.5,0.15],facecolor=self.axcolor)
        self.radio = RadioButtons(self.rax,('red','blue','green'),active=0)

        self.radio.on_clicked(self.colorfunc)

    #------------------------------------------------------

    def update(self,val):
        '''
        Updates the plot as sliders are moved
        '''
        self.a = self.sa.val
        self.b = self.sb.val
        self.l.set_xdata(self.u+self.a*np.cos(self.t))
        self.l.set_ydata(self.u+self.b*np.sin(self.t))

    #------------------------------------------------------

    def reset(self,event):
        '''
        Resets everything if reset button clicked
        '''
        self.sb.reset()
        self.sa.reset()

    #------------------------------------------------------

    def colorfunc(self,label):
        '''
        Changes color of the plot when button clicked
        '''
        self.l.set_color(label)
        self.fig.canvas.draw_idle()

#----------------------------------------------------------

if __name__ == '__main__':
    luke_gui = LukeOutline()
    luke_gui.mainloop()
    es = EllipseSlider()

(更新):

我已经实施了您显示的更正,代码看起来按预期工作,除了我现在使用的垂直滑块在我第二次移动滑块之前不会更新它们的位置。请参阅下面我的代码部分:

        # Call update if slider is changed
        self.sa.on_changed(self.schedule_update)
        self.sb.on_changed(self.schedule_update)

    def update(self,value):
        print(value)
        self.vert_a.set_xdata(value)
        self.vert_b.set_xdata(self.sb.val)
        root.update()

    def schedule_update(self,new_value):
        if self.after_id:
            root.after_cancel(self.after_id)
        self.after_id = root.after(1000,self.update,new_value)


root = tk.Tk()
mainapp = MainApp(root)
root.mainloop()

解决方法

Tkinter 有一个名为 after 的方法用于延迟命令的执行。您的滑块可以使用它来延迟滑块的效果一两秒钟。如果滑块移动,它可以取消之前安排的命令并提交新的命令。

为了简单起见,这是一个使用普通 tkinter 的示例。运行代码,然后快速或缓慢移动滑块。您会看到标签只有在您没有移动滑块一整秒后才会更新。

import tkinter as tk


after_id = None
def schedule_update(new_value):
    global after_id
    if after_id:
        root.after_cancel(after_id)
    after_id = root.after(1000,update_label,new_value)

def update_label(new_value):
    label.configure(text=f"New value: {new_value}")

root = tk.Tk()
label = tk.Label(root,text="",width=20)
scale = tk.Scale(
    root,from_=1,to=100,orient="horizontal",command=schedule_update
)

label.pack(side="top",fill="x",padx=20,pady=(10,0))
scale.pack(side="bottom",pady=10)

root.mainloop()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。