Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
227 views
in Technique[技术] by (71.8m points)

python - Tkinter ask a string inside a thread

I'm trying to run a multi thread application, each thread is asking for the user to provide a string input after some tasks the thread does.

simpledialog.askstring(title, content, parent=window)

the problem is that you cannot use the main thread window as the parent window inside a thread because you'll end up with this error:

_tkinter.TclError: window ".!_querystring2" was deleted before its visibility changed

which is logical because the thread and the main thread are not synchronized

after hard research I've ended up with a solution which says "Create a new TK window instance for the thread"

newWin = tk.Tk()
newWin.withdraw() #to make it invisible
simpledialog.askstring(title, content, parent=newWin)
newWin.destroy()

which will work for my case if I wouldn't have a main tk window instance inside the main thread. the destroy method for the "newWin" is causing my main window to be destroyed as well. and I'll be ended up with this error :

Tcl_AsyncDelete: async handler deleted by the wrong thread

tkinter isn't a thread safe, and not gives an easy life when using it cross threads.

does anyone have any idea/trick how can I ask a string from a user inside a thread?

Some extra info about my code : I'm triggering this function by a button click:

#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)

It works at the first attempt. but when I click again on the checkUsers button I end up with the error : "Tcl_AsyncDelete: async handler deleted by the wrong thread" I honestly don't know why

question from:https://stackoverflow.com/questions/65868995/tkinter-ask-a-string-inside-a-thread

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
  • Every time button is clicked we create new threading.Thread object and start it. It's target will be the askString function.
  • In the askString we create new window and add to it widget simpledialog.askstring.

This code below will do the trick:

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()

How you get the value from the function to your GUI is up to your implementation (framework).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...