python - 为什么 Tkinter 文本小部件 "lags"会更新以增加窗口高度?

标签 python user-interface tkinter tk-toolkit neovim

我目前正在为 neovim 实现一个示例 UI , 由于流行/简单,决定使用 Tkinter/python 平台。我遇到的问题是 tkinter 似乎“堆叠” 当窗口高度超过特定阈值时,UI 会更新。

Here是显示问题的视频。

右边的窗口是运行 neovim 的终端模拟器,左边的 window 是连接到它的 Tkinter UI 程序。这个想法是 tkinter UI 应该镜像 neovim 终端屏幕,包括 方面。在这个视频中,永远不要把注意力从 终端窗口,因此 Tk 必须处理的唯一事件来自 连接到 neovim(描述屏幕的虚拟“nvim”事件 更新)

视频的第一部分显示一切正常 窗口高度很小,但当我增加时开始滞后更新 高度。

Here是 Tkinter 程序的代码。虽然 neovim API 非常新并且仍在大量开发中(代码对某些读者来说可能没有意义),但我认为我要解决的问题接近于实现终端 模拟器(使用 Tk 文本小部件):它必须有效地处理大量的格式化文本更新。

我在 GUI 编程方面非常缺乏经验。 Tkinter 是一个明智的选择吗? 这个任务?如果是,那么有人可以提示我做错了什么吗?

稍微解释一下发生了什么:Neovim API 是线程安全的,vim.next_event() 方法会阻塞(无需忙等待,它在底层使用 libuv 事件循环)直到事件发生收到。

vim.next_event() 调用返回时,它将使用 generate_event 通知 Tkinter 线程,这将进行实际的事件处理(它还在 之间缓冲事件code>redraw:startredraw:stop 以优化屏幕更新)。

所以实际上有两个并行运行的事件循环,后台事件循环以线程安全的方式提供给 Tkinter 事件循环(generate_event 方法是为数不多的可以调用的方法之一来自其他线程)

最佳答案

我会仔细检查,事实上,Tkinter 才是阻碍。我这样做的方法是在收到事件时简单地写到终端。

但现在我仔细看看这可能是你的问题:

    t = Thread(target=get_nvim_events, args=(self.nvim_events,
                                             self.vim,
                                             self.root,))

线程不能很好地与事件循环一起使用——Tkinter 已经有一个。我不确定 neovim api 是否设置为使用回调,但这通常是您想要传播更改的方式。

既然您说您不熟悉 GUI 编程,那么我假设您不熟悉事件循环的概念。基本上,假设您有一些如下所示的代码:

while True:
    if something_to_do:
        do_it_now()

显然这是一个繁忙的循环,会消耗你的 CPU,所以通常你的事件循环会阻止或设置操作系统的回调,这允许它放弃 CPU,当一些有趣的事情发生时,操作系统会说这样的话,“有人点击了这里”或“有人按下了某个键”或“嘿,你让我现在叫醒你!”

因此,作为 GUI 开发人员,您的工作就是插入该事件循环。您并不真正关心何时某事发生 - 您只是想对其做出回应。使用 Tkinter,您可以使用 .after 方法来做到这一点 see "non-event callbacks" . .after_idle method 可能是一个不错的选择:

Registers a callback that is called when the system is idle. The callback will be called there are no more events to process in the mainloop. The callback is only called once for each call to after_idle.

这意味着您不会阻止按键或鼠标点击,它只会在 Tkinter 处理完其他内容(例如绘图、调用回调等)后运行

我预计可能发生的情况是您的线程和主循环出现问题(可能是由于 GIL)。我环顾四周,但没有立即看到任何明显的东西,但你想要做的是:

def do_something(arg):
    # do something with `arg` here


def event_happened(event_args): #whatever args the event generates
    root.after_idle(lambda: do_something(event_args))

vim.bind("did_something", event_happened)

当然,您也可以完全绕过事件循环,让事件执行您想要的操作。

关于python - 为什么 Tkinter 文本小部件 "lags"会更新以增加窗口高度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24292825/

相关文章:

python - 如何找出我未处理的异常是什么

python - Tkinter 使用 pyHook 撤回奇怪的内容

python - 当使用 noexec 挂载/tmp 时,为什么在 Python 中出现段错误?

python - 如何将两个或多个计数器合并到一个字典中?

java程序卡在while循环中

JavaFX 绘制形状时出现鼠标输入问题?

python - Pycharm 项目解释器

python - 我们如何在 Cheetah 中预编译基本模板,以便 #include、#extends 和 #import 在 Weby 中正常工作

c# - Form.ActiveForm 有时有效

python - 如何从 Tkinter 文本小部件读取文本