bagpuss
bagpuss

Reputation: 89

Refresh a tkinter frame on button press

I am utilising code from Switch between two frames in tkinter to make my GUI. I have a frame with refresh and restart buttons.

My original idea was for the restart button to go to the start page as in the code below but if this frame is called again it has the entries from the previous attempt still showing.

I've tried.destroy() for the refresh button but then I get an traceback message when I call the PLG frame again.

For the restart button, how would I close the PLG frame, go to the Start page and then be able to select PLG again?

For the refresh button, how would I remove the entries in the entry widget and text arrear so that another entry can be made and new answer returned?

class PLG(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="Enter the engine size (cc) below", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)
        vcmd = (self.register(self.onValidate), '%S')
        self.weight_entry = tk.Entry(self, validate='key', vcmd = vcmd)
        self.weight_entry.pack(pady = 10)
        tk.Button(self, text='Click here to display price', command=self.show_option).pack()
        self.text = tk.Text(self)
        self.text.pack(pady = 10)
        self.text.config(state='disabled')
        restart_button = tk.Button(self, text="Restart",
              command=self.restart)
        restart_button.pack()
        refresh_button = tk.Button(self, text="Refresh", command=self.refresh).pack() 
        refresh_button.pack()  

    def onValidate(self,S):
    if S in ['0','1','2', '3', '4', '5', '6', '7', '8', '9']: 
        return True
    else:
        self.bell() # adds a sound effect to error
        self.text.delete(1.0, tk.END) # deletes the error message if valid entry provided
        self.text.insert(tk.END, "Invalid entry.  Please try again.") # displays an error message if a number not provided in entry widget
        return False

    def restart(self):
        self.refresh()
        show_frame("StartPage")

    def refresh(self):
        self.text.config(state='normal')
        self.weight_entry.delete(0,tk.END)
        self.text.delete("1.0", "end")

Advice on both elements would be appreciated.

Upvotes: 0

Views: 31574

Answers (3)

Shah Vipul
Shah Vipul

Reputation: 747

Simple way: Just call that window with button or bind in which frame lies.

works good for windows refresh.

Upvotes: 0

Luther
Luther

Reputation: 574

The OP's question was about clearing input fields so prior input isn't still in the page when you expected to see empty fields for fresh input. I'm posting the finished code while omitting features of the OP's original code which were irrelevant to his question so the solution could easily be seen in its full context. I had been looking to solve this problem with this same frame-switching code from Bryan Oakley's famed tutorials on this topic. I also included an alternate version using grid_remove instead of tkraise since this is how I had solved the problem of ever-active but unseen frames trying to participate in the focus traversal as the user tries to tab through the page. It also kept the frames from all trying to be the same size.

import tkinter as tk

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        # alternate ways to create the frames & append to frames dict: comment out one or the other

        for F in (StartPage, PLG):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        # self.frames["StartPage"] = StartPage(parent=container, controller=self) 
        # self.frames["PLG"] = PLG(parent=container, controller=self)
        # self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
        # self.frames["PLG"].grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

    # alternate version of show_frame: comment out one or the other

    def show_frame(self, page_name):
        for frame in self.frames.values():
            frame.grid_remove()
        frame = self.frames[page_name]
        frame.grid()

    # def show_frame(self, page_name):
        # frame = self.frames[page_name]
        # frame.tkraise()

class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        label = tk.Label(self, text="start page")
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self, text="Go to Page One", command=lambda: controller.show_frame("PLG"))
        button1.pack()        

        button2 = tk.Button(self, text="focus traversal demo only")
        button2.pack()
        button2.focus_set()

        button3 = tk.Button(self, text="another dummy button")
        button3.pack()

        lbl = tk.Label(self, text="tkraise messes up focus traversal\nwhich you can see by testing the two versions of show_frame.()\nUsing grid_remove instead of tkraise solves that,\nwhile preventing frames from being unable to resize to fit their own contents.")
        lbl.pack()

class PLG(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        label = tk.Label(self, text="Enter something below; the two buttons clear what you type.")
        label.pack(side="top", fill="x", pady=10)
        self.wentry = tk.Entry(self)
        self.wentry.pack(pady = 10)
        self.text = tk.Text(self)
        self.text.pack(pady = 10)
        restart_button = tk.Button(self, text="Restart", command=self.restart)
        restart_button.pack()
        refresh_button = tk.Button(self, text="Refresh", command=self.refresh) 
        refresh_button.pack()  

    def restart(self):
        self.refresh()
        self.controller.show_frame("StartPage")

    def refresh(self):
        self.wentry.delete(0, "end")
        self.text.delete("1.0", "end")
        # set focus to any widget except a Text widget so focus doesn't get stuck in a Text widget when page hides
        self.wentry.focus_set()

if __name__ == "__main__":
    app = SampleApp()
    app.mainloop()

Upvotes: 2

Bryan Oakley
Bryan Oakley

Reputation: 385900

The first step is to have your button call a proper function rather than using lambda. Unless you understand why and when to use lambda, it usually just makes the code harder to write and understand.

Once you have it call a function, you can use the function to clear the entries.

Example:

class PLG(tk.Frame):
    def __init__(self, parent, controller):
        ...
        tk.Button(self, text="Restart", command=self.restart)
        tk.Button(self, text="Refresh", command=self.refresh)
        ...

    def restart(self):
        self.refresh()
        self.controller.show_frame("StartPage")

    def refresh(self):
        self.weight_entry.delete(0, "end")
        self.text.delete("1.0", "end")

Upvotes: 1

Related Questions