DuckPuncher DuckPuncher - 14 days ago 5
Python Question

How to create a GUI using Tkinter that can operate on a function and still accept input?

I have been trying to program a GUI for a timer.

I have tried threading after someone told me that is what I need to do. They told me that I needed threads to handle the GUI input and the calculations. I don't know if I did this incorrectly, or if it wasn't good advice.

Someone just told me, "Tkinter isn't thread safe. You should only create and access widgets from a single thread" after I asked why my Toplevel() widget kept freezing my program. It doesn't work on Windows, but it worked on Ubuntu, so now I'm back to square 1. At the same time, I don't know if this guy is incorrect. He hasn't offered any follow-up information on the correct way to do this.

I need to get a GUI programmed that will allow a user to input information, while keeping track of time. Once the time specified by the user is exceeded, a new Toplevel() window should appear to alert the user that the time they specified is met. Multiple Toplevel() alert windows should be allowed to pop up at any given time, and the main window should still accept input no matter how many Toplevel() windows open. Anyone who knows how to accomplish this without threading, your insight is definitely needed. I feel like I'm getting false information, and at the same time I have no idea what information is false.

Here is the source if you want to take a look.

It needs some cleaning up as I have been editing it a lot, so I apologize. I will try to clean it up some more, but I'm at work.

Answer

If all you need to do is pop up an alert when a certain time threshold has been met, you can easily do that in all that unused time when the GUI is otherwise sitting idle. You can either simply require that the window show up in the future, or periodically perform a check to determine if the window should appear.

Here is an example of the first technique of scheduling the window to appear in the future:

import Tkinter as tk

class TimedToplevel(object):
    def __init__(self, parent, milliseconds):
        self.milliseconds = milliseconds
        self.parent = parent
        self.parent.after(milliseconds, self.create_window)

    def create_window(self):
        self.top = tk.Toplevel(self.parent)
        label = tk.Label(self.top, text="Your time has expired")
        button = tk.Button(self.top, text="Ok", command=self.dismiss)
        button.pack(side="bottom")
        label.pack(side="top", fill="both", expand=True)

    def dismiss(self):
        self.top.destroy()


class Example(tk.Frame):
    def __init__(self, master):
        self.alerts = []
        tk.Frame.__init__(self, master)
        t = tk.Text(self, width=40, height=4, wrap="word")
        t.pack(side="top", fill="both", expand=True)
        t.insert("end", "type here, to see that you can type " +
                 "while the timers are running")
        for delay in (5,10,15):
            label = "Alert me in %s seconds" % delay
            b = tk.Button(self, text=label, 
                          command=lambda ms=delay*1000: self.alert(ms))
            b.pack(side="top")

    def alert(self, delay):
        alert=TimedToplevel(self, delay)
        self.alerts.append(alert)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Comments