Home > OS >  How do I access a list and items from another class?
How do I access a list and items from another class?

Time:09-22

I am trying to create a list and then add/read items from the list using python and tkinter.

I need to create a blank list, and add things too it, and then also be able to clear the entry from the widget created within a function in another class. I have been trying everything and cannot figure out how to access the widgets/list like I need to.

The error i keep getting is AttributeError: 'function' object has no attribute 'data'

Any help is appreciated.

from tkinter import font as tkfont

class SampleApp(tk.Tk):

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

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")

        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 = {}

        self.frames["StartPage"] = StartPage(parent=container, controller=self)

        self.frames["StartPage"].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
        self.grid(columnspan=5, rowspan=5)

        self.start = tk.Button(self, text='Start', command=lambda: self.start_frame())
        self.start.pack(side='top')


    def start_frame(self):
        data = []
        frame = tk.Frame(self)
        frame.pack(side='top', fill='both')

        entry = tk.Text(frame)
        entry.bind('<Return>', lambda event: stored_functions.print_date(self, entry=entry.get('1.0', 'end-1c')))
        entry.pack(side='top')



class stored_functions():
    def print_date(self, entry):
        StartPage.start_frame.entry.delete('1.0', tk.END)
        all_entries = StartPage.start_frame.data
        print('The last entry was {} \n\n Here is all the entries: {}'.format(entry, all_entries))


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

CodePudding user response:

In order to get variables from a class, you need to initialize it first. In the line StartPage.start_frame.entry.delete('1.0', tk.END), you try to access a variable from the StartPage class itself, not an instance of the class. You also try to access the start_frame method's variable (start_frame.entry), without calling the method.

When you bind the entry in the line entry.bind('<Return>', lambda event: stored_functions.print_date(self, entry=entry.get('1.0', 'end-1c'))), you make a similar mistake in using stored_functions.print_date(... instead of stored_functions().print_date(... (notice the parentheses).

When you need to access a variable outside of a method, it is good practice to create the variable with self. at the beginning. This makes it so that the variable can be accessed throughout the class, and by outside code using an instance of the class. Here is the modified code, with added and edited lines marked accordingly:

import tkinter as tk
from tkinter import font as tkfont

class SampleApp(tk.Tk):

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

        self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")

        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 = {}

        self.frames["StartPage"] = StartPage(parent=container, controller=self)

        self.frames["StartPage"].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
        self.grid(columnspan=5, rowspan=5)

        self.start = tk.Button(self, text='Start', command=lambda: self.start_frame())
        self.start.pack(side='top')


    def start_frame(self):
        self.data = []
        frame = tk.Frame(self)
        frame.pack(side='top', fill='both')

        self.entry = tk.Text(frame) ### EDITED LINE
        self.entry.bind('<Return>', lambda event: stored_functions().print_date(self, entry=self.entry.get('1.0', 'end-1c'))) ### EDITED LINE
        self.entry.pack(side='top') ### EDITED LINE



class stored_functions():
    def print_date(self, start_page, entry): ### EDITED LINE
        start_page.entry.delete('1.0', tk.END) ### EDITED LINE
        all_entries = start_page.data ### EDITED LINE
        print('The last entry was {} \n\n Here is all the entries: {}'.format(entry, all_entries))


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

I changed the line where the entry is bound so that when "<Enter>" is pressed, it passes the instance of StartPage and the entry's text as arguments to stored_functions.print_date().

Notice that when the entry is created in StartPage.start_frame(), it is called self.entry, not simply entry. Also note that stored_functions.print_date() uses start_page.entry instead of StartPage.start_frame.entry, and start_page.data instead of StartPage.start_frame.data. This is because start_page is referencing an instance of the StartPage class, not the class itself.

Let me know if you have any questions or problems!

  • Related