-2

I'm looking through the code at passing-functions-parameters-tkinter-using-lambda, and needed a tad more functionality inside his class PageOne(tk.Frame). Instead of using lambda commands below (as he did):

button1 = tk.Button(self, text="Back to Home",
                        command=lambda: controller.show_frame(StartPage))`

I'd like to be able to create a function that had an if/then hierarchy inside of it... specifically to check if all other inputs on PageOne had been fulfilled first (which I then know how to do) before allowing a frame change.

If this can be done individually using lambda, even better. Can anyone help me out?


Update: Using Bryan's advice and reformatting for the original code he linked, I now have:

class App(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, "APP") #window heading
        self.title_font = tkfont.Font(family='Helvetica', size=12) #options: weight="bold",slant="italic"

        container = tk.Frame(self) #container = stack of frames; one on top is visible
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (StartPage, PageOne):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame #puts all pages in stacked order
            frame.grid(row=0, column=0, sticky="nsew")
        self.show_frame("StartPage")

    def show_frame(self, page_name): #show a frame for the given 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="This is the start page", font=controller.title_font)
        label.pack(side="top", fill="x", pady=10)

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

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

####FIX PART 1####
        self.next1 = tk.Button(self,text="Next",padx=18,highlightbackground="black", 
                           command=lambda: self.maybe_switch("PageTwo"))  
        self.next1.grid(row=10,column=1,sticky='E')

####FIX PART 2####
    def maybe_switch(self, page_name):
        if ###SOMETHING###:
            self.controller.show_frame(page_name)

if __name__ == "__main__":
    app = App()
    app.mainloop()
evambivalence
  • 113
  • 1
  • 12
  • better will be to implement all controller logic in FSM (Finite state machine) and callbacks attached to `command` event will be naturally events for FSM/Controller. This will guarantes you robast code without bugs. So, lambdas will only "send" events to FSM – RandomB Jan 15 '18 at 17:53
  • @[Nae] I've updated above; Bryan's last edit in his response fixed – evambivalence Jan 17 '18 at 22:46

1 Answers1

0

You shouldn't put any logic in a lambda. Just create a normal function that has any logic you want, and call it from the button. It's really no more complicated that that.

class SomePage(...):
    def __init__(...):
        ...
        button1 = tk.Button(self, text="Back to Home", 
                        command=lambda: self.maybe_switch_page(StartPage))
        ...

    def maybe_switch_page(self, destination_page):
        if ...:
            self.controller.show_frame(destination_page)
        else:
            ...

If you want a more general purpose solution, move the logic to show_frame, and have it call a method on the current page to verify that it is OK to switch.

For example:

    class Controller(...):
        ...
        def show_frame(self, destination):
            if self.current_page is None or self.current_page.ok_to_switch():
                # switch the page
            else:
                # don't switch the page

Then, it's just a matter of implementing ok_to_switch in every page class.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Following your first suggestion, I'm getting: 'PageOne' object has no attribute 'controller' `self.next1 = tk.Button(self,text="Next", command=self.maybe_switch_frame(PageTwo))` `def maybe_switch_frame(self, destination_frame): self.controller.show_frame(destination_frame)` – evambivalence Jan 15 '18 at 18:48
  • @evambivalence: then you need to save a reference to the controller. Unfortunately, the tutorial you chose isn't a particularly good example and neglects to do that. That tutorial copied from an earlier version of an answer on this site which you can find here: https://stackoverflow.com/a/7557028/7432 – Bryan Oakley Jan 15 '18 at 18:54
  • @[Bryan Oakley] I've listed my entire code above; I'm feeling very close but still not quite there. I'm sticking with your function within a page idea in my example – evambivalence Jan 15 '18 at 21:12
  • @evambivalence: stackoverflow isn't for extended conversations. If you have new questions, click the "ask question" button. – Bryan Oakley Jan 15 '18 at 21:23
  • @[Bryan Oakley] thank you for all you're help, seriously. I'm being told by the site I'm not allowed to ask any more questions for the day. I'll try to figure it out. – evambivalence Jan 15 '18 at 21:28