我目前正在编写我的第一个tkinter图形用户界面.我正在try 制作一个交互式绘图,使用一些比例,以便用户可以设置影响该绘图的参数值.当我这样做时,它开始滞后,并且我的解决方案;使用线程的工作并不像预期的那样工作.

在我的实际代码中,有多个绘图,因此应用程序开始滞后,如下所示:

from tkinter import *
from tkinter import ttk

import threading

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import time

import numpy as np

def create_window(root):
    frame1 = Frame(root)
    frame2 = Frame(root)
    frame1.grid(row=0,column=0)
    frame2.grid(row=0,column=1)

    figure1 = plt.Figure(figsize=(5,5))
    figure1.set_tight_layout(True)
    
    ax1 = figure1.add_subplot(111)
    canvas1 = FigureCanvasTkAgg(figure1, frame1)
    canvas1.get_tk_widget().grid(column=1,row=1, padx=10, pady=10)

    m.trace_add('write', lambda var=None, index=None, mode=None: update_plot_tracer(ax1, canvas1))

    m_scale = ttk.Scale(frame2, orient=VERTICAL, length=200, from_=100.0, to=0.0, variable=m)
    m_scale.grid(column=0, row=0)
    update_plot(ax1,canvas1)


def update_plot(ax, canvas):
    #to make it lag
    time.sleep(0.1)

    x = np.arange(0,10,1)
    y = m.get() * x
    ax.clear()
    ax.plot(x,y)
    ax.set_ylim([0, 100])
    canvas.draw()
def update_plot_tracer(ax, canvas, var=None, index=None, mode=None):
    update_plot(ax, canvas)


if __name__ == '__main__':

    root = Tk()
    m = DoubleVar(value = 10.0)
    create_window(root)

    root.mainloop()

我的解决方案是使用线程,以便在绘制绘图时用户界面的其余部分仍然可用.我现在遇到的问题是,当用户大量拖动比例时,会有多个线程调用,这些线程会干扰我,所以我试图阻止程序同时多次更新,如下所示:

from tkinter import *
from tkinter import ttk

import threading

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

import time

import numpy as np

def create_window(root):
    frame1 = Frame(root)
    frame2 = Frame(root)
    frame1.grid(row=0,column=0)
    frame2.grid(row=0,column=1)

    figure1 = plt.Figure(figsize=(5,5))
    figure1.set_tight_layout(True)
    
    ax1 = figure1.add_subplot(111)
    canvas1 = FigureCanvasTkAgg(figure1, frame1)
    canvas1.get_tk_widget().grid(column=1,row=1, padx=10, pady=10)

    m.trace_add('write', lambda var=None, index=None, mode=None: update_plot_tracer(ax1, canvas1))

    m_scale = ttk.Scale(frame2, orient=VERTICAL, length=200, from_=100.0, to=0.0, variable=m)
    m_scale.grid(column=0, row=0)
    update_plot(ax1,canvas1)

    m_entry = ttk.Entry(frame2, textvariable = m)
    m_entry.grid(column=0, row=1)

def update_plot(ax, canvas):
    
    x = np.arange(0,10,1)
    y = m.get() * x
    print('last used value of m', m.get())
    ax.clear()
    ax.plot(x,y)
    ax.set_ylim([0, 100])
    canvas.draw()
    #to make it lag
    time.sleep(0.2)

def update_plot_tracer(ax, canvas, var=None, index=None, mode=None):
    global thread1#just to make this example work, in my real code i have it as a class variable...
    if thread1 is None or not thread1.is_alive():
        thread1 = threading.Thread(target= lambda: update_plot(ax, canvas))
        thread1.start()
        
if __name__ == '__main__':
    thread1 = None
    root = Tk()
    
    m = DoubleVar(value = 10.0)
    create_window(root)

    root.mainloop()

现在我有了下一个问题:当线程启动但用户不断移动鼠标时,他/她看到的值不是绘图中使用的值,所以我想做一些类似向队列添加线程的操作,但然后我会遇到这个队列可能会变得非常长的问题,而且需要一些时间才能更新到最新状态,即使当前运行的线程和最新添加的线程之间的所有线程都没有用,但据我所知,一旦我向队列添加了线程(从队列围栏),它就不能被删除.我从来没有用过这方面的大部分工作,所以任何帮助都是感激的.

此外,我想让它尽可能地保持互动性,所以只在用户松开天平后才更新不是我的首选方式.

编辑:问题是如何创建可以从中删除条目的线程队列?

推荐答案

尽管Tkinter支持在与创建TCL解释器的线程(根Tk实例)不同的线程中操作图形用户界面,但最好避免这种情况,因为实现并不完美,而且大多数图形用户界面框架不支持这种情况.我建议在后台线程中做数据处理工作,在主线程中做绘图工作.

现在,让我们转到队列中的主要主题.您不需要实现线程队列.当现有线程完成时,您可以只启动一个新线程.使用after_idle()来安排这样的功能.另外,别忘了加入帖子.

...
def update_plot_tracer(ax, canvas, var=None, index=None, mode=None):
    global thread1
    if thread1 is None:
        def entry():
            update_plot(ax, canvas)
            root.after_idle(on_end_thread)
        def on_end_thread(): # This will be run in the main thread.
            global thread1
            if thread1.invalidated:
                root.after_idle(lambda: update_plot_tracer(ax, canvas))
            thread1.join()
            thread1 = None
        thread1 = threading.Thread(target=entry)
        thread1.invalidated = False
        thread1.start()
    else:
        thread1.invalidated = True
...

Python相关问答推荐

CustomTKinter-向表单添加额外的输入字段

Plotly:如何更改Heatmap中彩色条的勾选文本

如何将新的SQL服务器功能映射到SQL Alchemy的ORM

剧作家Python:expect(locator).to_be_visible()vs locator.wait_for()

使用plotnine和Python构建地块

. str.替换pandas.series的方法未按预期工作

Vectorize多个头寸的止盈/止盈回溯测试pythonpandas

Mistral模型为不同的输入文本生成相同的嵌入

如何使用数组的最小条目拆分数组

如何从.cgi网站刮一张表到rame?

Python中绕y轴曲线的旋转

Python—从np.array中 Select 复杂的列子集

python中字符串的条件替换

考虑到同一天和前2天的前2个数值,如何估算电力时间序列数据中的缺失值?

如何在达到end_time时自动将状态字段从1更改为0

如何使用两个关键函数来排序一个多索引框架?

幂集,其中每个元素可以是正或负""""

干燥化与列姆化的比较

为什么在FastAPI中创建与数据库的连接时需要使用生成器?

ModuleNotFoundError:没有模块名为x时try 运行我的代码''