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 2 Answers
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 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)