假设我有一个请求输入的函数。在等待条目小部件中的用户输入时,如何暂停调用此函数的函数。我用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'变量成功获得的输入。
也许问题是函数正在运行,这就是为什么另一个函数无法执行...但如果我使用一个不同的线程,那不应该是无关紧要的吗?为什么没那样工作......?
这是一个创建简单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)
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线程通信。设置变量或使用信号量。此外,两个线程使用的数据结构可能必须受Lock
s保护,特别是如果两个线程都试图修改它们。
简而言之,使用线程并不像看起来那么简单。众所周知,多线程程序也很难调试。
等待用户输入的方法是打开一个对话框。模态对话框将强制用户关闭对话框,非模态对话框将允许用户继续使用主应用程序。
在您的情况下,您可以使用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,