我正在尝试运行多线程应用程序, 每个线程在线程执行某些任务后都要求用户提供字符串输入。
simpledialog.askstring(title, content, parent=window)
问题是你不能使用主线程窗口作为线程内的父窗口,因为你最终会遇到这个错误:
_tkinter.TclError: window ".!_querystring2" was deleted before its visibility changed
这是符合逻辑的,因为线程和主线程不同步
经过艰苦的研究,我最终得到了一个解决方案,上面写着 “为线程创建一个新的TK窗口实例”
newWin = tk.Tk()
newWin.withdraw() #to make it invisible
simpledialog.askstring(title, content, parent=newWin)
newWin.destroy()
如果我在主线程内没有主 tk 窗口实例,这将适用于我的情况。 “newWin”的 destroy 方法也导致我的主窗口被销毁。 我最终会遇到这个错误:
Tcl_AsyncDelete: async handler deleted by the wrong thread
tkinter 不是线程安全的,并且在跨线程使用它时并不轻松。
有人知道如何向线程内的用户询问字符串吗?
有关我的代码的一些额外信息: 我通过单击按钮触发此功能:
#tree is a treeview widget
def click(tree):
threads = simpledialog.askstring("Threads", "How many threads?")
if threads is not None:
try:
threads = int(threads)
if threads > 0:
thread = tm.threadObject(name="checkAccounts", target=checkUsers, args=[tree, threads])
thread.start()
#instance passes automatically from the tm.threadObject
def checkUsers(instance, tree, threads):
for child in tree.get_children():
while len(tm.threadsManager.getThreads("checkAccountWorker")) >= threads and instance.alive:
print("Sleeping")
time.sleep(1)
if not instance.alive:
break
item = tree.item(child)
taskHandler = task.taskHandler(tree, child)
thread = tm.threadObject(name="checkAccountWorker", target=taskHandler.doAction, args=[taskHandler.CHECK_USER], kill=taskHandler.kill, killargs=[], passInstance = False)
thread.start()
#part of the doAction code:
def doAction(*args):
#some code behind
newWin = tkinter.Tk()
newWin.withdraw()
code = tkinter.simpledialog.askstring("Activation", "Enter recevied code : ", parent=newWin)
newWin.destroy()
self.currentStatus = "Entering code"
mainEvents.updateTreeItem(tree, "status", item, self.currentStatus)
def updateTreeItem(tree, column, item, value):
key = ""
for col in tree["columns"]:
if tree.heading(col)["text"].lower() == column.lower():
key = col
if key == "":
return
tree.set(item, key, value)
第一次尝试就有效。但是当我再次单击 checkUsers 按钮时,我最终收到错误: “Tcl_AsyncDelete:异步处理程序被错误的线程删除” 说实话我也不知道为什么
threading.Thread
对象并启动它。它的目标将是 askString
函数。askString
中,我们创建新窗口并向其添加小部件simpledialog.askstring
。下面的代码就可以解决问题:
import tkinter as tk
from tkinter import simpledialog
import threading
def askString():
new_window = tk.Tk()
new_window.withdraw()
print(simpledialog.askstring(title = "String input", prompt = "What's your Name?:", parent = new_window))
new_window.destroy()
root = tk.Tk()
btn = tk.Button(root, text = "Click me!", command = lambda: threading.Thread(target = askString).start())
btn.pack()
root.mainloop()
如何从函数获取值到 GUI 取决于您 实施(框架)。
好吧, 经过一些工作后,我创建了自己的“getString”窗口对话框..
代码:
@staticmethod
def getString(title, content):
topLevel = tk.Toplevel()
topLevel.title(title)
topLevel.resizable(False, False)
strVar = tk.StringVar()
label = tk.Label(topLevel, text=content)
label.grid(column=0, row=0, columnspan=2)
entry = tk.Entry(topLevel, textvariable=strVar)
entry.grid(column=0, row=1)
def close(topLevel):
if entry.get() != "":
topLevel.destroy()
button = tk.Button(topLevel, text="Set", command=lambda: close(topLevel))
button.grid(column=1, row=1)
topLevel.lift()
while(topLevel.winfo_exists()): # wait until topLevel destroyed
time.sleep(1)
return strVar.get()
有点混乱..无法完成更干净的解决方案,但它有效。 您可以通过这种方式在线程内请求字符串:) 如果有人有更好的方法,您可以建议。
您可以使用 python 的多进程来解决此错误。
import multiprocessing as mp
def askstring_helper(*args, **kwargs):
root = tk.Tk()
root.withdraw()
result_queue = kwargs.pop('result_queue')
kwargs['parent'] = root
result_queue.put(simpledialog.askstring(*args, **kwargs))
root.destroy()
def askstring(*args, **kwargs):
result_queue = mp.Queue()
kwargs['result_queue'] = result_queue
askstring_thread = mp.Process(target=askstring_helper, args=args, kwargs=kwargs)
askstring_thread.start()
result = result_queue.get()
if askstring_thread.is_alive():
askstring_thread.join()
return result
现在您可以在任何代码中使用askstring,就像使用 simpledialog.askstring 一样。