Python Forum
Tkinter object scope - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: GUI (https://python-forum.io/forum-10.html)
+--- Thread: Tkinter object scope (/thread-39432.html)



Tkinter object scope - riversr54 - Feb-16-2023

New to Python but really new to Tkinter...

Working on a simple app that has multiple child pages and one 'home' page that dispatches to the 'child' pages. Each one of the child pages is defined as a class inherited from Toplevel. In that class there is a constructor (__init__) that does some setup and defines the controls on the page (Button and Entry). One of the Buttons calls a second function in the class named SaveData. The idea is that this function will collect data from the Entry objects and save it in a custom class(Book). So far, so good... my problem arises when I try to access the data in the Entry object inside the SaveData function. The Entry object was declared in the __init__ function. I get an error indicating that the Entry object is None. This makes perfect sense to me given that the Entry object was defined in a different function. The question is how do I make it Global so that it is available in all the functions of the class or is there some other way? I've done some experimenting with making the Entry object global in __init__ but with no success.

Thanks for any advice/suggestions. Code Below

rivers
from tkinter import *
from tkinter import ttk
from Book import *

class EnterBookWindow(Toplevel):

    [color=#2980B9]def __init__(self):[/color]
        Toplevel.__init__(self)
        self.title("Enter a Book in xx Data Structure")
        self.geometry('400x400')
        frameLeft = Frame(self, width=40, padx=20, pady=20)
        frameLeft.pack(side=LEFT)
        frameRight = Frame(self, width=40, padx=20, pady=20)
        frameRight.pack(side=RIGHT)

        lblTitle = ttk.Label(frameRight, text="Title:").pack(padx=10, pady=0)
        [color=#C0392B]global entBookTitle[/color]
        entBookTitle = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)

        lblISBN = ttk.Label(frameRight, text="ISBN:").pack(padx=10, pady=0)
        entBookISBN = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
        
        lblAuthor = ttk.Label(frameRight, text="Author:").pack(padx=10, pady=0)
        entBookAuthor = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
        
        lblYear = ttk.Label(frameRight, text="Year:").pack(padx=10, pady=0)
        entBookYear = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)

        btnSave = ttk.Button(frameRight, text="Save Data", command=self.SaveBook).pack(padx=20, pady=10)

    def SaveBook(self):
        book = Book(entBookTitle.get(), entBookISBN.get(), entBookAuthor.get(), entBookYear.get())
#!!!Error occurs here when trying to get data from entBookTitle



RE: Tkinter object scope - riversr54 - Feb-16-2023

I couldn't get the code snippet to post correctly so the indentation looks wrong in my code. That part is OK, so please ignore that.

rivers


RE: Tkinter object scope - Gribouillis - Feb-16-2023

The solution is to make the widgets that you want to access later members of the EnterBookWindow instance, for example in __init__()
self.entBookTitle = ttk.Entry(...)
Later
    def SaveBook(self):
        book = Book(
            self.entBookTitle.get(), self.entBookISBN.get(), self.entBookAuthor.get(), self.entBookYear.get())
Don't use the global keyword. It generally means that you are not organizing the data correctly.


RE: Tkinter object scope - riversr54 - Feb-16-2023

Thank you so much for the very quick reply. As soon as I saw it, I knew, "I should have thought of that", but I didn't Shy Now to the bad news...
I made those changes but still have the same problem.

The error is:
  File "D:\Projects\Pycharm\DSAFinalProject\EnterBook.py", line 27, in SaveBook
    book = Book(self.entBookTitle.get(), self.entBookISBN.get(), self.entBookAuthor.get(), self.entBookYear.get())
                ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'get'
What am I missing?


My updated code:

from tkinter import *
from tkinter import ttk
from Book import *

class EnterBookWindow(Toplevel):

    def __init__(self):
        Toplevel.__init__(self)
        self.title("Enter a Book in xx Data Structure")
        self.geometry('400x400')
        frameLeft = Frame(self, width=40, padx=20, pady=20)
        frameLeft.pack(side=LEFT)
        frameRight = Frame(self, width=40, padx=20, pady=20)
        frameRight.pack(side=RIGHT)

        lblTitle = ttk.Label(frameRight, text="Title:").pack(padx=10, pady=0)
        self.entBookTitle = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
        lblISBN = ttk.Label(frameRight, text="ISBN:").pack(padx=10, pady=0)
        self.entBookISBN = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
        lblAuthor = ttk.Label(frameRight, text="Author:").pack(padx=10, pady=0)
        self.entBookAuthor = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
        lblYear = ttk.Label(frameRight, text="Year:").pack(padx=10, pady=0)
        self.entBookYear = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
        btnSave = ttk.Button(frameRight, text="Save Data", command=self.SaveBook).pack(padx=20, pady=10)

    def SaveBook(self):
        book = Book(self.entBookTitle.get(), self.entBookISBN.get(), self.entBookAuthor.get(), self.entBookYear.get())



RE: Tkinter object scope - Gribouillis - Feb-16-2023

(Feb-16-2023, 08:14 PM)riversr54 Wrote: Now to the bad news...
It is because the .pack() method returns None. Do
self.entBookTitle = ttk.Entry(frameRight, font=('Helvetica', 12), width=20)
self.entBookTitle.pack(padx=10, pady=10)



RE: Tkinter object scope - riversr54 - Feb-16-2023

Thanks, that did the trick. I would likely never have figured that one out, but it does make sense, wasn't really a scope problem at all.

Have a good day!


RE: Tkinter object scope - deanhystad - Feb-17-2023

This is easy to figure out if you let Python help.

First you look at the error message:
Error:
book = Book(self.entBookTitle.get(), self.entBookISBN.get(), self.entBookAuthor.get(), self.entBookYear.get()) ^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'get'
The next thing I would do is put a breakpoint on line 32 and run the program in the debugger, or add a print command before line 32 to display the value of self.entBookTitle.
    def SaveBook(self):
        print(self.entBookTitle)
        book = Book(self.entBookTitle.get(), self.entBookISBN.get(), self.entBookAuthor.get(), self.entBookYear.get())
This would verify that self.entBookTitle is indeed None.

Next search for every line that includes entBookTitle and focus on lines that assign a value to entBookTitle. In your posted code this only happens on line 17.
        self.entBookTitle = ttk.Entry(frameRight, font=('Helvetica', 12), width=20).pack(padx=10, pady=10)
Using the debugger I would set a breakpoint on line 18, or add a print statement right after line 17 to see the value of self.entBookTitle right after the assignment. The value would indeed be Null. Now you have isolated the problem to 1 line which contains an assignment and two function calls. These are the two function calls:
ttk.Entry(frameRight, font=('Helvetica', 12), width=20)
.pack(padx=10, pady=10)
One of these returns None. I would rewrite like this:
        self.entBookTitle
temp = ttk.Entry(frameRight, font=('Helvetica', 12), width=20)
print("entry", temp)
temp.pack(padx=10, pady=10)
print("pack", temp)
self.entBookTitle = temp
Run the code and you would see that "ttk.Entry(frameRight, font=('Helvetica', 12), width=20)" returns a ttkEntry object and pack() returns None. Armed with that information you would quickly realize that you need to break up creating and packing the entry widget.
self.entBookTitle = ttk.Entry(frameRight, font=('Helvetica', 12), width=20)
self.entBooktitle.pack(padx=10, pady=10)
I'm confident that you would have solved this problem quite quickly if you did a little poking around instead of immediately throwing up your hands in frustration. Give yourself some credit.