Tkinter 询问线程内的字符串

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

我正在尝试运行多线程应用程序, 每个线程在线程执行某些任务后都要求用户提供字符串输入。

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:异步处理程序被错误的线程删除” 说实话我也不知道为什么

python multithreading tkinter
3个回答
1
投票
  • 每次单击按钮时,我们都会创建新的
    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 取决于您 实施(框架)。


0
投票

好吧, 经过一些工作后,我创建了自己的“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()

有点混乱..无法完成更干净的解决方案,但它有效。 您可以通过这种方式在线程内请求字符串:) 如果有人有更好的方法,您可以建议。


0
投票

您可以使用 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 一样。

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