1

Is it feasible to execute multiple statements in a lambda? The app_data dictionary in my controller has a property "listbox" that stores a selected listbox value. A button on SelectPage has a lambda command that changes frames to NextPage.

Can the frame switch and app_data set statements both occur in a double-lambda? If so, what might this look like? Or do these operations have to occur separately (i.e. in a button click event)?

Update #1:

Issue isolated to lambda expression since just putting a constant (i.e. 1) in like .set(1) does not save either.

Update #2: Using a lambda with doStuff(controller) solves the problem somewhat, but the listbox reference throws an error:

value = self.listbox.get(self.listbox.curselection()) AttributeError: 'SelectPage' object has no attribute 'listbox'

button:

   button1 = ttk.Button(self, text='Next Page',
                         command=lambda:self.doStuff(controller))

doStuff():

    def doStuff(self,controller):
        controller.show_frame(NextPage)
        controller.app_data["listbox"].set(1)
        value = self.listbox.get(self.listbox.curselection())
        print(value)
        print("do Stuff successful")

Full Code:

from tkinter import *
from tkinter import ttk



class MyApp(Tk):
    # Controller class
    def __init__(self):
        Tk.__init__(self)

        # App data in controller
        self.app_data = {"listbox":    StringVar(),
                         "entry":       StringVar(),
                         }

        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        self.frames = {}
        for F in (SelectPage, NextPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky = NSEW)
        self.show_frame(SelectPage)

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

    def get_page(self,classname):
        for page in self.frames.values():
            if str(page.__class__.__name__) == classname:
                return page
        return None

class SelectPage(ttk.Frame):

    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        ttk.Label(self, text='Select Page').grid(padx=(20,20), pady=(20,20))

        listbox = Listbox(self,exportselection=0)
        listbox.grid()
        for item in [0,1,2,3,4,5]:
            listbox.insert(END, item)
            print (item)
        entry1 = ttk.Entry(self, textvariable=self.controller.app_data["entry"], width=8)
        entry1.grid()
        button1 = ttk.Button(self, text='Next Page',
                          command=lambda:self.doStuff(controller)) # something like this lambda concept
        button1.grid()

    def doStuff(self,controller):
        controller.show_frame(NextPage)
        controller.app_data["listbox"].set(1)
        value = self.listbox.get(self.listbox.curselection())
        print(value)
        print("do Stuff successful")

class NextPage(ttk.Frame):
    def __init__(self, parent, controller):
        self.controller = controller
        ttk.Frame.__init__(self, parent)
        ttk.Label(self, text='Next Page').grid(padx=(20,20), pady=(20,20))
        button1 = ttk.Button(self, text='Select Page',
                             command=lambda: controller.show_frame(SelectPage))
        button1.grid()
        button2 = ttk.Button(self, text='press to print', command=self.print_it)
        button2.grid()

    def print_it(self):

        value = self.controller.app_data["listbox"].get()
        print ('The value stored in StartPage some_entry = ' + str(value))
        value = self.controller.app_data["entry"].get()
        print ('The value stored in StartPage some_entry = ' + str(value))
app = MyApp()
app.title('Multi-Page Test App')
app.mainloop()
9
  • 4
    Possible duplicate of Is it possible to have multiple statements in a python lambda expression? Commented Feb 8, 2017 at 18:33
  • 3
    Don't use a lambda, use a proper function with def. Note: You can define functions within functions and make use of their local namespace, if necessary. Commented Feb 8, 2017 at 18:35
  • What happened when you tried? Does your code work as expected? Commented Feb 8, 2017 at 18:36
  • @Goyo Code runs, but listbox item does not get saved Commented Feb 8, 2017 at 18:37
  • 2
    Even easier: def doStuff() with the statements inside, and then command=doStuff. Note: doStuff, NOT doStuff(). Commented Feb 8, 2017 at 18:53

2 Answers 2

2

Your original lambda didn't work because show_frame(...) returns None, which short-circuits the and so the remaining expression doesn't execute. Just change it to an or. Since the event caller doesn't care what you return, you could also create a two item tuple for the two subexpressions. There are other problems, such as self.listbox doesn't exist, which I fixed, but others remain.

As a pedantic aside, lambda only accepts an expression, which is a subset of a statement.

self.listbox = listbox
button1 = ttk.Button(self, text='Next Page',
                  command=lambda: controller.show_frame(NextPage) or self.controller
                     .app_data["listbox"]
                     .set(self.listbox.get(self.listbox.curselection()))) # something like this lam
Sign up to request clarification or add additional context in comments.

Comments

1

Is it feasible to execute multiple statements in a lambda?

No. In general, you should avoid using lambda at all, and when you must use it you need to keep it as brief as possible. If you need to call more than a single function, create a new function that calls the other functions.

Also, if you're passing in an argument that is just an attribute of the object, you don't need lambda at all. I think the following code is much easier to read, much easier to understand, much easier to write, and thus much easier to maintain:

button1 = ttk.Button(self, text='Next Page',command=self.doStuff)
...
def doStuff(self):
    self.controller.show_frame(NextPage)
    self.controller.app_data["listbox"].set(1)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.