Question

I have a program I've been writing that began as a helper function for me to find a certain report on a shared drive based on some information in that report. I decided to give it a GUI so I can distribute it to other employees, and have ran into several errors on my first attempt to implement tkinter and threading.

I'm aware of the old adage "I had one problem, then I used threads, now I have two problems." The thread did, at least, solve the first problem -- so now on to the second....

My watered down code is:

class GetReport(threading.Thread):
    def __init__(self,root):
        threading.Thread.__init__(self)

        # this is just a hack to get the StringVar in the new thread, HELP!
        self.date = root.getvar('date')
        self.store = root.getvar('store')
        self.report = root.getvar('report')
        # this is just a hack to get the StringVar in the new thread, HELP!

        self.top = Toplevel(root)
        ttk.Label(self.top,text="Fooing the Bars into Bazes").pack()
        self.top.withdraw()
    def run(self):
        self.top.deiconify()
        # a function call that takes a long time
        self.top.destroy() #this crashes the program

def main():
    root = Tk()
    date,store,report = StringVar(),StringVar(),StringVar()

    #####
    ## labels and Entries go here that define and modify those StringVar
    #####

    def launchThread(rpt):
        report.set(rpt)

        # this is just a hack to get the StringVar in the new thread, HELP!
        root.setvar('date',date.get())
        root.setvar('store',store.get())
        root.setvar('report',report.get())
        # this is just a hack to get the StringVar in the new thread, HELP!

        reportgetter = GetReport(root)
        reportgetter.start()

    ttk.Button(root,text="Lottery Summary",
                command=lambda: launchThread('L')).grid(row=1,column=3)

    root.mainloop()

My expected output is for root to open and populate with Labels, Entries, and Buttons (some of which are hidden in this example). Each button will pull data from the Entries and send them to the launchThread function, which will create a new thread to perform the foos and the bars needed to grab the paperwork I need.

That thread will launch a Toplevel basically just informing the user that it's working on it. When it's done, the Toplevel will close and the paperwork I requested will open (I'm using ShellExecute to open a .pdf) while the Thread exits (since it exits its run function)

What's ACTUALLY happening is that the thread will launch its Toplevel, the paperwork will open, then Python will become non-responsive and need to be "end processed".

Was it helpful?

Solution 2

Tkinter cannot accept any commands from threads other than the main thread, so launching a TopLevel in a thread will fail by design since it cannot access the Tk in the other thread. To get around this, use the .is_alive method of threads.

def GetReport(threading.Thread):
    def __init__(self,text):
        self.text = text
        super().__init__()

    def run(self):
        # do some stuff that takes a long time
        # to the text you're given as input

def main():
    root = Tk()
    text = StringVar()

    def callbackFunc(text):
        top = Toplevel(root)
        ttk.Label(top,text="I'm working here!").pack()
        thread = GetReport(text)
        thread.start()
        while thread.is_alive():
            root.update() # this keeps the GUI updating
        top.destroy() # only when thread dies.

    e_text = ttk.Entry(root,textvariable=text).pack()
    ttk.Button(root,text="Frobnicate!",
        command = lambda: callbackFunc(text.get())).pack()

OTHER TIPS

As far as I know you cannot use Threading to alter any GUI elements. Such as destroying a Toplevel window.

Any Tkinter code needs to be done in the main loop of your program.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top