【问题标题】:Python3, Understanding the "GUI loop" as in tkinter-mainloop() or QtPy-exec_()Python3,了解 tkinter-mainloop() 或 QtPy-exec_() 中的“GUI 循环”
【发布时间】:2016-08-07 20:03:05
【问题描述】:

3 个子问题:
[1] python GUI 是基于轮询的?
在我看来,tk 和 qtpy 都是基于轮询的,如果 gui 调用一个需要一段时间才能执行的函数,整个 gui 就会挂起。
很早以前就了解了gui,我记得现代的gui应该是基于中断的,即使gui执行了一些大的事情,gui也应该一直响应。 gui 可能不会显示这些大计算的结果,但它会响应调整大小、显示按钮单击动画等。所以我的问题是,是否有类似的选项:

    #psuedo code
    root=tkinter.Tk()
    root.setInterruptMode(True)

[2] tk.mainloop() 只是一个巨大的循环吗?
如果我的第一个问题是白日梦,我只需要学习线程和多进程,那么我的下一个问题是关于root.mainloop()(或qtpy 的exec_())。
我的印象是 mainloop() 并没有真正在 python 中启动线程或任何东西,它只是将一个巨大且不可见的 tkinter 的 gui polling+painting 循环打包到我的主线中。我的印象正确吗?

[3] 为什么要把 mainloop 放在 Main 行?
mainloop() 是否必须驻留在主线中?我可以线程/多处理它吗?这样我的主线就可以专注于大计算,主线控制gui进程和IO进程。我遇到的所有示例在 Main 行中都有 mainloop(),我不确定这是推荐的方法或有什么好处。

下面是我在学习python gui时写的代码:

    import tkinter
    import random

    class myGUI():
        def __init__(self, arg_tkroot):
            self.GUI_display = tkinter.Label(arg_tkroot, text='init-ed')
            self.GUI_button = tkinter.Button(arg_tkroot, text='click')
            self.GUI_display.pack()
            self.GUI_button.pack()

            self.GUI_button.bind('<Button-1>', self.handle_user_interaction)
            self.list_bigData = []

        #handles GUI interaction, and call bigData_and_bigCalculation()
        def handle_user_interaction(self, arg_event):
            print(arg_event, ' detected by myGUI')
            strResult_toFeedbackToUser = self.bigData_and_bigCalculation()
            self.GUI_display.config(text=strResult_toFeedbackToUser)
            print('finished handling user interact')

        # slow calculations and memory consuming operations
        def bigData_and_bigCalculation(self):
            self.list_bigData[:]=[]
            for i in range(500000):
                self.list_bigData.append( ''.join(random.choice('asdfas') for k in range(10)) )
            return self.list_bigData[-1]
    # Main()
    if __name__ == '__main__':
        root = tkinter.Tk()
        mygui = myGUI(root)
        root.mainloop()

【问题讨论】:

  • 好吧,如果你将在 GUI 线程中执行一些长时间的工作,那么 GUI 将冻结。为避免这种情况,请为不同的作业生成不同的线程。
  • 有趣的问题;我已经在 Tk 中编写了大约 10 个小型和一个平均大小的应用程序(在 Qt 中没有),从来没有出现过响应问题,也从未想过 Tk 的 GUI 方式 :) 我建议总体上遵循 ForceBru adwise;顺便说一句,我已经将 Kivy 用于我的 GUI 应用程序,可以推荐,它比 Tk 更灵活,并且如果需要也可以在 android 和 ios 上很好地工作。
  • 我担心不得不使用线程和多进程是不可避免的。但是在我的第一个子问题中,我之前了解了基于“中断”的 gui(当时的 java 小程序),我当时从未遇到过 gui freeze,这是一个错误的印象,因为我们在学习这些东西时从未做过任何大的计算?
  • 如果您可以将 1 秒以上的工作分解为 10 甚至 100 毫秒的片段,那么您可以使用 root.after 循环并在对其他事件的响应中穿插工作片段。在 SO 中搜索 [tkinter] root.after 以获取示例。

标签: python qt tkinter python-3.5


【解决方案1】:

[1] GUI 是基于轮询的吗?

GUI 是基于事件的——GUI 中发生的一切都是对事件的响应。应用程序如何处理事件取决于应用程序。如果事件的处理可以在几百毫秒或更短的时间内发生,则可以在主线程中进行处理。如果需要更长的时间,应用程序应该在单独的线程或进程中运行代码。

对于 Tkinter,Tkinter 设计为在单线程中运行。这并不意味着您不能使用线程,只是对 tkinter 对象的所有访问都应该在单个线程上。常见的做法是让其他线程通过线程安全队列与 GUI 通信,GUI 线程负责定期检查该队列。

所以我的问题是,有没有像 [root.setInterruptMode(True)] 这样的选项

在 tkinter 中,不,没有办法进入特殊的“中断”模式。

[2] 这只是一个巨大的循环?

我假设“它”是指对mainloop 的调用。是的,这是一个巨大的循环。或者更准确地说,这是一个小循环。它所做的只是等待一个事件,然后查找该事件的处理程序并运行该处理程序。

我的印象是 mainloop() 在 python 中并没有真正启动线程或任何东西

正确。它在当前线程中运行事件循环。

[3] 为什么要把 mainloop 放在 Main 行?

您必须询问原始开发人员。可能是因为这对于非常非常大比例的应用程序来说是必需的(也许在发明工具包时比现在更真实)。

Tkinter 只不过是一个 tcl 解释器的包装器,并在解释器中加载了 tk 工具包。 tcl/tk 被设计为嵌入到其他应用程序中,因此它需要是轻量级的,并且能够在不支持线程的系统上运行(tcl 已经足够老,无法在每个平台上都保证线程支持)

mainloop() 是否必须驻留在 Main 行中?我可以线程/多处理它吗?

mainloop() 需要在创建根窗口的同一线程中运行。如果您需要线程(同样,您很可能不需要线程),您当然可以在单独的线程中运行您的事件处理程序。它增加了复杂性,但如果你需要它就在那里。对于一大类程序,根本不需要那么复杂。

您应该能够创建一个工作线程并从那里创建和运行您的 GUI。我从未尝试过,但我看不出它为什么不起作用。 Tkinter 不在乎它是主线程,只关心所有 tkinter 代码在同一个线程中运行。

我遇到的所有示例在 Main 行中都有 mainloop(),我不确定这是推荐的方法或有什么好处。

在主线程中调用 mainloop 是 tkinter 的设计工作方式。如果您有长时间运行的计算,则应将这些计算放在单独的线程或进程中。

【讨论】:

  • 谢谢你一步一步的回答,我肯定需要线程或进程,我有太多的IO和大量的大数据结构。我感兴趣的是,在您之前对类似问题的回答中,这里的其他助手引用了您提到的 tk 不适合在线程模式下运行;但你现在似乎在说,这样做是可以的。
猜你喜欢
  • 2015-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
相关资源
最近更新 更多