为了账号安全,请及时绑定邮箱和手机立即绑定

Tkinter GUI、I/O 和线程:何时使用队列,何时使用事件?

Tkinter GUI、I/O 和线程:何时使用队列,何时使用事件?

繁花如伊 2023-05-23 14:55:26
我正在使用 TKinter 构建一个 GUI(用于与多通道分析仪的套接字连接)以定期(~15 秒)接收和绘制数据(~15.000.000 值)。在接收数据时,我不希望 GUI 冻结,所以我使用多线程进行连接处理、数据接收和绘图操作。正如在可重现的代码中看到的那样,我通过设置一个事件并threading.Event()一个接一个地处理线程(initSettings()&中的几行代码acquireAndPlotData)来实现这一点。我唯一一次干扰 GUI 是在绘制到画布时 & 我使用 tkintersafter()方法执行此操作。启动时,只要窗口打开并按预期工作,代码就会运行而不会冻结并接收和绘图。当我读到在 tkinter GUI 中处理阻塞 I/O 操作时,我只找到了递归排队和检查队列的示例(使用Queue& after(), 1 2 3 4 5 ),但我发现它更方便,更容易处理这些操作threading.Event()。现在我的问题是:我是在使用正确的方法还是在这里遗漏了一些重要的东西?(关于线程安全,竞争条件,如果绘图失败并且花费的时间比数据采集时间长怎么办?我没有想到的东西?不良做法?等等......)我将非常感谢您对此事的反馈!
查看完整描述

1 回答

?
波斯汪

TA贡献1811条经验 获得超4个赞

所以我这样做了,但我不知道它是否适合你,或者这是否是一个很好的方法,但它可以像评论中所述那样保护你,它的好处是.after你的函数do_stuff只在需要时被调用.


import tkinter as tk

import time

import threading


def get_data():

    time.sleep(3)

    print('sleeped 3')

    _check.set(1)


def do_stuff():

    try:

        root.configure(bg='#'+str(_var.get()))

        _var.set(_var.get()+101010)

    except:

        _var.set(101010)


root = tk.Tk()

_check = tk.IntVar(value=0)

_var = tk.IntVar(value=101010)



def callback(event=None, *args):

    t1 = threading.Thread(target=get_data)

    t1.start()

    

    do_stuff()

    

_check.trace_add('write', callback) #kepp track of that variable and trigger callback if changed

callback() # start the loop




root.mainloop()

一些研究:

interpreter 仅在创建它的线程中有效,所有 Tk 活动也必须在该线程中发生。这意味着必须在创建解释器的线程中调用主循环。可以从其他线程调用命令;_tkinter 将为解释器线程排队一个事件,然后解释器线程将执行命令并传回结果。

#l1493 var_invoke

 The current thread is not the interpreter thread.  Marshal

       the call to the interpreter thread, then wait for

       completion. */    if (!WaitForMainloop(self))  
             return NULL;

在 python 线程中使用 intvar-doublevar 是否安全

当您设置一个变量时,它会在与该变量关联的主控件上调用 globalsetvar 方法。_tk.globalsetvar 方法在 C 中实现,并在内部调用 var_invoke,后者在内部调用 WaitForMainLoop,它将尝试安排命令在主线程中执行,如我上面引用的 _tkinter 源中所述。

     Start

       |

       |<----------------------------------------------------------+

       v                                                           ^

   Do I have    No[*]  Calculate how            Sleep for at       |

   work to do?  -----> long I may sleep  -----> most that much --->|

       |                                        time               |

       | Yes                                                       |

       |                                                           |

       v                                                           |

   Do one callback                                                 |

       |                                                           |

       +-----------------------------------------------------------+

常识

来自错误追踪器:

Tkinter 和线程。

如果你想同时使用 tkinter 和线程,最安全的方法是在主线程中进行所有 tkinter 调用。如果工作线程生成 tkinter 调用所需的数据,请使用 queue.Queue 将数据发送到主线程。为了彻底关闭,添加一个方法来等待线程停止并在按下窗口关闭按钮 [X] 时调用它。

效果机器人

只需在主线程中运行所有 UI 代码,让编写器写入一个 Queue 对象;例如

结论

你做事的方式和我做事的方式似乎很理想,但它们似乎一点也没错。这取决于需要什么。


查看完整回答
反对 回复 2023-05-23
  • 1 回答
  • 0 关注
  • 117 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信