Spencer H
Spencer H

Reputation: 663

AttributeError: object has no attribute 'listbox'?

In this simple script an item selected in listbox on Page1 is saved and printed from a Page2. The value in listbox is saved in app_data. The code runs, but no value is recorded in app_data (the value printed is blank). The following exception occurs:

Exception in Tkinter callback Traceback (most recent call last):
  File "/usr/lib/python3.4/tkinter/__init__.py", line 1536, in __call__
    return self.func(*args)   File "filepath/file.py", line 35, in <lambda>
    button1 = ttk.Button(self,text="Next Page",command=lambda: controller.show_frame(Page2)
                         or self.controller.app_data["listbox"]
                         .set(self.listbox.get(self.listbox.curselection())))
AttributeError: 'Page1' object has no attribute 'listbox'

From what I can tell the controller is unable to recognize the listbox. I looked into a possible resolution using super() as shown in Understanding Python super() with init() methods but have not had success.

Question: What will enable listbox values to correctly save to app_data?

button1 = ttk.Button(self,text="Next Page"
                     ,command=lambda: controller.show_frame(Page2)
                     or self.controller.app_data["listbox"]
                     .set(self.listbox.get(self.listbox.curselection()))

Full Code:

from tkinter import *
from tkinter import ttk

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

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

        container = ttk.Frame(self)
        container.pack(side="top", fill="both", expand = True)
        self.frames = {}

        for F in (Page1, Page2):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky = NSEW)
        self.show_frame(Page1)

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

class Page1(ttk.Frame):
    def __init__(self, parent, controller):
        ttk.Frame.__init__(self, parent)
        self.controller = controller

        listbox = Listbox(self,exportselection=0)
        listbox.grid()
        for item in [0,1,2,3,4,5]:
            listbox.insert(END, item)

        button1 = ttk.Button(self,text="Next Page"
                             ,command=lambda: controller.show_frame(Page2)
                             or self.controller.app_data["listbox"]
                             .set(self.listbox.get(self.listbox.curselection())))
        button1.grid()

class Page2(ttk.Frame):
    def __init__(self, parent, controller):
        ttk.Frame.__init__(self, parent)
        self.controller = controller
        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(Page1))
        button1.grid()
        button2 = ttk.Button(self, text='print value', command=self.print_value)
        button2.grid()
    def print_value(self):
        value = self.controller.app_data["listbox"].get()
        print ('The value stored in StartPage some_entry = ' + str(value))

app = MyApp()
app.mainloop()

Upvotes: 0

Views: 2869

Answers (1)

j_4321
j_4321

Reputation: 16169

Your problem is simple: in button1 definition you use some methods of self.listbox, but when you defined your listbox, you haven't defined it as an attribute of Page1: listbox = Listbox(self,exportselection=0) (no self. in front of it). That's why you get the error AttributeError: 'Page1' object has no attribute 'listbox'.

There are two ways to avoid this:

  • If you need to use the listbox somewhere outside the __init__ method of Page1, make it an attribute: self.listbox = Listbox(self,exportselection=0) and replace all the listbox by self.listbox.

  • If you don't need it anywhere else, just change the self.listbox in listbox in the button1 definition:

    button1 = ttk.Button(self,text="Next Page"
                         ,command=lambda: controller.show_frame(Page2)
                         or self.controller.app_data["listbox"]
                         .set(listbox.get(listbox.curselection())))
    

Upvotes: 1

Related Questions