在等待输入(tkinter)时暂停函数执行

问题描述 投票:0回答:3

假设我有一个请求输入的函数。在等待条目小部件中的用户输入时,如何暂停调用此函数的函数。我用while循环和time.sleep(秒)尝试了它。此外,我在另一个线程中执行了该函数(因此主线程不应该被中断),但总是出现的问题是整个程序冻结(输入无法输入)!

因为我没有那么多Python经验,所以我真的被卡住了。

PS:我在mac上编码。

我用过的代码:

    import time
    _input = []

    def get_input()
      try:
        return _input[0]
      except IndexError:
        return None

    def req():
      while get_input() == None:
        time.sleep(1)
      return get_input()

函数req()总是在一个函数中调用,该函数在一个解析条目小部件中的输入的函数中通过'getattr()'调用。变量'_input'自动从条目中获取用户输入。然后丢弃从'_input'变量成功获得的输入。

也许问题是函数正在运行,这就是为什么另一个函数无法执行...但如果我使用一个不同的线程,那不应该是无关紧要的吗?为什么没那样工作......?

python python-3.x tkinter
3个回答
1
投票

这是一个创建简单Tkinter GUI的函数,允许用户将数据输入到Entry小部件中。当用户点击Enter时,该函数从Entry获取当前值,关闭GUI并将值返回给调用代码。调用代码将阻塞,直到tkinter_input返回。如果用户使用关闭按钮关闭GUI窗口,则忽略Entry的内容,并返回None

import tkinter as tk

def tkinter_input(prompt=""):
    root = tk.Tk()
    tk.Label(root, text=prompt).pack()
    entry = tk.Entry(root)
    entry.pack()
    result = None
    def callback(event):
        nonlocal result
        result = entry.get()
        root.destroy()
    entry.bind("<Return>", callback)
    root.mainloop()
    return result

result = tkinter_input("Enter data")
print(result)

1
投票

GUI编程与普通的python脚本完全不同。

当您看到GUI弹出时,它已经在mainloop中运行。这意味着您的代码仅作为附加到某个事件的回调或作为超时函数从mainloop调用。您的代码实际上会中断mainloop中的事件流。

因此,要保持GUI响应,回调和超时必须快速完成(比如最多0.1秒)。这就是为什么你不应该在回调中运行长循环; GUI将冻结。

因此,在GUI程序中进行长时间计算的规范方法是将其拆分为小块。而不是例如循环遍历for循环中的一长串项目,您可以创建一个全局变量来保存列表中的当前位置。然后,您创建一个超时函数(计划由after方法运行),该函数需要例如列表中的下10个项目,处理它们,更新当前位置并使用after重新安排自己。

获取函数输入的正确方法是在启动函数之前获取必要的输入。或者,您可以使用函数中的消息框来获取输入。但总的来说,将程序的“胆量”与GUI分开是一种很好的设计。 (考虑到您可能希望将来从Tkinter切换到GTK +或QT工具包。)

现在进入线程。您可能认为使用线程可以使长时间运行的任务更容易。但事实并非如此。首先,标准的Python实现(我们称之为CPython)有一个全局解释器锁,它确保一次只有一个线程可以运行Python字节码。因此,每次长时间运行的计算线程运行时,包含mainloop的另一个线程将停止。在Python 3中,线程调度在w.r.t中得到了改进。 Python 2尝试而不是饿死线程。但是,当其他线程正在进行大量工作时,无法保证GUI线程获得足够的运行时间。

另一个限制是Tkinter GUI工具包不是线程安全的。所以第二个线程不应该使用Tkinter调用。它必须通过例如GUI线程与GUI线程通信。设置变量或使用信号量。此外,两个线程使用的数据结构可能必须受Locks保护,特别是如果两个线程都试图修改它们。

简而言之,使用线程并不像看起来那么简单。众所周知,多线程程序也很难调试。


1
投票

等待用户输入的方法是打开一个对话框。模态对话框将强制用户关闭对话框,非模态对话框将允许用户继续使用主应用程序。

在您的情况下,您可以使用Toplevel创建一个对话框并将其填充到您想要的任何小部件,然后使用wait_window函数等待该窗口被销毁。要使其成为模态,您可以在顶层创建“抓取”。为了简单起见,我在下面的例子中没有这样做。

这是一个基本的例子。关键是调用wait_window,在对话框被销毁之前不会返回。

import tkinter as tk

class CustomDialog(object):
    def __init__(self, parent, prompt="", default=""):
        self.popup = tk.Toplevel(parent)
        self.popup.title(prompt)
        self.popup.transient(parent)

        self.var = tk.StringVar(value=default)

        label = tk.Label(self.popup, text=prompt)
        entry = tk.Entry(self.popup, textvariable=self.var)
        buttons = tk.Frame(self.popup)

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

        ok = tk.Button(buttons, text="Ok", command=self.popup.destroy)
        ok.pack(side="top")

        self.entry = entry

    def show(self):
        self.entry.focus_force()
        root.wait_window(self.popup)
        return self.var.get()

要使用它,请调用show方法:

dialog = CustomDialog(root, prompt="Enter your name:")
result = dialog.show()

有了上述内容,result将包含您输入的字符串。

有关创建对话框的更多信息,请参阅effbot站点上的Dialog Windows

© www.soinside.com 2019 - 2024. All rights reserved.